summaryrefslogtreecommitdiffstats
path: root/js/src/tests/test262/built-ins/Temporal
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
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')
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/argument-wrong-type.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/builtin.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/constructor.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/from/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/from/builtin.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/from/calendar-case-insensitive.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/from/calendar-number.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/from/calendar-object.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/from/calendar-string-builtin.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/from/calendar-string-leap-second.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/from/calendar-string-not-builtin.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/from/calendar-string.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/from/calendar-temporal-object.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/from/calendar-wrong-type.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/from/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/from/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/from/not-a-constructor.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/from/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/from/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/from/subclassing-ignored.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/missing-arguments.js14
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/constructor.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/add-days.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/add-months-weeks.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/add-months.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/add-weeks-days.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/add-weeks.js66
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/add-years-months-days.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/add-years-months.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/add-years-weeks.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/add-years.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-builtin-calendar-no-array-iteration.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-calendar-datefromfields-called-with-null-prototype-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-constructor-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-duplicate-calendar-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-duration-max.js58
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-duration-out-of-range.js75
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-duration-years-and-months-number-max-value.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-leap-second.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-plaindatetime.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-propertybag-calendar-case-insensitive.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-propertybag-calendar-leap-second.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-propertybag-calendar-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-propertybag-calendar-string.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-propertybag-calendar-wrong-type.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-propertybag-calendar-year-zero.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-proto-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-string-calendar-annotation.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-string-critical-unknown-annotation.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-string-date-with-utc-offset.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-string-invalid.js64
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-string-multiple-calendar.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-string-multiple-time-zone.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-string-time-separators.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-string-time-zone-annotation.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-string-unknown-annotation.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-string-with-utc-designator.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-wrong-type.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-zoneddatetime-convert.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-zoneddatetime-slots.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/balance-smaller-units.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/basic.js53
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/calendar-datefromfields-called-with-options-undefined.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/calendar-fields-iterable.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/calendar-temporal-object.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/date-infinity-throws-rangeerror.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/duration-argument-string-negative-fractional-units.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/options-object.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/options-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/order-of-operations.js122
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/overflow-invalid-string.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/overflow-undefined.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/overflow-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/throw-range-error-from-ToTemporalDate.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/throw-range-error-from-ToTemporalDuration.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/throw-type-error-from-GetOptionsObject.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/year-zero.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/fields-not-object.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/infinity-throws-rangeerror.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/missing-properties.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/one-of-era-erayear-undefined.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/options-object.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/options-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/order-of-operations.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/overflow-invalid-string.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/overflow-undefined.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/overflow-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/throw-type-error-from-GetOptionsObject.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/throws-range-error.js126
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/throws-type-error.js58
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/with-year-month-day-need-constrain.js90
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/with-year-month-day.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/with-year-monthCode-day-need-constrain.js80
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/with-year-monthCode-day.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-builtin-calendar-no-array-iteration.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-calendar-datefromfields-called-with-null-prototype-fields.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-constructor-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-duplicate-calendar-fields.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-infinity-throws-rangeerror.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-number.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-plaindatetime.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-propertybag-calendar-case-insensitive.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-propertybag-calendar-leap-second.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-propertybag-calendar-number.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-propertybag-calendar-string.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-propertybag-calendar-wrong-type.js52
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-propertybag-calendar-year-zero.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-proto-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-string-calendar-annotation.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-string-critical-unknown-annotation.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-string-date-with-utc-offset.js52
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-string-invalid.js70
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-string-multiple-calendar.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-string-multiple-time-zone.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-string-time-separators.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-string-time-zone-annotation.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-string-unknown-annotation.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-string-with-utc-designator.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-wrong-type.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-zoneddatetime-slots.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/basic.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/calendar-datefromfields-called-with-options-undefined.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/calendar-fields-iterable.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/calendar-temporal-object.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/largest-unit-day.js62
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/largest-unit-month.js97
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/largest-unit-week.js74
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/largest-unit-year.js207
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/largestunit-plurals-accepted.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/leap-second.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/no-options.js59
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/options-object.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/options-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/order-of-operations.js129
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/throws-range-error-ToLargestTemporalUnit.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/throws-range-error-ToTemporalDate.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/throws-type-error-GetOptionsObject.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/year-zero.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-builtin-calendar-no-array-iteration.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-calendar-datefromfields-called-with-null-prototype-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-constructor-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-duplicate-calendar-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-leap-second.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-propertybag-calendar-case-insensitive.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-propertybag-calendar-leap-second.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-propertybag-calendar-number.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-propertybag-calendar-string.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-propertybag-calendar-wrong-type.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-propertybag-calendar-year-zero.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-proto-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-string-calendar-annotation.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-string-critical-unknown-annotation.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-string-date-with-utc-offset.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-string-invalid.js64
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-string-multiple-calendar.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-string-multiple-time-zone.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-string-time-separators.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-string-time-zone-annotation.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-string-unknown-annotation.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-string-with-utc-designator.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-wrong-type.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-zoneddatetime-convert.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-zoneddatetime-slots.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/basic.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/calendar-datefromfields-called-with-options-undefined.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/calendar-fields-iterable.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/calendar-temporal-object.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/date-time.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/date.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/infinity-throws-rangeerror.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/month-day.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/string.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/throw-range-error-ToTemporalDate.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/year-zero.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-builtin-calendar-no-array-iteration.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-calendar-datefromfields-called-with-null-prototype-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-constructor-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-duplicate-calendar-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-leap-second.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-propertybag-calendar-case-insensitive.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-propertybag-calendar-leap-second.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-propertybag-calendar-number.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-propertybag-calendar-string.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-propertybag-calendar-wrong-type.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-propertybag-calendar-year-zero.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-proto-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-string-calendar-annotation.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-string-critical-unknown-annotation.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-string-date-with-utc-offset.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-string-invalid.js64
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-string-multiple-calendar.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-string-multiple-time-zone.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-string-time-separators.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-string-time-zone-annotation.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-string-unknown-annotation.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-string-with-utc-designator.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-wrong-type.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-zoneddatetime-convert.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-zoneddatetime-slots.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/basic.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/calendar-datefromfields-called-with-options-undefined.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/calendar-fields-iterable.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/calendar-temporal-object.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/infinity-throws-rangeerror.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/plain-date-time.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/plain-date.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/string.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/throw-range-error-ToTemporalDate.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/year-zero.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-builtin-calendar-no-array-iteration.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-calendar-datefromfields-called-with-null-prototype-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-constructor-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-duplicate-calendar-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-leap-second.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-propertybag-calendar-case-insensitive.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-propertybag-calendar-leap-second.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-propertybag-calendar-number.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-propertybag-calendar-string.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-propertybag-calendar-wrong-type.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-propertybag-calendar-year-zero.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-proto-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-string-calendar-annotation.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-string-critical-unknown-annotation.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-string-date-with-utc-offset.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-string-invalid.js64
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-string-multiple-calendar.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-string-multiple-time-zone.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-string-time-separators.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-string-time-zone-annotation.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-string-unknown-annotation.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-string-with-utc-designator.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-wrong-type.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-zoneddatetime-convert.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-zoneddatetime-slots.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/basic.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/calendar-datefromfields-called-with-options-undefined.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/calendar-fields-iterable.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/calendar-temporal-object.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/infinity-throws-rangeerror.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/plain-date-time.js58
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/plain-date.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/string.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/throw-range-error-ToTemporalDate.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/year-zero.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-builtin-calendar-no-array-iteration.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-calendar-datefromfields-called-with-null-prototype-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-constructor-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-duplicate-calendar-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-leap-second.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-propertybag-calendar-case-insensitive.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-propertybag-calendar-leap-second.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-propertybag-calendar-number.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-propertybag-calendar-string.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-propertybag-calendar-wrong-type.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-propertybag-calendar-year-zero.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-proto-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-string-calendar-annotation.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-string-critical-unknown-annotation.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-string-date-with-utc-offset.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-string-invalid.js64
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-string-multiple-calendar.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-string-multiple-time-zone.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-string-time-separators.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-string-time-zone-annotation.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-string-unknown-annotation.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-string-with-utc-designator.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-wrong-type.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-zoneddatetime-convert.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-zoneddatetime-slots.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/basic.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/calendar-datefromfields-called-with-options-undefined.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/calendar-fields-iterable.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/calendar-temporal-object.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/infinity-throws-rangeerror.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/plain-date-time.js116
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/plain-date.js58
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/string.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/throw-range-error-ToTemporalDate.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/year-zero.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-builtin-calendar-no-array-iteration.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-calendar-datefromfields-called-with-null-prototype-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-constructor-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-duplicate-calendar-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-leap-second.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-propertybag-calendar-case-insensitive.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-propertybag-calendar-leap-second.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-propertybag-calendar-number.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-propertybag-calendar-string.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-propertybag-calendar-wrong-type.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-propertybag-calendar-year-zero.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-proto-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-string-calendar-annotation.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-string-critical-unknown-annotation.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-string-date-with-utc-offset.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-string-invalid.js64
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-string-multiple-calendar.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-string-multiple-time-zone.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-string-time-separators.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-string-time-zone-annotation.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-string-unknown-annotation.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-string-with-utc-designator.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-wrong-type.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-zoneddatetime-convert.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-zoneddatetime-slots.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/basic.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/calendar-datefromfields-called-with-options-undefined.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/calendar-fields-iterable.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/calendar-temporal-object.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/date-time.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/date.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/infinity-throws-rangeerror.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/string.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/throw-range-error-ToTemporalDate.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/year-zero.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-builtin-calendar-no-array-iteration.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-calendar-datefromfields-called-with-null-prototype-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-constructor-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-duplicate-calendar-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-leap-second.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-propertybag-calendar-case-insensitive.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-propertybag-calendar-leap-second.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-propertybag-calendar-number.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-propertybag-calendar-string.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-propertybag-calendar-wrong-type.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-propertybag-calendar-year-zero.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-proto-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-string-calendar-annotation.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-string-critical-unknown-annotation.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-string-date-with-utc-offset.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-string-invalid.js64
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-string-multiple-calendar.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-string-multiple-time-zone.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-string-time-separators.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-string-time-zone-annotation.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-string-unknown-annotation.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-string-with-utc-designator.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-wrong-type.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-zoneddatetime-convert.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-zoneddatetime-slots.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/basic.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/calendar-datefromfields-called-with-options-undefined.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/calendar-fields-iterable.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/calendar-temporal-object.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/infinity-throws-rangeerror.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/plain-date-time.js94
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/plain-date.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/string.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/throw-range-error-ToTemporalDate.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/year-zero.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/fields/argument-iterable-not-array.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/fields/argument-throws-duplicate-keys.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/fields/argument-throws-invalid-keys.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/fields/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/fields/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/fields/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/fields/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/fields/long-input.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/fields/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/fields/non-string-element-throws.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/fields/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/fields/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/fields/repeated-throw.js58
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/fields/reverse.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/fields/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/id/branding.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/id/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/id/custom-calendar.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/id/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/id/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-builtin-calendar-no-array-iteration.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-calendar-datefromfields-called-with-null-prototype-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-constructor-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-duplicate-calendar-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-leap-second.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-propertybag-calendar-case-insensitive.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-propertybag-calendar-leap-second.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-propertybag-calendar-number.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-propertybag-calendar-string.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-propertybag-calendar-wrong-type.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-propertybag-calendar-year-zero.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-proto-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-string-calendar-annotation.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-string-critical-unknown-annotation.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-string-date-with-utc-offset.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-string-invalid.js64
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-string-multiple-calendar.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-string-multiple-time-zone.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-string-time-separators.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-string-time-zone-annotation.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-string-unknown-annotation.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-string-with-utc-designator.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-string.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-wrong-type.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-zoneddatetime-convert.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-zoneddatetime-slots.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/basic.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/calendar-datefromfields-called-with-options-undefined.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/calendar-fields-iterable.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/calendar-temporal-object.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/infinity-throws-rangeerror.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/year-zero.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/mergeFields/arguments-empty-object.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/mergeFields/arguments-not-object.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/mergeFields/basic.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/mergeFields/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/mergeFields/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/mergeFields/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/mergeFields/iso8601-calendar-month-monthCode.js102
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/mergeFields/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/mergeFields/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/mergeFields/non-string-properties.js58
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/mergeFields/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/mergeFields/order-of-operations.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/mergeFields/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/mergeFields/shell.js353
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-builtin-calendar-no-array-iteration.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-calendar-datefromfields-called-with-null-prototype-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-constructor-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-duplicate-calendar-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-leap-second.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-propertybag-calendar-case-insensitive.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-propertybag-calendar-leap-second.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-propertybag-calendar-number.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-propertybag-calendar-string.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-propertybag-calendar-wrong-type.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-propertybag-calendar-year-zero.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-proto-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-string-calendar-annotation.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-string-critical-unknown-annotation.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-string-date-with-utc-offset.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-string-invalid.js64
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-string-multiple-calendar.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-string-multiple-time-zone.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-string-time-separators.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-string-time-zone-annotation.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-string-unknown-annotation.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-string-with-utc-designator.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-wrong-type.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-zoneddatetime-convert.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-zoneddatetime-slots.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/basic.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/calendar-datefromfields-called-with-options-undefined.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/calendar-fields-iterable.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/calendar-temporal-object.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/date-time.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/date.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/infinity-throws-rangeerror.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/month-day-throw-type-error.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/string.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/throw-range-error-ToTemporalDate.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/year-month.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/year-zero.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-builtin-calendar-no-array-iteration.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-calendar-datefromfields-called-with-null-prototype-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-constructor-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-duplicate-calendar-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-leap-second.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-propertybag-calendar-case-insensitive.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-propertybag-calendar-leap-second.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-propertybag-calendar-number.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-propertybag-calendar-string.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-propertybag-calendar-wrong-type.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-propertybag-calendar-year-zero.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-proto-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-string-calendar-annotation.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-string-critical-unknown-annotation.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-string-date-with-utc-offset.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-string-invalid.js64
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-string-multiple-calendar.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-string-multiple-time-zone.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-string-time-separators.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-string-time-zone-annotation.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-string-unknown-annotation.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-string-with-utc-designator.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-wrong-type.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-zoneddatetime-convert.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-zoneddatetime-slots.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/basic.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/calendar-datefromfields-called-with-options-undefined.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/calendar-fields-iterable.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/calendar-temporal-object.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/date-time.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/date.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/infinity-throws-rangeerror.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/month-day.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/string.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/throw-range-error-ToTemporalDate.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/year-month.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/year-zero.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/basic.js51
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/fields-missing-properties.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/fields-not-object.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/infinity-throws-rangeerror.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/missing-properties.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/monthcode-invalid.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/one-of-era-erayear-undefined.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/options-object.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/options-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/order-of-operations.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/overflow-constrain.js68
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/overflow-invalid-string.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/overflow-reject.js65
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/overflow-undefined.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/overflow-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-builtin-calendar-no-array-iteration.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-calendar-datefromfields-called-with-null-prototype-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-constructor-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-duplicate-calendar-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-leap-second.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-propertybag-calendar-case-insensitive.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-propertybag-calendar-leap-second.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-propertybag-calendar-number.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-propertybag-calendar-string.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-propertybag-calendar-wrong-type.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-propertybag-calendar-year-zero.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-proto-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-string-calendar-annotation.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-string-critical-unknown-annotation.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-string-date-with-utc-offset.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-string-invalid.js64
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-string-multiple-calendar.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-string-multiple-time-zone.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-string-time-separators.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-string-time-zone-annotation.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-string-unknown-annotation.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-string-with-utc-designator.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-string.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-wrong-type.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-zoneddatetime-convert.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-zoneddatetime-slots.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/basic.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/calendar-datefromfields-called-with-options-undefined.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/calendar-fields-iterable.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/calendar-temporal-object.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/infinity-throws-rangeerror.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/year-zero.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/prop-desc.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toJSON/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toJSON/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toJSON/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toJSON/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toJSON/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toJSON/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toJSON/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toJSON/returns-identifier-slot.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toJSON/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toString/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toString/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toString/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toString/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toString/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toString/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toString/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toString/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toStringTag/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toStringTag/prop-desc.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toStringTag/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-builtin-calendar-no-array-iteration.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-calendar-datefromfields-called-with-null-prototype-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-constructor-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-duplicate-calendar-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-leap-second.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-plaindate.js79
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-plaindatetime.js79
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-propertybag-calendar-case-insensitive.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-propertybag-calendar-leap-second.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-propertybag-calendar-number.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-propertybag-calendar-string.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-propertybag-calendar-wrong-type.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-propertybag-calendar-year-zero.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-proto-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-string-calendar-annotation.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-string-critical-unknown-annotation.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-string-date-with-utc-offset.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-string-invalid.js64
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-string-multiple-calendar.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-string-multiple-time-zone.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-string-time-separators.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-string-time-zone-annotation.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-string-unknown-annotation.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-string-with-utc-designator.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-string.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-wrong-type.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-zoneddatetime-convert.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-zoneddatetime-slots.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/basic.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/calendar-datefromfields-called-with-options-undefined.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/calendar-fields-iterable.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/calendar-temporal-object.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/cross-year.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/infinity-throws-rangeerror.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/year-zero.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-builtin-calendar-no-array-iteration.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-calendar-datefromfields-called-with-null-prototype-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-constructor-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-duplicate-calendar-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-leap-second.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-propertybag-calendar-case-insensitive.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-propertybag-calendar-leap-second.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-propertybag-calendar-number.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-propertybag-calendar-string.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-propertybag-calendar-wrong-type.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-propertybag-calendar-year-zero.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-proto-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-string-calendar-annotation.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-string-critical-unknown-annotation.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-string-date-with-utc-offset.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-string-invalid.js64
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-string-multiple-calendar.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-string-multiple-time-zone.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-string-time-separators.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-string-time-zone-annotation.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-string-unknown-annotation.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-string-with-utc-designator.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-wrong-type.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-zoneddatetime-convert.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-zoneddatetime-slots.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/basic.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/calendar-datefromfields-called-with-options-undefined.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/calendar-fields-iterable.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/calendar-temporal-object.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/date-time.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/date.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/infinity-throws-rangeerror.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/string.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/throw-range-error-ToTemporalDate.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/year-month.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/year-zero.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/basic.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/fields-missing-properties.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/fields-not-object.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/infinity-throws-rangeerror.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/missing-properties.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/monthcode-invalid.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/one-of-era-erayear-undefined.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/options-not-object.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/options-object.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/options-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/order-of-operations.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/overflow-constrain.js94
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/overflow-invalid-string.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/overflow-reject.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/overflow-undefined.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/overflow-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/reference-day.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-builtin-calendar-no-array-iteration.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-calendar-datefromfields-called-with-null-prototype-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-constructor-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-duplicate-calendar-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-leap-second.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-propertybag-calendar-case-insensitive.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-propertybag-calendar-leap-second.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-propertybag-calendar-number.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-propertybag-calendar-string.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-propertybag-calendar-wrong-type.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-propertybag-calendar-year-zero.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-proto-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-string-calendar-annotation.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-string-critical-unknown-annotation.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-string-date-with-utc-offset.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-string-invalid.js64
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-string-multiple-calendar.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-string-multiple-time-zone.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-string-time-separators.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-string-time-zone-annotation.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-string-unknown-annotation.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-string-with-utc-designator.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-string.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-wrong-type.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-zoneddatetime-convert.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-zoneddatetime-slots.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/basic.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/calendar-datefromfields-called-with-options-undefined.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/calendar-fields-iterable.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/calendar-temporal-object.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/cross-year.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/infinity-throws-rangeerror.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/year-zero.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Calendar/subclass.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/basic.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/builtin.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/call-builtin.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/compare/argument-cast.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/compare/argument-duration-max.js47
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/compare/argument-duration-out-of-range.js74
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/compare/argument-duration-precision-exact-numerical-values.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/compare/argument-string-fractional-units-rounding-mode.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/compare/argument-string-negative-fractional-units.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/compare/basic.js56
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/compare/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/compare/builtin.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/compare/calendar-dateadd-called-with-options-undefined.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/compare/calendar-dateadd-called-with-plaindate-instance.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/compare/calendar-fields-iterable.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/compare/calendar-possibly-required.js55
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/compare/calendar-temporal-object.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/compare/constructor-in-calendar-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/compare/duplicate-calendar-fields.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/compare/exhaustive.js516
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/compare/instances-identical.js47
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/compare/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/compare/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/compare/not-a-constructor.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/compare/options-object.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/compare/options-undefined.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/compare/options-wrong-type.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/compare/order-of-operations.js310
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/compare/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/compare/proto-in-calendar-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/compare/read-time-fields-before-datefromfields.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-hour.js44
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-month.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-plaindate-add24hourdaystonormalizedtimeduration-out-of-range.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-ambiguous-wall-clock-time.js93
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-builtin-calendar-no-array-iteration.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-calendar-string.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-getpossibleinstantsfor-called-with-iso8601-calendar.js58
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-infinity-throws-rangeerror.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-invalid-offset-string.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-invalid.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-getoffsetnanosecondsfor-not-callable.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-string-datetime.js64
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-string-leap-second.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-string-year-zero.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-string.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-wrong-type.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-string-invalid.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-string-plaindatetime.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-string-zoneddatetime-wrong-offset.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-string-zoneddatetime.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-sub-minute-offset.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-undefined-throw-on-calendar-units.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-year.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-zoneddatetime-negative-epochnanoseconds.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/compare/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/compare/timezone-getpossibleinstantsfor-iterable.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/compare/twenty-five-hour-day.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/compare/year-zero.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/constructor.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/days-undefined.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/fractional-throws-rangeerror.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/from/argument-duration-max.js55
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/from/argument-duration-out-of-range.js59
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/from/argument-duration-precision-exact-numerical-values.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/from/argument-duration.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/from/argument-existing-object.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/from/argument-non-string.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/from/argument-object-invalid.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/from/argument-string-fractional-precision.js59
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/from/argument-string-fractional-units-rounding-mode.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/from/argument-string-fractional-with-zero-subparts.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/from/argument-string-invalid.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/from/argument-string-negative-fractional-units.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/from/argument-string.js51
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/from/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/from/builtin.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/from/infinity-throws-rangeerror.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/from/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/from/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/from/negative-inifinity-throws-rangeerror.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/from/non-integer-throws-rangeerror.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/from/not-a-constructor.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/from/order-of-operations.js61
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/from/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/from/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/from/string-with-skipped-units.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/from/subclassing-ignored.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/hours-undefined.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/infinity-throws-rangeerror.js84
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/max.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/microseconds-undefined.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/milliseconds-undefined.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/minutes-undefined.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/mixed.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/months-undefined.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/nanoseconds-undefined.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/negative-infinity-throws-rangeerror.js84
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/out-of-range.js47
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/basic.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/new-object.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/subclassing-ignored.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/argument-duration-max.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/argument-duration-out-of-range.js75
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/argument-duration-precision-exact-numerical-values.js79
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/argument-invalid-property.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/argument-mixed-sign.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/argument-not-object.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/argument-singular-properties.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/argument-string-fractional-units-rounding-mode.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/argument-string-negative-fractional-units.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/argument-string.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/balance-negative-result.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/balance-negative-time-units.js63
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/basic.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/calendar-dateadd-called-with-options-undefined.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/calendar-dateadd-called-with-plaindate-instance.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/calendar-dateadd.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/calendar-dateuntil-called-with-singular-largestunit.js81
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/calendar-fields-iterable.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/calendar-temporal-object.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/constructor-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/duplicate-calendar-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/infinity-throws-rangeerror.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/intermediate-instant-too-large-with-zoneddatetime.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/nanoseconds-is-number-max-safe-integer.js60
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/negative-infinity-throws-rangeerror.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/non-integer-throws-rangeerror.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/normalized-time-duration-to-days-loop-arbitrarily.js78
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/options-object.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/options-undefined.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/options-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/order-of-operations.js493
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/precision-exact-mathematical-values.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/precision-no-floating-point-loss.js52
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/proto-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/read-time-fields-before-datefromfields.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-infinity-throws-rangeerror.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-leap-second.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-month.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-order.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-ambiguous-wall-clock-time.js92
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-builtin-calendar-no-array-iteration.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-calendar-datefromfields-called-with-null-prototype-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-calendar-number.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-calendar-string.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-calendar-wrong-type.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-getpossibleinstantsfor-called-with-iso8601-calendar.js59
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-invalid-offset-string.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-no-time-units.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-getoffsetnanosecondsfor-not-callable.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-string-datetime.js66
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-string-leap-second.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-string-year-zero.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-string.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-wrong-type.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-required.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-string-datetime.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-string-invalid.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-string-plaindatetime.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-string-zoneddatetime-wrong-offset.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-string-zoneddatetime.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-sub-minute-offset.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-wrong-type.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-year.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-zoneddatetime-negative-epochnanoseconds.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-zoneddatetime-normalized-time-duration-to-days-range-errors.js146
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/result-out-of-range-1.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/result-out-of-range-2.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/subclassing-ignored.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/timezone-getpossibleinstantsfor-iterable.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/year-zero.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/blank/basic.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/blank/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/blank/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/blank/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/blank/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/constructor.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/days/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/days/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/days/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/days/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/hours/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/hours/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/hours/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/hours/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/microseconds/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/microseconds/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/microseconds/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/microseconds/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/milliseconds/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/milliseconds/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/milliseconds/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/milliseconds/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/minutes/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/minutes/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/minutes/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/minutes/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/months/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/months/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/months/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/months/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/nanoseconds/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/nanoseconds/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/nanoseconds/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/nanoseconds/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/basic.js54
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/subclassing-ignored.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/prop-desc.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/balance-negative-result.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/balance-subseconds.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/calendar-dateadd-called-with-options-undefined.js60
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/calendar-dateadd-called-with-plaindate-instance.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/calendar-dateuntil-called-with-singular-largestunit.js157
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/calendar-fields-iterable.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/calendar-possibly-required.js65
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/calendar-temporal-object.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/constructor-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/date-and-time-durations-opposite-signs.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/dateuntil-field.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/duplicate-calendar-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/duration-out-of-range-added-to-relativeto.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/february-leap-year.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/largestunit-correct-rebalancing.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/largestunit-invalid-string.js14
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/largestunit-plurals-accepted.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/largestunit-smallestunit-default.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/largestunit-smallestunit-mismatch.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/largestunit-undefined.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/largestunit-wrong-type.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/normalized-time-duration-to-days-loop-arbitrarily.js79
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/options-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/order-of-operations.js458
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/out-of-range-when-converting-from-normalized-duration.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/precision-exact-in-balance-time-duration.js69
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/precision-exact-in-round-duration.js53
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/proto-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/read-time-fields-before-datefromfields.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-infinity-throws-rangeerror.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-leap-second.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-ambiguous-wall-clock-time.js92
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-builtin-calendar-no-array-iteration.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-calendar-datefromfields-called-with-null-prototype-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-calendar-number.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-calendar-string.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-calendar-wrong-type.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-getpossibleinstantsfor-called-with-iso8601-calendar.js59
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-invalid-offset-string.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-no-time-units.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-getoffsetnanosecondsfor-not-callable.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-string-datetime.js66
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-string-leap-second.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-string-year-zero.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-string.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-wrong-type.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-string-datetime.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-string-invalid.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-string-plaindatetime.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-string-zoneddatetime-wrong-offset.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-string-zoneddatetime.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-sub-minute-offset.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-undefined-throw-on-calendar-units.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-wrong-type.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-zoneddatetime-convert.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-zoneddatetime-negative-epochnanoseconds.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-zoneddatetime-normalized-time-duration-to-days-range-errors.js128
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-zoneddatetime-slots.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/result-out-of-range.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/round-cross-unit-boundary.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/round-negative-result.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/rounding-is-noop.js89
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingincrement-nan.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingincrement-non-integer.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingincrement-out-of-range.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingincrement-undefined.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingincrement-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-ceil.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-expand.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-floor.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-halfCeil.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-halfEven.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-halfExpand.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-halfFloor.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-halfTrunc.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-invalid-string.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-trunc.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-undefined.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-wrong-type.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundto-invalid-string.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/smallestunit-invalid-string.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/smallestunit-plurals-accepted-string.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/smallestunit-plurals-accepted.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/smallestunit-string-shorthand-string.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/smallestunit-undefined.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/smallestunit-wrong-type.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/smallestunit.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/subclassing-ignored.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/throws-in-balance-duration-when-sign-mismatched-with-zoned-date-time.js51
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/throws-in-unbalance-duration-relative-when-sign-mismatched.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/timezone-getpossibleinstantsfor-iterable.js47
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/total-duration-nanoseconds-too-large-with-zoned-datetime.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/year-zero.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/zero-day-length.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/zero-year-month-week-length.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/seconds/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/seconds/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/seconds/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/seconds/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/sign/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/sign/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/sign/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/sign/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/argument-duration-max.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/argument-duration-out-of-range.js75
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/argument-duration-precision-exact-numerical-values.js79
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/argument-invalid-property.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/argument-mixed-sign.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/argument-not-object.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/argument-singular-properties.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/argument-string-fractional-units-rounding-mode.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/argument-string-negative-fractional-units.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/argument-string.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/balance-negative-result.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/balance-negative-time-units.js63
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/basic.js69
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/calendar-dateadd-called-with-options-undefined.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/calendar-dateadd-called-with-plaindate-instance.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/calendar-dateadd.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/calendar-dateuntil-called-with-singular-largestunit.js81
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/calendar-fields-iterable.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/calendar-temporal-object.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/constructor-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/duplicate-calendar-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/infinity-throws-rangeerror.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/intermediate-instant-too-large-with-zoneddatetime.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/nanoseconds-is-number-max-safe-integer.js60
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/negative-infinity-throws-rangeerror.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/non-integer-throws-rangeerror.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/normalized-time-duration-to-days-loop-arbitrarily.js78
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/options-object.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/options-undefined.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/options-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/order-of-operations.js493
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/precision-exact-mathematical-values.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/precision-no-floating-point-loss.js52
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/proto-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/read-time-fields-before-datefromfields.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-infinity-throws-rangeerror.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-leap-second.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-month.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-order.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-ambiguous-wall-clock-time.js92
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-builtin-calendar-no-array-iteration.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-calendar-datefromfields-called-with-null-prototype-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-calendar-number.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-calendar-string.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-calendar-wrong-type.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-getpossibleinstantsfor-called-with-iso8601-calendar.js59
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-invalid-offset-string.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-no-time-units.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-getoffsetnanosecondsfor-not-callable.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-string-datetime.js66
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-string-leap-second.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-string-year-zero.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-string.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-wrong-type.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-required.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-string-datetime.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-string-invalid.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-string-plaindatetime.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-string-zoneddatetime-wrong-offset.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-string-zoneddatetime.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-sub-minute-offset.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-wrong-type.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-year.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-zoneddatetime-negative-epochnanoseconds.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-zoneddatetime-normalized-time-duration-to-days-range-errors.js144
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/result-out-of-range-1.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/result-out-of-range-2.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/subclassing-ignored.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/timezone-getpossibleinstantsfor-iterable.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/year-zero.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/balance-subseconds.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/basic.js243
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/max-value.js14
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/negative-components.js14
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/options.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/toLocaleString/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/toLocaleString/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/toLocaleString/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/toLocaleString/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/toLocaleString/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/toLocaleString/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/toLocaleString/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/toLocaleString/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/balance-subseconds.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/balance.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/blank-duration-precision.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-auto.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-exact-number-of-digits.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-invalid-string.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-nan.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-non-integer.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-number.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-out-of-range.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-undefined.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-wrong-type.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/max-value.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/negative-components.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/options-object.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/options-undefined.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/options-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/order-of-operations.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/precision.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/round-cross-unit-boundary.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/roundingmode-ceil.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/roundingmode-floor.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/roundingmode-halfExpand.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/roundingmode-invalid-string.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/roundingmode-trunc.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/roundingmode-undefined.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/roundingmode-wrong-type.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/smallestunit-fractionalseconddigits.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/smallestunit-invalid-string.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/smallestunit-plurals-accepted.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/smallestunit-undefined.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/smallestunit-valid-units.js57
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/smallestunit-wrong-type.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/throws-when-rounded-duration-is-invalid.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/toStringTag/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/toStringTag/prop-desc.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/toStringTag/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/balance-negative-result.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/balance-subseconds.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/calendar-dateadd-called-with-options-undefined.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/calendar-dateadd-called-with-plaindate-instance.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/calendar-dateuntil-called-with-singular-largestunit.js68
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/calendar-fields-iterable.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/calendar-possibly-required.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/calendar-temporal-object.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/constructor-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/dateuntil-field.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/duplicate-calendar-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/duration-out-of-range-added-to-relativeto.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/normalized-time-duration-to-days-loop-arbitrarily.js80
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/options-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/order-of-operations.js396
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/precision-exact-mathematical-values-1.js99
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/precision-exact-mathematical-values-2.js101
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/precision-exact-mathematical-values-3.js121
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/precision-exact-mathematical-values-4.js155
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/precision-exact-mathematical-values-5.js61
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/precision-exact-mathematical-values-6.js143
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/precision-exact-mathematical-values-7.js122
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/proto-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/read-time-fields-before-datefromfields.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-infinity-throws-rangeerror.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-leap-second.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-plaindate-add24hourdaystonormalizedtimeduration-out-of-range.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-ambiguous-wall-clock-time.js92
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-builtin-calendar-no-array-iteration.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-calendar-datefromfields-called-with-null-prototype-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-calendar-number.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-calendar-string.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-calendar-wrong-type.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-getpossibleinstantsfor-called-with-iso8601-calendar.js59
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-invalid-offset-string.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-no-time-units.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-getoffsetnanosecondsfor-not-callable.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-string-datetime.js66
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-string-leap-second.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-string-year-zero.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-string.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-wrong-type.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-string-datetime.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-string-invalid.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-string-plaindatetime-invalid.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-string-plaindatetime.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-string-zoneddatetime-wrong-offset.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-string-zoneddatetime.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-sub-minute-offset.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-undefined-throw-on-calendar-units.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-wrong-type.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-negative-epochnanoseconds.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-normalized-time-duration-to-days-range-errors.js128
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-with-fractional-days-different-sign.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-with-fractional-days.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/timezone-getpossibleinstantsfor-iterable.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/unit-disallowed-units-string.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/unit-invalid-string.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/unit-plurals-accepted-string.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/unit-plurals-accepted.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/unit-string-shorthand-string.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/unit-wrong-type.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/year-zero.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/zero-day-length.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/zero-year-month-week-length.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/valueOf/basic.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/valueOf/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/valueOf/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/valueOf/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/valueOf/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/valueOf/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/valueOf/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/valueOf/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/valueOf/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/weeks/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/weeks/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/weeks/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/weeks/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/all-negative.js96
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/all-positive.js95
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/argument-invalid-property.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/argument-mixed-sign.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/argument-not-object.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/argument-singular-properties.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/copy-properties-not-undefined.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/infinity-throws-rangeerror.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/negative-infinity-throws-rangeerror.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/non-integer-throws-rangeerror.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/order-of-operations.js62
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/partial-positive.js89
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/sign-conflict-throws-rangeerror.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/sign-replace.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/subclassing-ignored.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/years/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/years/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/years/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/prototype/years/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/seconds-undefined.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/subclass.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/weeks-undefined.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Duration/years-undefined.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/argument.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/basic.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/builtin.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/compare/argument-object-tostring.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/compare/argument-string-calendar-annotation.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/compare/argument-string-critical-unknown-annotation.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/compare/argument-string-date-with-utc-offset.js56
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/compare/argument-string-invalid.js74
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/compare/argument-string-multiple-calendar.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/compare/argument-string-multiple-time-zone.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/compare/argument-string-time-separators.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/compare/argument-string-time-zone-annotation.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/compare/argument-string-unknown-annotation.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/compare/argument-string-with-offset-not-valid-epoch-nanoseconds.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/compare/argument-wrong-type.js62
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/compare/argument-zoneddatetime.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/compare/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/compare/builtin.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/compare/exhaustive.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/compare/instant-string-limits.js51
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/compare/instant-string-multiple-offsets.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/compare/instant-string-sub-minute-offset.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/compare/instant-string.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/compare/leap-second.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/compare/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/compare/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/compare/not-a-constructor.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/compare/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/compare/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/compare/year-zero.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/constructor.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/from/argument-instant.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/from/argument-object-tostring.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/from/argument-string-calendar-annotation.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/from/argument-string-critical-unknown-annotation.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/from/argument-string-date-with-utc-offset.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/from/argument-string-invalid.js67
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/from/argument-string-multiple-calendar.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/from/argument-string-multiple-time-zone.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/from/argument-string-time-separators.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/from/argument-string-time-zone-annotation.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/from/argument-string-unknown-annotation.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/from/argument-string.js80
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/from/argument-wrong-type.js44
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/from/argument-zoneddatetime.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/from/basic.js62
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/from/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/from/builtin.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/from/instant-string-limits.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/from/instant-string-multiple-offsets.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/from/instant-string-sub-minute-offset.js65
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/from/instant-string.js44
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/from/leap-second.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/from/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/from/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/from/not-a-constructor.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/from/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/from/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/from/subclassing-ignored.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/from/timezone-custom.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/from/year-zero.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMicroseconds/basic.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMicroseconds/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMicroseconds/builtin.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMicroseconds/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMicroseconds/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMicroseconds/not-a-constructor.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMicroseconds/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMicroseconds/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMicroseconds/subclassing-ignored.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMilliseconds/basic.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMilliseconds/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMilliseconds/builtin.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMilliseconds/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMilliseconds/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMilliseconds/not-a-constructor.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMilliseconds/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMilliseconds/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMilliseconds/subclassing-ignored.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/fromEpochNanoseconds/basic.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/fromEpochNanoseconds/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/fromEpochNanoseconds/builtin.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/fromEpochNanoseconds/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/fromEpochNanoseconds/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/fromEpochNanoseconds/not-a-constructor.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/fromEpochNanoseconds/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/fromEpochNanoseconds/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/fromEpochNanoseconds/subclassing-ignored.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/fromEpochSeconds/basic.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/fromEpochSeconds/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/fromEpochSeconds/builtin.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/fromEpochSeconds/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/fromEpochSeconds/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/fromEpochSeconds/not-a-constructor.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/fromEpochSeconds/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/fromEpochSeconds/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/fromEpochSeconds/subclassing-ignored.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/argument-duration-max.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/argument-duration-out-of-range.js75
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/argument-invalid-property.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/argument-mixed-sign.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/argument-not-object.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/argument-singular-properties.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/argument-string-fractional-units-rounding-mode.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/argument-string-negative-fractional-units.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/argument-string.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/basic.js75
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/disallowed-duration-units.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/infinity-throws-rangeerror.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/minimum-maximum-instant.js63
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/negative-infinity-throws-rangeerror.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/non-integer-throws-rangeerror.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/order-of-operations.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/result-out-of-range.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/subclassing-ignored.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/builtin.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/constructor.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochMicroseconds/basic.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochMicroseconds/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochMicroseconds/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochMicroseconds/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochMicroseconds/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochMilliseconds/basic.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochMilliseconds/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochMilliseconds/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochMilliseconds/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochMilliseconds/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochNanoseconds/basic.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochNanoseconds/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochNanoseconds/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochNanoseconds/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochNanoseconds/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochSeconds/basic.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochSeconds/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochSeconds/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochSeconds/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochSeconds/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/argument-object-tostring.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/argument-string-calendar-annotation.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/argument-string-critical-unknown-annotation.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/argument-string-date-with-utc-offset.js52
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/argument-string-invalid.js69
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/argument-string-multiple-calendar.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/argument-string-multiple-time-zone.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/argument-string-time-separators.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/argument-string-time-zone-annotation.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/argument-string-unknown-annotation.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/argument-wrong-type.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/argument-zoneddatetime.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/instant-string-limits.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/instant-string-multiple-offsets.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/instant-string-sub-minute-offset.js67
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/instant-string.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/leap-second.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/year-zero.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/prop-desc.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/options-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/rounding-direction.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingincrement-nan.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingincrement-non-integer.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingincrement-out-of-range.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingincrement-undefined.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingincrement-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingmode-ceil.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingmode-expand.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingmode-floor.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingmode-halfCeil.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingmode-halfEven.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingmode-halfExpand.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingmode-halfFloor.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingmode-halfTrunc.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingmode-invalid-string.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingmode-trunc.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingmode-undefined.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingmode-wrong-type.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundto-invalid-string.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/smallestunit-invalid-string.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/smallestunit-plurals-accepted.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/smallestunit-string-shorthand.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/smallestunit-wrong-type.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/subclassing-ignored.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/argument-object-tostring.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/argument-string-calendar-annotation.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/argument-string-critical-unknown-annotation.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/argument-string-date-with-utc-offset.js53
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/argument-string-invalid.js69
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/argument-string-multiple-calendar.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/argument-string-multiple-time-zone.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/argument-string-time-separators.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/argument-string-time-zone-annotation.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/argument-string-unknown-annotation.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/argument-wrong-type.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/argument-zoneddatetime.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/instant-string-limits.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/instant-string-multiple-offsets.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/instant-string-sub-minute-offset.js68
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/instant-string.js47
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/largestunit-invalid-string.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/largestunit-plurals-accepted.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/largestunit-smallestunit-mismatch.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/largestunit-undefined.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/largestunit-wrong-type.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/largestunit.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/leap-second.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/options-object.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/options-undefined.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/options-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/order-of-operations.js57
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/round-cross-unit-boundary.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/roundingincrement-nan.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/roundingincrement-non-integer.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/roundingincrement-out-of-range.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/roundingincrement-undefined.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/roundingincrement-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/roundingmode-ceil.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/roundingmode-expand.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/roundingmode-floor.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/roundingmode-halfCeil.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/roundingmode-halfEven.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/roundingmode-halfExpand.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/roundingmode-halfFloor.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/roundingmode-halfTrunc.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/roundingmode-invalid-string.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/roundingmode-trunc.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/roundingmode-undefined.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/roundingmode-wrong-type.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/smallestunit-invalid-string.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/smallestunit-plurals-accepted.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/smallestunit-undefined.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/smallestunit-wrong-type.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/year-zero.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/argument-duration-max.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/argument-duration-out-of-range.js75
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/argument-invalid-property.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/argument-mixed-sign.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/argument-not-object.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/argument-singular-properties.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/argument-string-fractional-units-rounding-mode.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/argument-string-negative-fractional-units.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/argument-string.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/basic.js75
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/disallowed-duration-units.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/infinity-throws-rangeerror.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/minimum-maximum-instant.js55
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/negative-infinity-throws-rangeerror.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/non-integer-throws-rangeerror.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/order-of-operations.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/result-out-of-range.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/subclassing-ignored.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toJSON/basic.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toJSON/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toJSON/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toJSON/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toJSON/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toJSON/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toJSON/negative-epochnanoseconds.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toJSON/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toJSON/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toJSON/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toJSON/year-format.js56
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toLocaleString/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toLocaleString/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toLocaleString/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toLocaleString/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toLocaleString/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toLocaleString/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toLocaleString/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toLocaleString/return-string.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toLocaleString/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/basic.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/fractionalseconddigits-auto.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/fractionalseconddigits-invalid-string.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/fractionalseconddigits-nan.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/fractionalseconddigits-non-integer.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/fractionalseconddigits-number.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/fractionalseconddigits-out-of-range.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/fractionalseconddigits-undefined.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/fractionalseconddigits-wrong-type.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/negative-epochnanoseconds.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/options-object.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/options-undefined.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/options-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/order-of-operations.js65
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/precision.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/rounding-cross-midnight.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/rounding-direction.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/roundingmode-ceil.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/roundingmode-expand.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/roundingmode-floor.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/roundingmode-halfCeil.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/roundingmode-halfEven.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/roundingmode-halfExpand.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/roundingmode-halfFloor.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/roundingmode-halfTrunc.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/roundingmode-invalid-string.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/roundingmode-trunc.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/roundingmode-undefined.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/roundingmode-wrong-type.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/smallestunit-fractionalseconddigits.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/smallestunit-invalid-string.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/smallestunit-plurals-accepted.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/smallestunit-undefined.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/smallestunit-valid-units.js58
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/smallestunit-wrong-type.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/timezone-getoffsetnanosecondsfor-non-integer.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/timezone-getoffsetnanosecondsfor-not-callable.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/timezone-getoffsetnanosecondsfor-out-of-range.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/timezone-getoffsetnanosecondsfor-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/timezone-offset.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/timezone-string-datetime.js65
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/timezone-string-leap-second.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/timezone-string-multiple-offsets.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/timezone-string-year-zero.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/timezone-string.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/timezone-wrong-type.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/timezone.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/year-format.js56
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toStringTag/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toStringTag/prop-desc.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toStringTag/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/calendar-case-insensitive.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/calendar-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/calendar-string-leap-second.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/calendar-string.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/calendar-temporal-object.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/calendar-wrong-type.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/plain-custom-timezone.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/timezone-case-insensitive.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/timezone-string-datetime.js65
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/timezone-string-leap-second.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/timezone-string-multiple-offsets.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/timezone-string-year-zero.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/timezone-string.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/timezone-wrong-type.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/calendar-is-builtin.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/timezone-case-insensitive.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/timezone-string-datetime.js65
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/timezone-string-leap-second.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/timezone-string-multiple-offsets.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/timezone-string-year-zero.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/timezone-string.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/timezone-wrong-type.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/argument-object-tostring.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/argument-string-calendar-annotation.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/argument-string-critical-unknown-annotation.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/argument-string-date-with-utc-offset.js53
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/argument-string-invalid.js69
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/argument-string-multiple-calendar.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/argument-string-multiple-time-zone.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/argument-string-time-separators.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/argument-string-time-zone-annotation.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/argument-string-unknown-annotation.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/argument-wrong-type.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/argument-zoneddatetime.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/instant-string-limits.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/instant-string-multiple-offsets.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/instant-string-sub-minute-offset.js68
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/instant-string.js47
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/largestunit-invalid-string.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/largestunit-plurals-accepted.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/largestunit-smallestunit-mismatch.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/largestunit-undefined.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/largestunit-wrong-type.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/leap-second.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/options-object.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/options-undefined.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/options-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/order-of-operations.js57
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/round-cross-unit-boundary.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/roundingincrement-nan.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/roundingincrement-non-integer.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/roundingincrement-out-of-range.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/roundingincrement-undefined.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/roundingincrement-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/roundingmode-ceil.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/roundingmode-expand.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/roundingmode-floor.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/roundingmode-halfCeil.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/roundingmode-halfEven.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/roundingmode-halfExpand.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/roundingmode-halfFloor.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/roundingmode-halfTrunc.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/roundingmode-invalid-string.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/roundingmode-trunc.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/roundingmode-undefined.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/roundingmode-wrong-type.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/smallestunit-invalid-string.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/smallestunit-plurals-accepted.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/smallestunit-undefined.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/smallestunit-wrong-type.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/year-zero.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/valueOf/basic.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/valueOf/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/valueOf/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/valueOf/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/valueOf/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/valueOf/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/valueOf/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/valueOf/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/prototype/valueOf/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Instant/subclass.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/builtin.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/instant/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/instant/extensible.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/instant/length.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/instant/name.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/instant/not-a-constructor.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/instant/prop-desc.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/instant/return-value-distinct.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/instant/return-value-instance.js14
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/instant/return-value-prototype.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/instant/return-value-value.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/instant/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDate/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDate/calendar-case-insensitive.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDate/calendar-number.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDate/calendar-string-leap-second.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDate/calendar-string.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDate/calendar-temporal-object.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDate/calendar-undefined.js14
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDate/calendar-wrong-type.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDate/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDate/prop-desc.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDate/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDate/timezone-getoffsetnanosecondsfor-non-integer.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDate/timezone-getoffsetnanosecondsfor-not-callable.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDate/timezone-getoffsetnanosecondsfor-out-of-range.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDate/timezone-getoffsetnanosecondsfor-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDate/timezone-string-datetime.js64
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDate/timezone-string-leap-second.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDate/timezone-string-year-zero.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDate/timezone-string.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDate/timezone-wrong-type.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDate/toPlainDate-override.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateISO/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateISO/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateISO/prop-desc.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateISO/return-value.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateISO/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateISO/timezone-getoffsetnanosecondsfor-non-integer.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateISO/timezone-getoffsetnanosecondsfor-not-callable.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateISO/timezone-getoffsetnanosecondsfor-out-of-range.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateISO/timezone-getoffsetnanosecondsfor-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateISO/timezone-string-datetime.js64
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateISO/timezone-string-leap-second.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateISO/timezone-string-year-zero.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateISO/timezone-string.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateISO/timezone-wrong-type.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/calendar-case-insensitive.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/calendar-function.js64
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/calendar-number.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/calendar-object.js52
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/calendar-string-leap-second.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/calendar-string.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/calendar-temporal-object.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/calendar-undefined.js14
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/calendar-wrong-type.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/extensible.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/name.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/not-a-constructor.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/prop-desc.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/return-value.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/time-zone-undefined.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/timezone-getoffsetnanosecondsfor-invocation.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/timezone-getoffsetnanosecondsfor-non-integer.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/timezone-getoffsetnanosecondsfor-non-method.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/timezone-getoffsetnanosecondsfor-not-a-number.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/timezone-getoffsetnanosecondsfor-not-callable.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/timezone-getoffsetnanosecondsfor-out-of-range.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/timezone-getoffsetnanosecondsfor-poisoned.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/timezone-getoffsetnanosecondsfor-throws.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/timezone-getoffsetnanosecondsfor-wrong-type.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/timezone-object.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/timezone-string-datetime.js64
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/timezone-string-leap-second.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/timezone-string-year-zero.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/timezone-string.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/timezone-wrong-type.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/timezone.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/extensible.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/name.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/not-a-constructor.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/prop-desc.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/return-value-calendar.js14
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/return-value.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/time-zone-undefined.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/timezone-getoffsetnanosecondsfor-invocation.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/timezone-getoffsetnanosecondsfor-non-integer.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/timezone-getoffsetnanosecondsfor-non-method.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/timezone-getoffsetnanosecondsfor-not-a-number.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/timezone-getoffsetnanosecondsfor-not-callable.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/timezone-getoffsetnanosecondsfor-out-of-range.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/timezone-getoffsetnanosecondsfor-poisoned.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/timezone-getoffsetnanosecondsfor-throws.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/timezone-getoffsetnanosecondsfor-wrong-type.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/timezone-object.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/timezone-string-datetime.js64
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/timezone-string-leap-second.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/timezone-string-year-zero.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/timezone-string.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/timezone-wrong-type.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainTimeISO/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainTimeISO/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainTimeISO/prop-desc.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainTimeISO/return-value.js14
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainTimeISO/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainTimeISO/timezone-getoffsetnanosecondsfor-non-integer.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainTimeISO/timezone-getoffsetnanosecondsfor-not-callable.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainTimeISO/timezone-getoffsetnanosecondsfor-out-of-range.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainTimeISO/timezone-getoffsetnanosecondsfor-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainTimeISO/timezone-string-datetime.js64
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainTimeISO/timezone-string-leap-second.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainTimeISO/timezone-string-year-zero.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainTimeISO/timezone-string.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainTimeISO/timezone-wrong-type.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainTimeISO/timezone.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/plainTimeISO/toPlainTime-override.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/timeZoneId/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/timeZoneId/extensible.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/timeZoneId/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/timeZoneId/name.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/timeZoneId/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/timeZoneId/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/timeZoneId/return-value.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/timeZoneId/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/toStringTag/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/toStringTag/prop-desc.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/toStringTag/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/toStringTag/string.js13
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/calendar-case-insensitive.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/calendar-function.js61
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/calendar-number.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/calendar-object.js52
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/calendar-string-leap-second.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/calendar-string.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/calendar-temporal-object.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/calendar-undefined.js14
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/calendar-wrong-type.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/extensible.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/name.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/not-a-constructor.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/prop-desc.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/time-zone-undefined.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/timezone-case-insensitive.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/timezone-object.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/timezone-string-datetime.js63
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/timezone-string-leap-second.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/timezone-string-multiple-offsets.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/timezone-string-year-zero.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/timezone-string.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/timezone-wrong-type.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/extensible.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/name.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/not-a-constructor.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/prop-desc.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/return-value.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/time-zone-undefined.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/timezone-case-insensitive.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/timezone-object.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/timezone-string-datetime.js63
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/timezone-string-leap-second.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/timezone-string-multiple-offsets.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/timezone-string-year-zero.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/timezone-string.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/timezone-wrong-type.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/argument-convert.js55
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/argument-invalid.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/basic.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/builtin.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/calendar-case-insensitive.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/calendar-number.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/calendar-string.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/calendar-temporal-object.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/calendar-undefined.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/calendar-wrong-type.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-builtin-calendar-no-array-iteration.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-calendar-datefromfields-called-with-null-prototype-fields.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-constructor-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-duplicate-calendar-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-number.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-object.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-plaindatetime.js53
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-propertybag-calendar-case-insensitive.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-propertybag-calendar-leap-second.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-propertybag-calendar-number.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-propertybag-calendar-string.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-propertybag-calendar-wrong-type.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-propertybag-calendar-year-zero.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-proto-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-calendar-annotation.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-critical-unknown-annotation.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-date-with-utc-offset.js51
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-invalid.js69
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-multiple-calendar.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-multiple-time-zone.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-time-separators.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-time-zone-annotation.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-unknown-annotation.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-with-utc-designator.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-wrong-type.js47
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-zoneddatetime-slots.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-zoneddatetime.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/basic.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/builtin.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/calendar-datefromfields-called-with-options-undefined.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/calendar-fields-iterable.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/calendar-temporal-object.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/calendar.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/exhaustive.js71
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/infinity-throws-rangeerror.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/leap-second.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/not-a-constructor.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/use-internal-slots.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/compare/year-zero.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/constructor.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-builtin-calendar-no-array-iteration.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-calendar-datefromfields-called-with-null-prototype-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-constructor-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-duplicate-calendar-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-leap-second.js44
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-number.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-object-invalid.js44
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-object-valid.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-plaindate.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-plaindatetime.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar-case-insensitive.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar-leap-second.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar-number.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar-string.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar-wrong-type.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar-year-zero.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-proto-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-calendar-annotation.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-critical-unknown-annotation.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-date-with-utc-offset.js47
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-invalid.js63
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-multiple-calendar.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-multiple-time-zone.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-time-separators.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-time-zone-annotation.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-trailing-junk.js14
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-unknown-annotation.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-with-utc-designator.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-wrong-type.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-zoneddatetime-convert.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-zoneddatetime-slots.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-zoneddatetime.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/builtin.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/calendar-fields-custom.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/calendar-fields-iterable.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/calendar-temporal-object.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/infinity-throws-rangeerror.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/limits.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/not-a-constructor.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/observable-get-overflow-argument-primitive.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/observable-get-overflow-argument-string-invalid.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/options-object.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/options-undefined.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/options-wrong-type.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/order-of-operations.js79
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/overflow-invalid-string.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/overflow-undefined.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/overflow-wrong-type.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/subclassing-ignored.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/from/year-zero.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/infinity-throws-rangeerror.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/limits.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/missing-arguments.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/negative-infinity-throws-rangeerror.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-duration-max.js58
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-duration-out-of-range.js75
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-invalid-property.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-mixed-sign.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-not-object.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-singular-properties.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-string-negative-fractional-units.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-string.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/balance-smaller-units-basic.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/balance-smaller-units.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/basic.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/calendar-invalid-return.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/custom.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/infinity-throws-rangeerror.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/limits.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/negative-infinity-throws-rangeerror.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/non-integer-throws-rangeerror.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/options-object.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/options-undefined.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/options-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/order-of-operations.js129
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/overflow-constrain.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/overflow-invalid-string.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/overflow-reject.js14
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/overflow-undefined.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/overflow-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/subclassing-ignored.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/calendarId/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/calendarId/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/calendarId/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/calendarId/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/calendarId/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/constructor.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/day/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/day/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/day/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/day/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/day/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/day/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/day/validate-calendar-value.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfWeek/basic.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfWeek/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfWeek/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfWeek/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfWeek/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfWeek/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfWeek/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfWeek/validate-calendar-value.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/basic.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/validate-calendar-value.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/basic.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/validate-calendar-value.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInWeek/basic.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInWeek/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInWeek/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInWeek/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInWeek/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInWeek/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInWeek/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInWeek/validate-calendar-value.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/basic.js14
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/validate-calendar-value.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-builtin-calendar-no-array-iteration.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-calendar-datefromfields-called-with-null-prototype-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-constructor-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-duplicate-calendar-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-leap-second.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-object-invalid.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-object-valid.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-plaindatetime.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-propertybag-calendar-case-insensitive.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-propertybag-calendar-leap-second.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-propertybag-calendar-number.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-propertybag-calendar-string.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-propertybag-calendar-wrong-type.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-propertybag-calendar-year-zero.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-proto-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-calendar-annotation.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-critical-unknown-annotation.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-date-with-utc-offset.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-invalid.js64
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-multiple-calendar.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-multiple-time-zone.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-time-separators.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-time-zone-annotation.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-unknown-annotation.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-with-utc-designator.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-wrong-type.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-zoneddatetime-convert.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-zoneddatetime-slots.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/basic.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/calendar-call-different.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/calendar-call-same.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/calendar-datefromfields-called-with-options-undefined.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/calendar-fields-iterable.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/calendar-no-call.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/calendar-temporal-object.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/infinity-throws-rangeerror.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/year-zero.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getCalendar/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getCalendar/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getCalendar/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getCalendar/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getCalendar/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getCalendar/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getCalendar/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getCalendar/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/custom.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/field-names.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/field-prop-desc.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/field-traversal-order.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/prototype.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/basic.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/validate-calendar-value.js56
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/month/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/month/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/month/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/month/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/month/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/month/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/month/validate-calendar-value.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthCode/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthCode/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthCode/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthCode/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthCode/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthCode/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthCode/validate-calendar-value.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/basic.js14
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/validate-calendar-value.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/prop-desc.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-builtin-calendar-no-array-iteration.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-calendar-datefromfields-called-with-null-prototype-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-constructor-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-duplicate-calendar-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-leap-second.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-plaindatetime.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-propertybag-calendar-case-insensitive.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-propertybag-calendar-leap-second.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-propertybag-calendar-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-propertybag-calendar-string.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-propertybag-calendar-wrong-type.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-propertybag-calendar-year-zero.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-proto-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-calendar-annotation.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-critical-unknown-annotation.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-date-with-utc-offset.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-invalid.js64
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-multiple-calendar.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-multiple-time-zone.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-time-separators.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-time-zone-annotation.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-unknown-annotation.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-with-utc-designator.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-wrong-type.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-zoneddatetime-convert.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-zoneddatetime-slots.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/basic.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-dateadd-called-with-plaindate-instance.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-datefromfields-called-with-options-undefined.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-dateuntil-called-with-null-prototype-options.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-dateuntil-called-with-singular-largestunit.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-fields-iterable.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-id-match.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-invalid-return.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-mismatch.js62
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-temporal-object.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/custom.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/days-in-month.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/days-in-year.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/infinity-throws-rangeerror.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/largestunit-default.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/largestunit-higher-units.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/largestunit-invalid-string.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/largestunit-plurals-accepted.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/largestunit-smallestunit-mismatch.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/largestunit-undefined.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/largestunit-wrong-type.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/largestunit.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/options-object.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/options-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/order-of-operations.js213
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/round-cross-unit-boundary.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/rounding-relative.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/rounding-zero-year-month-week-length.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingincrement-nan.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingincrement-non-integer.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingincrement-out-of-range.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingincrement-undefined.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingincrement-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingincrement.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-ceil.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-expand.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-floor.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-halfCeil.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-halfEven.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-halfExpand.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-halfFloor.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-halfTrunc.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-invalid-string.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-trunc.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-undefined.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-wrong-type.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/smallestunit-higher-units.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/smallestunit-invalid-string.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/smallestunit-plurals-accepted.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/smallestunit-undefined.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/smallestunit-wrong-type.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/weeks-months.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/year-zero.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-duration-max.js58
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-duration-out-of-range.js75
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-invalid-property.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-mixed-sign.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-not-object.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-singular-properties.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-string-negative-fractional-units.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-string.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/balance-smaller-units-basic.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/balance-smaller-units.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/basic.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/calendar-invalid-return.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/custom.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/infinity-throws-rangeerror.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/limits.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/negative-infinity-throws-rangeerror.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/non-integer-throws-rangeerror.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/options-object.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/options-undefined.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/options-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/order-of-operations.js129
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/overflow-constrain.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/overflow-invalid-string.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/overflow-reject.js14
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/overflow-undefined.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/overflow-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/subclassing-ignored.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/basic.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/year-format.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/return-string.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-object.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-calendar-annotation.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-critical-unknown-annotation.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-date-with-utc-offset.js53
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-multiple-calendar.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-multiple-time-zone.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-no-implicit-midnight.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-time-designator-required-for-disambiguation.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-time-separators.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-time-zone-annotation.js51
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-unknown-annotation.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-with-time-designator.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-with-utc-designator.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-wrong-type.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-zoneddatetime-balance-negative-time-units.js47
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-zoneddatetime-negative-epochnanoseconds.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/basic.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/custom.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/leap-second.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/limits.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/plaintime-propertybag-no-time-units.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/time-invalid.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/time-undefined.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/year-zero.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/basic.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/builtin-calendar-no-array-iteration.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/calendar-arguments.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/calendar-fields-iterable.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/calendar-fromfields-called-with-null-prototype-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/calendar-invalid-return.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/calendar-monthdayfromfields-called-with-options-undefined.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/constructor-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/duplicate-calendar-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/proto-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/basic.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/builtin-calendar-no-array-iteration.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/calendar-arguments.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/calendar-fields-iterable.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/calendar-fromfields-called-with-null-prototype-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/calendar-invalid-return.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/calendar-yearmonthfromfields-called-with-options-undefined.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/constructor-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/duplicate-calendar-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/limits.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/proto-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/basic.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/calendar-tostring.js56
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/calendarname-always.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/calendarname-auto.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/calendarname-critical.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/calendarname-invalid-string.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/calendarname-never.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/calendarname-undefined.js56
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/calendarname-wrong-type.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/options-object.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/options-undefined.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/options-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/order-of-operations.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/year-format.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toStringTag/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toStringTag/prop-desc.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toStringTag/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-calendar-annotation.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-critical-unknown-annotation.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-date-with-utc-offset.js52
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-multiple-calendar.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-multiple-time-zone.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-no-implicit-midnight.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-time-designator-required-for-disambiguation.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-time-separators.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-time-zone-annotation.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-unknown-annotation.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-with-time-designator.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-with-utc-designator.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-wrong-type.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-zoneddatetime-negative-epochnanoseconds.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/basic.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/calendar.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/getpossibleinstantsfor-called-with-iso8601-calendar.js58
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/leap-second.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/order-of-operations.js98
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/plaintime-argument-zoneddatetime-balance-negative-time-units.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/plaintime-argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/plaintime-argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/plaintime-argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/plaintime-argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/plaintime-propertybag-no-time-units.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-case-insensitive.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-non-integer.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-not-callable.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-out-of-range.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-wrong-type.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-getpossibleinstantsfor-iterable.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-getpossibleinstantsfor.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-string-datetime.js65
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-string-leap-second.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-string-multiple-offsets.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-string-year-zero.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-string.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-wrong-type.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/year-zero.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-builtin-calendar-no-array-iteration.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-calendar-datefromfields-called-with-null-prototype-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-constructor-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-duplicate-calendar-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-leap-second.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-plaindatetime.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-propertybag-calendar-case-insensitive.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-propertybag-calendar-leap-second.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-propertybag-calendar-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-propertybag-calendar-string.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-propertybag-calendar-wrong-type.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-propertybag-calendar-year-zero.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-proto-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-calendar-annotation.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-critical-unknown-annotation.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-date-with-utc-offset.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-invalid.js64
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-multiple-calendar.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-multiple-time-zone.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-time-separators.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-time-zone-annotation.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-unknown-annotation.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-with-utc-designator.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-wrong-type.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-zoneddatetime-convert.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-zoneddatetime-slots.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/basic.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-dateadd-called-with-plaindate-instance.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-datefromfields-called-with-options-undefined.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-dateuntil-called-with-null-prototype-options.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-dateuntil-called-with-singular-largestunit.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-fields-iterable.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-id-match.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-invalid-return.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-mismatch.js62
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-temporal-object.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/custom.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/days-in-month.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/days-in-year.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/infinity-throws-rangeerror.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/largestunit-default.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/largestunit-higher-units.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/largestunit-invalid-string.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/largestunit-plurals-accepted.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/largestunit-smallestunit-mismatch.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/largestunit-undefined.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/largestunit-wrong-type.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/options-object.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/options-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/order-of-operations.js214
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/round-cross-unit-boundary.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/rounding-relative.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/rounding-zero-year-month-week-length.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingincrement-nan.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingincrement-non-integer.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingincrement-out-of-range.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingincrement-undefined.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingincrement-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingincrement.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-ceil.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-expand.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-floor.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-halfCeil.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-halfEven.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-halfExpand.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-halfFloor.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-halfTrunc.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-invalid-string.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-trunc.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-undefined.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-wrong-type.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/smallestunit-higher-units.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/smallestunit-invalid-string.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/smallestunit-plurals-accepted.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/smallestunit-undefined.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/smallestunit-wrong-type.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/weeks-months.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/year-zero.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/basic.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/weekOfYear/basic.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/weekOfYear/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/weekOfYear/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/weekOfYear/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/weekOfYear/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/weekOfYear/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/weekOfYear/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/weekOfYear/validate-calendar-value.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/basic.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/builtin-calendar-no-array-iteration.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/builtin-calendar-no-observable-calls.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/calendar-fields-iterable.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/calendar-fromfields-called-with-null-prototype-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/calendar-invalid-return.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/calendar-merge-fields-returns-primitive.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/calendar-mergefields-called-with-null-prototype-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/constructor-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/copies-merge-fields-object.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/copy-properties-not-undefined.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/custom.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/duplicate-calendar-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/infinity-throws-rangeerror.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/options-object.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/options-undefined.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/options-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/order-of-operations.js80
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/overflow-invalid-string.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/overflow-undefined.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/overflow-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/plaindatelike-invalid.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/proto-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/subclassing-ignored.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/basic.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/calendar-case-insensitive.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/calendar-number.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/calendar-string-leap-second.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/calendar-string.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/calendar-temporal-object.js64
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/calendar-wrong-type.js64
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/missing-argument.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/subclassing-ignored.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/year/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/year/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/year/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/year/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/year/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/year/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/year/validate-calendar-value.js54
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/yearOfWeek/basic.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/yearOfWeek/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/yearOfWeek/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/yearOfWeek/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/yearOfWeek/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/yearOfWeek/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/yearOfWeek/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/yearOfWeek/validate-calendar-value.js55
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDate/subclass.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/builtin.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/calendar-case-insensitive.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/calendar-number.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/calendar-string.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/calendar-temporal-object.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/calendar-undefined.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/calendar-wrong-type.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-builtin-calendar-no-array-iteration.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-number.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-object-insufficient-data.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-plaindate.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-propertybag-calendar-case-insensitive.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-propertybag-calendar-leap-second.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-propertybag-calendar-number.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-propertybag-calendar-string.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-propertybag-calendar-wrong-type.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-propertybag-calendar-year-zero.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-calendar-annotation.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-critical-unknown-annotation.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-date-with-utc-offset.js51
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-multiple-calendar.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-multiple-time-zone.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-time-separators.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-time-zone-annotation.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-unknown-annotation.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-with-utc-designator.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-wrong-type.js47
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-zoneddatetime-negative-epochnanoseconds.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/basic.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/builtin.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/calendar-datefromfields-called-with-null-prototype-fields.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/calendar-fields-iterable.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/calendar-ignored.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/calendar-temporal-object.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/cast.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/constructor-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/duplicate-calendar-fields.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/exhaustive.js167
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/infinity-throws-rangeerror.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/leap-second.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/not-a-constructor.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/proto-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/read-time-fields-before-datefromfields.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/use-internal-slots.js47
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/year-zero.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/constructor-full.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/constructor.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/datetime-math.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-builtin-calendar-no-array-iteration.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-number.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-object-month.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-object.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-plaindate.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-plaindatetime.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-propertybag-calendar-case-insensitive.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-propertybag-calendar-leap-second.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-propertybag-calendar-number.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-propertybag-calendar-string.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-propertybag-calendar-wrong-type.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-propertybag-calendar-year-zero.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-calendar-annotation.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-comma-decimal-separator.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-critical-unknown-annotation.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-date-with-utc-offset.js47
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-invalid.js62
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-minus-sign.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-multiple-calendar.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-multiple-time-zone.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-offset.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-optional-data.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-out-of-range.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-subsecond.js72
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-time-separators.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-time-zone-annotation.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-timezone.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-unknown-annotation.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-with-utc-designator.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-wrong-type.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-zoneddatetime-balance-negative-time-units.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-zoneddatetime-negative-epochnanoseconds.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/builtin.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/calendar-datefromfields-called-with-null-prototype-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/calendar-fields-iterable.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/calendar-temporal-object.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/constructor-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/duplicate-calendar-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/infinity-throws-rangeerror.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/leap-second.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/limits.js73
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/not-a-constructor.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/observable-get-overflow-argument-primitive.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/observable-get-overflow-argument-string-invalid.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/options-object.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/options-undefined.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/options-wrong-type.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/order-of-operations.js107
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/overflow-default-constrain.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/overflow-invalid-string.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/overflow-reject.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/overflow-undefined.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/overflow-wrong-type.js65
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/parser.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/proto-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/read-time-fields-before-datefromfields.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/subclassing-ignored.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/year-zero.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/hour-undefined.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/infinity-throws-rangeerror.js165
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/limits.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/microsecond-undefined.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/millisecond-undefined.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/minute-undefined.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/missing-arguments.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/nanosecond-undefined.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/negative-infinity-throws-rangeerror.js165
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/order-of-operations.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/ambiguous-date.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-duration-max.js58
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-duration-out-of-range.js75
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-duration.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-invalid-property.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-mixed-sign.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-not-object.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-singular-properties.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-string-fractional-units-rounding-mode.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-string-negative-fractional-units.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-string.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/balance-negative-time-units.js52
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/calendar-dateadd.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/hour-overflow.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/infinity-throws-rangeerror.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/limits.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/negative-duration.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/negative-infinity-throws-rangeerror.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/non-integer-throws-rangeerror.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/options-empty.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/options-invalid.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/options-undefined.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/options-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/order-of-operations.js127
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/overflow-invalid-string.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/overflow-undefined.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/overflow-wrong-type.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/subclassing-ignored.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/calendarId/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/calendarId/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/calendarId/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/calendarId/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/calendarId/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/constructor.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/day/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/day/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/day/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/day/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/day/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/day/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/day/validate-calendar-value.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/basic.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/validate-calendar-value.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/basic.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/validate-calendar-value.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/basic.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/validate-calendar-value.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/basic.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/validate-calendar-value.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/basic.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/validate-calendar-value.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-builtin-calendar-no-array-iteration.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-object-insufficient-data.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-plaindate.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-propertybag-calendar-case-insensitive.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-propertybag-calendar-leap-second.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-propertybag-calendar-number.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-propertybag-calendar-string.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-propertybag-calendar-wrong-type.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-propertybag-calendar-year-zero.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-calendar-annotation.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-critical-unknown-annotation.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-date-with-utc-offset.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-multiple-calendar.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-multiple-time-zone.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-time-separators.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-time-zone-annotation.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-unknown-annotation.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-with-utc-designator.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-wrong-type.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-zoneddatetime-balance-negative-time-units.js44
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-zoneddatetime-negative-epochnanoseconds.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/basic.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/calendar-checked.js112
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/calendar-datefromfields-called-with-null-prototype-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/calendar-fields-iterable.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/calendar-temporal-object.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/cast.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/constructor-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/duplicate-calendar-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/infinity-throws-rangeerror.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/leap-second.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/proto-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/read-time-fields-before-datefromfields.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/year-zero.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getCalendar/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getCalendar/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getCalendar/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getCalendar/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getCalendar/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getCalendar/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getCalendar/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getCalendar/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/custom.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/field-names.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/field-prop-desc.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/field-traversal-order.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/prototype.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/hour/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/hour/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/hour/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/hour/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/basic.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/validate-calendar-value.js56
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/microsecond/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/microsecond/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/microsecond/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/microsecond/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/millisecond/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/millisecond/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/millisecond/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/millisecond/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/minute/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/minute/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/minute/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/minute/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/month/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/month/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/month/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/month/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/month/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/month/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/month/validate-calendar-value.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthCode/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthCode/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthCode/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthCode/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthCode/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthCode/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthCode/validate-calendar-value.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/basic.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/validate-calendar-value.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/nanosecond/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/nanosecond/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/nanosecond/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/nanosecond/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/prop-desc.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/balance.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/limits.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/options-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/rounding-direction.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-divides.js56
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-does-not-divide.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-nan.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-non-integer.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-one-day.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-out-of-range.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-undefined.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-wrong-type.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-basic.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-ceil.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-expand.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-floor.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-halfCeil.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-halfEven.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-halfExpand.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-halfFloor.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-halfTrunc.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-halfexpand-is-default.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-invalid-string.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-trunc.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-undefined.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-wrong-type.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundto-invalid-string.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/smallestunit-invalid-string.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/smallestunit-plurals-accepted.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/smallestunit-string-shorthand.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/smallestunit-wrong-type.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/subclassing-ignored.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/throws-argument-object-insufficient-data.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/throws-argument-object.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/throws-no-argument.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/throws-undefined.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/second/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/second/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/second/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/second/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-builtin-calendar-no-array-iteration.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-object.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-plaindate.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-propertybag-calendar-case-insensitive.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-propertybag-calendar-leap-second.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-propertybag-calendar-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-propertybag-calendar-string.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-propertybag-calendar-wrong-type.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-propertybag-calendar-year-zero.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-calendar-annotation.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-critical-unknown-annotation.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-date-with-utc-offset.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-multiple-calendar.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-multiple-time-zone.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-time-separators.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-time-zone-annotation.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-unknown-annotation.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-with-utc-designator.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-wrong-type.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-zoneddatetime-balance-negative-time-units.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-zoneddatetime-negative-epochnanoseconds.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/balance-negative-duration.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/balance-negative-time-units.js53
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/calendar-dateadd-called-with-plaindate-instance.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/calendar-datefromfields-called-with-null-prototype-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/calendar-dateuntil-called-with-copy-of-options.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/calendar-dateuntil-called-with-null-prototype-options.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/calendar-dateuntil-called-with-plaindate-calendar.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/calendar-dateuntil-called-with-singular-largestunit.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/calendar-fields-iterable.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/calendar-temporal-object.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/constructor-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/different-calendars-throws.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/duplicate-calendar-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/infinity-throws-rangeerror.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/largestunit-invalid-string.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/largestunit-plurals-accepted.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/largestunit-smallestunit-mismatch.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/largestunit-undefined.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/largestunit-wrong-type.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/largestunit.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/leap-second.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/no-unnecessary-units.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/options-empty.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/options-invalid.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/options-undefined.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/options-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/order-of-operations.js250
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/proto-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/read-time-fields-before-datefromfields.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/returns-days.js47
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/round-cross-unit-boundary.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/round-negative-duration.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/round-relative-to-receiver.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/rounding-zero-year-month-week-length.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-basic.js51
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-cleanly-divides.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-does-not-divide.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-nan.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-non-integer.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-out-of-range.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-undefined.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-ceil.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-expand.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-floor.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-halfCeil.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-halfEven.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-halfExpand.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-halfFloor.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-halfTrunc.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-halfexpand-default-changes.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-invalid-string.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-trunc-is-default.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-trunc.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-undefined.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-wrong-type.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/smallestunit-invalid-string.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/smallestunit-plurals-accepted.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/smallestunit-undefined.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/smallestunit-wrong-type.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/subseconds.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/weeks-months-mutually-exclusive.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/year-zero.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/ambiguous-date.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-duration-max.js58
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-duration-out-of-range.js75
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-duration.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-invalid-property.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-mixed-sign.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-not-object.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-singular-properties.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-string-fractional-units-rounding-mode.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-string-negative-fractional-units.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-string.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/balance-negative-time-units.js52
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/calendar-dateadd.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/hour-overflow.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/infinity-throws-rangeerror.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/limits.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/negative-duration.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/negative-infinity-throws-rangeerror.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/non-integer-throws-rangeerror.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/options-empty.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/options-invalid.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/options-undefined.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/options-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/order-of-operations.js127
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/overflow-invalid-string.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/overflow-undefined.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/overflow-wrong-type.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/subclassing-ignored.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/basic.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/year-format.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/return-string.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/limits.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/builtin-calendar-no-array-iteration.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/builtin-calendar-no-observable-calls.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/calendar-arguments.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/calendar-fields-iterable.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/calendar-fromfields-called-with-null-prototype-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/calendar-monthdayfromfields-called-with-options-undefined.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/constructor-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/duplicate-calendar-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/proto-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/basic.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/builtin-calendar-no-array-iteration.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/builtin-calendar-no-observable-calls.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/calendar-arguments.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/calendar-fields-iterable.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/calendar-fromfields-called-with-null-prototype-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/calendar-yearmonthfromfields-called-with-options-undefined.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/constructor-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/duplicate-calendar-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/proto-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/basic.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/calendar-tostring.js56
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-always.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-auto.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-critical.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-invalid-string.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-never.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-undefined.js56
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-wrong-type.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-auto.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-invalid-string.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-nan.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-non-integer.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-number.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-out-of-range.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-undefined.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-wrong-type.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/options-object.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/options-undefined.js51
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/options-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/order-of-operations.js70
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/rounding-cross-midnight.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/rounding-direction.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-ceil.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-expand.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-floor.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-halfCeil.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-halfEven.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-halfExpand.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-halfFloor.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-halfTrunc.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-invalid-string.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-trunc.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-undefined.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-wrong-type.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/smallestunit-fractionalseconddigits.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/smallestunit-invalid-string.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/smallestunit-plurals-accepted.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/smallestunit-undefined.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/smallestunit-valid-units.js58
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/smallestunit-wrong-type.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/year-format.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toStringTag/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toStringTag/prop-desc.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toStringTag/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/balance-negative-time-units.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/basic.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/disambiguate-empty-possible-instants-with-datetime-near-limits.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/disambiguation-invalid-string.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/disambiguation-undefined.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/disambiguation-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/getpossibleinstantsfor-called-with-iso8601-calendar.js58
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/invalid-instant.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/multiple-instants.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/options-object.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/options-undefined.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/options-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/order-of-operations.js65
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/plain-custom-timezone.js52
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/plain-date-time-near-limits.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-case-insensitive.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-non-integer.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-not-callable.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-out-of-range.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-wrong-type.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-getpossibleinstantsfor-iterable.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-string-datetime.js65
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-string-leap-second.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-string-multiple-offsets.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-string-year-zero.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-string.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-wrong-type.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-builtin-calendar-no-array-iteration.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-object.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-plaindate.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-propertybag-calendar-case-insensitive.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-propertybag-calendar-leap-second.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-propertybag-calendar-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-propertybag-calendar-string.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-propertybag-calendar-wrong-type.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-propertybag-calendar-year-zero.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-calendar-annotation.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-critical-unknown-annotation.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-date-with-utc-offset.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-multiple-calendar.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-multiple-time-zone.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-time-separators.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-time-zone-annotation.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-unknown-annotation.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-with-utc-designator.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-wrong-type.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-zoneddatetime-balance-negative-time-units.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-zoneddatetime-negative-epochnanoseconds.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/balance-negative-duration.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/balance-negative-time-units.js53
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/balance.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/calendar-dateadd-called-with-plaindate-instance.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/calendar-datefromfields-called-with-null-prototype-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/calendar-dateuntil-called-with-copy-of-options.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/calendar-dateuntil-called-with-null-prototype-options.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/calendar-dateuntil-called-with-plaindate-calendar.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/calendar-dateuntil-called-with-singular-largestunit.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/calendar-fields-iterable.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/calendar-temporal-object.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/casts-argument.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/constructor-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/different-calendars-throws.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/duplicate-calendar-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/infinity-throws-rangeerror.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/inverse.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/largestunit-invalid-string.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/largestunit-plurals-accepted.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/largestunit-smallestunit-mismatch.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/largestunit-undefined.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/largestunit-wrong-type.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/leap-second.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/no-unnecessary-units.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/options-empty.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/options-invalid.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/options-undefined.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/options-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/order-of-operations.js250
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/proto-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/read-time-fields-before-datefromfields.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/returns-days.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/round-cross-unit-boundary.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/round-negative-duration.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/round-relative-to-receiver.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/rounding-zero-year-month-week-length.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-basic.js51
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-cleanly-divides.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-does-not-divide.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-nan.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-non-integer.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-out-of-range.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-undefined.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-ceil.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-expand.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-floor.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-halfCeil.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-halfEven.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-halfExpand.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-halfFloor.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-halfTrunc.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-halfexpand-default-changes.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-invalid-string.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-trunc-is-default.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-trunc.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-undefined.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-wrong-type.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/smallestunit-invalid-string.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/smallestunit-plurals-accepted.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/smallestunit-undefined.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/smallestunit-wrong-type.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/subseconds.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/units-changed.js69
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/weeks-months-mutually-exclusive.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/year-zero.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/basic.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/basic.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/validate-calendar-value.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/argument-not-object.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/argument-object-insufficient-data.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/basic.js80
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/builtin-calendar-no-array-iteration.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/builtin-calendar-no-observable-calls.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/calendar-fields-iterable.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/calendar-fromfields-called-with-null-prototype-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/calendar-merge-fields-returns-primitive.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/calendar-mergefields-called-with-null-prototype-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/calendar-options.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/calendar-temporal-object-throws.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/calendar-throws.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/constructor-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/copies-merge-fields-object.js58
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/copy-properties-not-undefined.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/duplicate-calendar-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/infinity-throws-rangeerror.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/month-and-monthcode-must-agree.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/multiple-unrecognized-properties-ignored.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/options-empty.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/options-invalid.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/options-undefined.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/options-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/order-of-operations.js110
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/overflow-invalid-string.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/overflow-undefined.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/overflow-wrong-type.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/proto-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/read-time-fields-before-datefromfields.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/string-throws.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/subclassing-ignored.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/timezone-throws.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/argument-string.js57
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/basic.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/calendar-case-insensitive.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/calendar-number.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/calendar-string-leap-second.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/calendar-string.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/calendar-temporal-object.js64
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/calendar-wrong-type.js64
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/missing-argument.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/subclassing-ignored.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-builtin-calendar-no-array-iteration.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-calendar-datefromfields-called-with-null-prototype-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-constructor-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-duplicate-calendar-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-leap-second.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-object-insufficient-data.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-object.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-plaindate-calendar-noniso.js56
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-plaindate-calendar-same-id.js79
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-plaindate-calendar-same-object.js60
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-plaindate-calendar.js56
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-plaindate.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-plaindatetime.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-propertybag-calendar-case-insensitive.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-propertybag-calendar-leap-second.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-propertybag-calendar-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-propertybag-calendar-string.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-propertybag-calendar-wrong-type.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-propertybag-calendar-year-zero.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-proto-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-calendar-annotation.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-critical-unknown-annotation.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-date-with-utc-offset.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-invalid.js64
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-iso-calendar.js54
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-multiple-calendar.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-multiple-time-zone.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-time-separators.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-time-zone-annotation.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-unknown-annotation.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-with-utc-designator.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-wrong-type.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-zoneddatetime-convert.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-zoneddatetime-slots.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/calendar-datefromfields-called-with-options-undefined.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/calendar-fields-iterable.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/calendar-temporal-object.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/infinity-throws-rangeerror.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/non-compatible-calendars-throw.js56
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/subclassing-ignored.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/year-zero.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-object-insufficient-data.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-calendar-annotation.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-critical-unknown-annotation.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-date-with-utc-offset.js53
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-multiple-calendar.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-multiple-time-zone.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-no-implicit-midnight.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-time-designator-required-for-disambiguation.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-time-separators.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-time-zone-annotation.js51
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-unknown-annotation.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-with-time-designator.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-with-utc-designator.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-without-time-designator.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-time.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-wrong-type.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-zoneddatetime-balance-negative-time-units.js47
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-zoneddatetime-negative-epochnanoseconds.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/leap-second.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/no-argument-default-to-midnight.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/plaintime-propertybag-no-time-units.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/subclassing-ignored.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/time-undefined.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/year-zero.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/year/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/year/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/year/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/year/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/year/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/year/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/year/validate-calendar-value.js54
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/yearOfWeek/basic.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/yearOfWeek/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/yearOfWeek/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/yearOfWeek/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/yearOfWeek/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/yearOfWeek/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/yearOfWeek/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/yearOfWeek/validate-calendar-value.js54
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/second-undefined.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainDateTime/subclass.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/basic.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/builtin.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/calendar-always.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/calendar-case-insensitive.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/calendar-invalid.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/calendar-number.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/calendar-string.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/calendar-temporal-object.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/calendar-undefined.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/calendar-wrong-type.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/constructor.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-builtin-calendar-no-array-iteration.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-number.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-plainmonthday.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-propertybag-calendar-case-insensitive.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-propertybag-calendar-leap-second.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-propertybag-calendar-number.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-propertybag-calendar-string.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-propertybag-calendar-wrong-type.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-propertybag-calendar-year-zero.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-string-calendar-annotation.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-string-critical-unknown-annotation.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-string-date-with-utc-offset.js63
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-string-multiple-calendar.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-string-multiple-time-zone.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-string-time-separators.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-string-time-zone-annotation.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-string-unknown-annotation.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-string-with-utc-designator.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-wrong-type.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/builtin.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/calendar-datefromfields-called-with-null-prototype-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/calendar-fields-iterable.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/calendar-monthdayfromfields-validates-fields.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/calendar-temporal-object.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/constructor-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/duplicate-calendar-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/fields-leap-day.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/fields-missing-properties.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/fields-object.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/fields-plainmonthday.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/fields-string.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/infinity-throws-rangeerror.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/leap-second.js44
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/not-a-constructor.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/observable-get-overflow-argument-primitive.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/observable-get-overflow-argument-string-invalid.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/options-invalid.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/options-object.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/options-undefined.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/options-wrong-type.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/order-of-operations.js83
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/overflow-invalid-string.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/overflow-undefined.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/overflow-wrong-type.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/overflow.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/proto-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/subclassing-ignored.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/year-zero.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/infinity-throws-rangeerror.js44
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/missing-arguments.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/negative-infinity-throws-rangeerror.js44
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/calendarId/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/calendarId/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/calendarId/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/calendarId/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/calendarId/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/constructor.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/day/basic.js14
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/day/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/day/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/day/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/day/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/day/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/day/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/day/validate-calendar-value.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-builtin-calendar-no-array-iteration.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-propertybag-calendar-case-insensitive.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-propertybag-calendar-leap-second.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-propertybag-calendar-number.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-propertybag-calendar-string.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-propertybag-calendar-wrong-type.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-propertybag-calendar-year-zero.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-string-calendar-annotation.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-string-critical-unknown-annotation.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-string-date-with-utc-offset.js64
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-string-invalid.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-string-multiple-calendar.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-string-multiple-time-zone.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-string-time-separators.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-string-time-zone-annotation.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-string-unknown-annotation.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-string-with-utc-designator.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-string.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-wrong-type.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/basic.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/calendar-datefromfields-called-with-null-prototype-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/calendar-fields-iterable.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/calendar-monthdayfromfields-called-with-options-undefined.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/calendar-temporal-object.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/calendars.js55
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/constructor-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/duplicate-calendar-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/infinity-throws-rangeerror.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/leap-second.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/proto-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/year-zero.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getCalendar/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getCalendar/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getCalendar/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getCalendar/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getCalendar/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getCalendar/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getCalendar/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getCalendar/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/custom.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/field-names.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/field-prop-desc.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/field-traversal-order.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/prototype.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/month/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/month/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/month/unsupported.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/monthCode/basic.js14
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/monthCode/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/monthCode/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/monthCode/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/monthCode/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/monthCode/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/monthCode/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/monthCode/validate-calendar-value.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/prop-desc.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toJSON/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toJSON/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toJSON/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toJSON/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toJSON/calendarname.js54
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toJSON/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toJSON/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toJSON/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toJSON/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toJSON/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toJSON/year-format.js58
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toLocaleString/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toLocaleString/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toLocaleString/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toLocaleString/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toLocaleString/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toLocaleString/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toLocaleString/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toLocaleString/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toLocaleString/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/argument-not-object.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/basic.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/builtin-calendar-no-array-iteration.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/builtin-calendar-no-observable-calls.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/calendar-fields-iterable.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/calendar-fromfields-called-with-null-prototype-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/calendar-merge-fields-returns-primitive.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/calendar-mergefields-called-with-null-prototype-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/constructor-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/copies-merge-fields-object.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/default-overflow-behaviour.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/duplicate-calendar-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/infinity-throws-rangeerror.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/limits.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/proto-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/calendar-tostring.js56
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/calendarname-always.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/calendarname-auto.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/calendarname-critical.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/calendarname-invalid-string.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/calendarname-never.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/calendarname-undefined.js56
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/calendarname-wrong-type.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/options-object.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/options-undefined.js51
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/options-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/order-of-operations.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/year-format.js58
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toStringTag/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toStringTag/prop-desc.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toStringTag/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/valueOf/basic.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/valueOf/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/valueOf/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/valueOf/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/valueOf/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/valueOf/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/valueOf/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/valueOf/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/valueOf/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/basic.js44
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/builtin-calendar-no-array-iteration.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/builtin-calendar-no-observable-calls.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/calendar-arguments.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/calendar-fields-iterable.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/calendar-fromfields-called-with-null-prototype-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/calendar-merge-fields-returns-primitive.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/calendar-mergefields-called-with-null-prototype-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/constructor-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/copies-merge-fields-object.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/copy-properties-not-undefined.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/duplicate-calendar-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/infinity-throws-rangeerror.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/options-invalid.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/options-object.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/options-undefined.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/options-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/order-of-operations.js76
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/overflow-invalid-string.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/overflow-undefined.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/overflow-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/proto-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/subclassing-ignored.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/refisoyear-out-of-range.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/refisoyear-undefined.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainMonthDay/subclass.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/basic.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/builtin.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-cast.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-number.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-calendar-annotation.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-critical-unknown-annotation.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-date-with-utc-offset.js55
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-multiple-calendar.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-multiple-time-zone.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-no-implicit-midnight.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-time-designator-required-for-disambiguation.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-time-separators.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-time-zone-annotation.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-unknown-annotation.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-with-time-designator.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-with-utc-designator.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-wrong-type.js47
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-zoneddatetime-negative-epochnanoseconds.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/compare/basic.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/compare/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/compare/builtin.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/compare/exhaustive.js119
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/compare/leap-second.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/compare/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/compare/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/compare/not-a-constructor.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/compare/plaintime-propertybag-no-time-units.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/compare/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/compare/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/compare/use-internal-slots.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/compare/year-zero.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/constructor.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-number.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-object-leap-second.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-object.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-plaindatetime.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-plaintime.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-calendar-annotation.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-critical-unknown-annotation.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-date-with-utc-offset.js51
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-leap-second.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-multiple-calendar.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-multiple-time-zone.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-no-implicit-midnight.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-time-designator-required-for-disambiguation.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-time-separators.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-time-zone-annotation.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-trailing-junk.js14
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-unknown-annotation.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-with-time-designator.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-with-utc-designator.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string.js60
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-wrong-type.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-zoneddatetime-balance-negative-time-units.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-zoneddatetime-negative-epochnanoseconds.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/builtin.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/infinity-throws-rangeerror.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/leap-second.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/not-a-constructor.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/observable-get-overflow-argument-string-invalid.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/options-invalid.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/options-object.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/options-undefined.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/options-wrong-type.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/order-of-operations.js55
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/overflow-constrain.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/overflow-invalid-string.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/overflow-reject.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/overflow-undefined.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/overflow-wrong-type.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/plaintime-propertybag-no-time-units.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/subclassing-ignored.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/from/year-zero.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/hour-undefined.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/infinity-throws-rangeerror.js60
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/microsecond-undefined.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/millisecond-undefined.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/minute-undefined.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/nanosecond-undefined.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/negative-infinity-throws-rangeerror.js60
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/negative-zero.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-duration-max.js58
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-duration-out-of-range.js75
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-duration-precision-exact-numerical-values.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-duration.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-higher-units.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-invalid-property.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-mixed-sign.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-not-object.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-object.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-singular-properties.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-string-duration-too-large.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-string-fractional-units-rounding-mode.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-string-negative-fractional-units.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-string.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/balance-negative-time-units.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/infinity-throws-rangeerror.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/negative-infinity-throws-rangeerror.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/non-integer-throws-rangeerror.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/options-ignored.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/order-of-operations.js62
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/precision-exact-mathematical-values-1.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/precision-exact-mathematical-values-2.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/subclassing-ignored.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/constructor.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-cast.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-calendar-annotation.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-critical-unknown-annotation.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-date-with-utc-offset.js52
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-multiple-calendar.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-multiple-time-zone.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-no-implicit-midnight.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-time-designator-required-for-disambiguation.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-time-separators.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-time-zone-annotation.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-unknown-annotation.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-with-time-designator.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-with-utc-designator.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-wrong-type.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-zoneddatetime-balance-negative-time-units.js44
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-zoneddatetime-negative-epochnanoseconds.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/basic.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/leap-second.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/plaintime-propertybag-no-time-units.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/year-zero.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/field-names.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/field-prop-desc.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/field-traversal-order.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/prototype.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/hour/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/hour/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/hour/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/hour/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/microsecond/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/microsecond/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/microsecond/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/microsecond/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/millisecond/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/millisecond/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/millisecond/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/millisecond/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/minute/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/minute/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/minute/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/minute/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/nanosecond/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/nanosecond/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/nanosecond/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/nanosecond/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/prop-desc.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/options-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/rounding-cross-midnight.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-hours.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-invalid.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-microseconds.js60
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-milliseconds.js60
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-minutes.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-nan.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-nanoseconds.js60
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-non-integer.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-out-of-range.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-seconds.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-undefined.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-ceil.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-expand.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-floor.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-halfCeil.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-halfEven.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-halfExpand.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-halfFloor.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-halfTrunc.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-invalid-string.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-trunc.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-undefined.js56
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-wrong-type.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundto-invalid-string.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/smallestunit-invalid-string.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/smallestunit-missing.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/smallestunit-plurals-accepted.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/smallestunit-string-shorthand.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/smallestunit-wrong-type.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/subclassing-ignored.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/second/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/second/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/second/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/second/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/shell.js0
-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
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-duration-max.js58
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-duration-out-of-range.js75
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-duration-precision-exact-numerical-values.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-duration.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-higher-units.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-invalid-property.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-mixed-sign.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-not-object.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-object.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-singular-properties.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-string-duration-too-large.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-string-fractional-units-rounding-mode.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-string-negative-fractional-units.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-string.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/balance-negative-time-units.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/infinity-throws-rangeerror.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/negative-infinity-throws-rangeerror.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/non-integer-throws-rangeerror.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/options-ignored.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/order-of-operations.js62
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/precision-exact-mathematical-values-1.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/precision-exact-mathematical-values-2.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/subclassing-ignored.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/basic.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/return-string.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-builtin-calendar-no-array-iteration.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-calendar-datefromfields-called-with-null-prototype-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-constructor-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-duplicate-calendar-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-leap-second.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-plaindatetime.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-propertybag-calendar-case-insensitive.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-propertybag-calendar-leap-second.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-propertybag-calendar-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-propertybag-calendar-string.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-propertybag-calendar-wrong-type.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-propertybag-calendar-year-zero.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-proto-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-calendar-annotation.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-critical-unknown-annotation.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-date-with-utc-offset.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-invalid.js64
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-multiple-calendar.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-multiple-time-zone.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-time-separators.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-time-zone-annotation.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-unknown-annotation.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-with-utc-designator.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-wrong-type.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-zoneddatetime-convert.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-zoneddatetime-slots.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/basic.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/calendar-datefromfields-called-with-options-undefined.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/calendar-fields-iterable.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/calendar-temporal-object.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/infinity-throws-rangeerror.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/limits.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/year-zero.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/basic.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-auto.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-invalid-string.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-nan.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-non-integer.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-number.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-out-of-range.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-undefined.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-wrong-type.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/options-invalid.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/options-object.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/options-undefined.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/options-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/order-of-operations.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/rounding-cross-midnight.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-ceil.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-expand.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-floor.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-halfCeil.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-halfEven.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-halfExpand.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-halfFloor.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-halfTrunc.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-invalid-string.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-trunc.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-undefined.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-wrong-type.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/smallestunit-fractionalseconddigits.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/smallestunit-invalid-string.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/smallestunit-plurals-accepted.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/smallestunit-undefined.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/smallestunit-valid-units.js58
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/smallestunit-wrong-type.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toStringTag/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toStringTag/prop-desc.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toStringTag/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-builtin-calendar-no-array-iteration.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-calendar-datefromfields-called-with-null-prototype-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-constructor-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-duplicate-calendar-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-leap-second.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-plaindatetime.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-primitive.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-propertybag-calendar-case-insensitive.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-propertybag-calendar-leap-second.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-propertybag-calendar-number.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-propertybag-calendar-string.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-propertybag-calendar-wrong-type.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-propertybag-calendar-year-zero.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-propertybag-missing-properties.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-proto-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-calendar-annotation.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-critical-unknown-annotation.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-date-with-utc-offset.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-invalid.js64
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-multiple-calendar.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-multiple-time-zone.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-time-separators.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-time-zone-annotation.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-unknown-annotation.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-with-utc-designator.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-wrong-type.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-zoneddatetime-convert.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-zoneddatetime-slots.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/basic.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/calendar-datefromfields-called-with-options-undefined.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/calendar-fields-iterable.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/calendar-temporal-object.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/getpossibleinstantsfor-called-with-iso8601-calendar.js58
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/infinity-throws-rangeerror.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/order-of-operations.js118
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/plaindate-infinity-throws-rangeerror.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-case-insensitive.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-non-integer.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-not-callable.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-out-of-range.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-wrong-type.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-getpossibleinstantsfor-iterable.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-string-datetime.js65
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-string-leap-second.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-string-multiple-offsets.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-string-year-zero.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-string.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-wrong-type.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/year-zero.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-cast.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-calendar-annotation.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-critical-unknown-annotation.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-date-with-utc-offset.js53
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-multiple-calendar.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-multiple-time-zone.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-no-implicit-midnight.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-time-designator-required-for-disambiguation.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-time-separators.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-time-zone-annotation.js51
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-unknown-annotation.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-with-time-designator.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-with-utc-designator.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-wrong-type.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-zoneddatetime-balance-negative-time-units.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-zoneddatetime-negative-epochnanoseconds.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/balance-negative-time-units.js51
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/basic.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/largestunit-invalid-string.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/largestunit-plurals-accepted.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/largestunit-smallestunit-mismatch.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/largestunit-undefined.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/largestunit-wrong-type.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/largestunit.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/leap-second.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/options-invalid.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/options-object.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/options-undefined.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/options-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/order-of-operations.js95
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/plaintime-propertybag-no-time-units.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/result-sub-second.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/round-cross-unit-boundary.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-hours.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-invalid.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-microseconds.js61
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-milliseconds.js61
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-minutes.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-nan.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-nanoseconds.js61
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-non-integer.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-out-of-range.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-seconds.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-undefined.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-ceil.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-expand.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-floor.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-halfCeil.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-halfEven.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-halfExpand.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-halfFloor.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-halfTrunc.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-invalid-string.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-trunc.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-undefined.js93
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-wrong-type.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/smallestunit-invalid-string.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/smallestunit-plurals-accepted.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/smallestunit-undefined.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/smallestunit-wrong-type.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/year-zero.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/basic.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/argument-not-object.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/basic.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/copy-properties-not-undefined.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/infinity-throws-rangeerror.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/options-invalid.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/options-object.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/options-undefined.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/options-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/order-of-operations.js59
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/overflow-invalid-string.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/overflow-undefined.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/overflow-wrong-type.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/plaintimelike-invalid.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/subclassing-ignored.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/second-undefined.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/subclass.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/basic.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/builtin.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-always.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-case-insensitive.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-invalid.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-number.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-string.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-temporal-object.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-undefined.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-wrong-type.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-builtin-calendar-no-array-iteration.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-cast.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-number.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-propertybag-calendar-case-insensitive.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-propertybag-calendar-leap-second.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-propertybag-calendar-number.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-propertybag-calendar-string.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-propertybag-calendar-wrong-type.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-propertybag-calendar-year-zero.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-calendar-annotation.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-critical-unknown-annotation.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-date-with-utc-offset.js58
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-invalid.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-multiple-calendar.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-multiple-time-zone.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-time-separators.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-time-zone-annotation.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-unknown-annotation.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-with-utc-designator.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-wrong-type.js47
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/basic.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/builtin.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/calendar-datefromfields-called-with-null-prototype-fields.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/calendar-fields-iterable.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/calendar-temporal-object.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/calendar-yearmonthfromfields-called-with-options-undefined.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/compare-calendar.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/compare-reference-day.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/constructor-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/duplicate-calendar-fields.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/exhaustive.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/infinity-throws-rangeerror.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/leap-second.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/not-a-constructor.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/proto-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/use-internal-slots.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/year-zero.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/constructor.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-builtin-calendar-no-array-iteration.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-number.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-object.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-plaindate.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-plainyearmonth.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-propertybag-calendar-case-insensitive.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-propertybag-calendar-leap-second.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-propertybag-calendar-number.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-propertybag-calendar-string.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-propertybag-calendar-wrong-type.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-propertybag-calendar-year-zero.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-calendar-annotation.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-critical-unknown-annotation.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-date-with-utc-offset.js54
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-invalid.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-multiple-calendar.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-multiple-time-zone.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-time-separators.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-time-zone-annotation.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-trailing-junk.js13
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-unknown-annotation.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-with-utc-designator.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-wrong-type.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/builtin.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/calendar-datefromfields-called-with-null-prototype-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/calendar-fields-iterable.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/calendar-temporal-object.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/constructor-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/duplicate-calendar-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/infinity-throws-rangeerror.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/leap-second.js44
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/limits.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/not-a-constructor.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/observable-get-overflow-argument-primitive.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/observable-get-overflow-argument-string-invalid.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/options-invalid.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/options-object.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/options-undefined.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/options-wrong-type.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/order-of-operations.js80
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/overflow-constrain.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/overflow-invalid-string.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/overflow-reject.js14
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/overflow-undefined.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/overflow-wrong-type.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/proto-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/subclassing-ignored.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/year-zero.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/infinity-throws-rangeerror.js44
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/limits.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/missing-arguments.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/negative-infinity-throws-rangeerror.js44
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-duration-max.js58
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-duration-object.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-duration-out-of-range.js75
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-invalid-property.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-lower-units.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-mixed-sign.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-not-object.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-object.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-singular-properties.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-string-negative-fractional-units.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-string.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/builtin-calendar-no-array-iteration.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-arguments-extra-options.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-arguments.js56
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-dateadd-called-with-plaindate-instance.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-dateadd.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-datefromfields-called.js164
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-fields-iterable.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-fromfields-called-with-null-prototype-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-yearmonthfromfields-called-with-null-prototype-options.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/constructor-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/custom-daysInMonth-irrelevant.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/duplicate-calendar-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/end-of-month-out-of-range.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/infinity-throws-rangeerror.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/limits.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/month-length.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/negative-infinity-throws-rangeerror.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/non-integer-throws-rangeerror.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/options-invalid.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/options-object.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/options-undefined.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/options-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/order-of-operations.js183
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/overflow-invalid-string.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/overflow-undefined.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/overflow-wrong-type.js51
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/proto-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/shell.js353
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/subclassing-ignored.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/calendarId/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/calendarId/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/calendarId/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/calendarId/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/calendarId/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/constructor.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/basic.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/validate-calendar-value.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/basic.js14
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/validate-calendar-value.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-builtin-calendar-no-array-iteration.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-cast.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-propertybag-calendar-case-insensitive.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-propertybag-calendar-leap-second.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-propertybag-calendar-number.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-propertybag-calendar-string.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-propertybag-calendar-wrong-type.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-propertybag-calendar-year-zero.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-calendar-annotation.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-critical-unknown-annotation.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-date-with-utc-offset.js55
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-invalid.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-multiple-calendar.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-multiple-time-zone.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-time-separators.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-time-zone-annotation.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-unknown-annotation.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-with-utc-designator.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-wrong-type.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/basic.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/calendar-datefromfields-called-with-null-prototype-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/calendar-fields-iterable.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/calendar-temporal-object.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/calendar-yearmonthfromfields-called-with-options-undefined.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/compare-calendar.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/compare-reference-day.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/constructor-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/duplicate-calendar-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/infinity-throws-rangeerror.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/leap-second.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/proto-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/use-internal-slots.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/year-zero.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getCalendar/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getCalendar/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getCalendar/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getCalendar/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getCalendar/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getCalendar/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getCalendar/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getCalendar/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/custom.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/field-names.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/field-prop-desc.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/field-traversal-order.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/prototype.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/inLeapYear/basic.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/inLeapYear/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/inLeapYear/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/inLeapYear/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/inLeapYear/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/inLeapYear/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/inLeapYear/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/inLeapYear/validate-calendar-value.js56
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/month/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/month/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/month/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/month/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/month/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/month/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/month/validate-calendar-value.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthCode/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthCode/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthCode/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthCode/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthCode/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthCode/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthCode/validate-calendar-value.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/basic.js14
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/validate-calendar-value.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/prop-desc.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-builtin-calendar-no-array-iteration.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-casting.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-propertybag-calendar-case-insensitive.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-propertybag-calendar-leap-second.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-propertybag-calendar-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-propertybag-calendar-string.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-propertybag-calendar-wrong-type.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-propertybag-calendar-year-zero.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-calendar-annotation.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-critical-unknown-annotation.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-date-with-utc-offset.js56
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-invalid.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-multiple-calendar.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-multiple-time-zone.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-time-separators.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-time-zone-annotation.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-unknown-annotation.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-with-utc-designator.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-wrong-type.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/arguments-missing-throws.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/builtin-calendar-no-array-iteration.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-dateadd-called-with-plaindate-instance.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-datefromfields-called-with-null-prototype-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-datefromfields-called-with-options-undefined.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-dateuntil-called-with-null-prototype-options.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-dateuntil-called-with-singular-largestunit.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-fields-iterable.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-fromfields-called-with-null-prototype-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-temporal-object.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-yearmonthfromfields-called-with-options-undefined.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/constructor-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/duplicate-calendar-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/infinity-throws-rangeerror.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-auto.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-disallowed-units.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-invalid-string.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-months.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-plurals-accepted.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-smallestunit-mismatch.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-undefined.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-wrong-type.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-years.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/leap-second.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/mixed-calendar-invalid.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/options-invalid.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/options-object.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/options-undefined.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/options-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/order-of-operations.js192
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/proto-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/round-cross-unit-boundary.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/rounding-zero-year-month-length.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingincrement-as-expected.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingincrement-nan.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingincrement-non-integer.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingincrement-out-of-range.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingincrement-undefined.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingincrement-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-ceil.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-expand.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-floor.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-halfCeil.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-halfEven.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-halfExpand.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-halfFloor.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-halfTrunc.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-invalid-string.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-trunc.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-undefined.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-wrong-type.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/smallestunit-invalid-string.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/smallestunit-plurals-accepted.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/smallestunit-undefined.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/smallestunit-wrong-type.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/symmetry.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/year-zero.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-duration-max.js58
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-duration-object.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-duration-out-of-range.js75
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-invalid-property.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-lower-units.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-mixed-sign.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-not-object.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-object.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-singular-properties.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-string-negative-fractional-units.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-string.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/builtin-calendar-no-array-iteration.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-arguments-extra-options.js53
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-arguments.js58
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-dateadd-called-with-plaindate-instance.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-dateadd.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-datefromfields-called.js164
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-fields-iterable.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-fromfields-called-with-null-prototype-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-yearmonthfromfields-called-with-null-prototype-options.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/constructor-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/custom-daysInMonth-irrelevant.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/duplicate-calendar-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/end-of-month-out-of-range.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/infinity-throws-rangeerror.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/limits.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/month-length.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/negative-infinity-throws-rangeerror.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/non-integer-throws-rangeerror.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/options-invalid.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/options-object.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/options-undefined.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/options-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/order-of-operations.js190
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/overflow-invalid-string.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/overflow-undefined.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/overflow-wrong-type.js51
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/proto-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/shell.js353
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/subclassing-ignored.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/year-format.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/argument-not-object.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/basic.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/builtin-calendar-no-array-iteration.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/builtin-calendar-no-observable-calls.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/calendar-fields-iterable.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/calendar-fromfields-called-with-null-prototype-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/calendar-merge-fields-returns-primitive.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/calendar-mergefields-called-with-null-prototype-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/constructor-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/copies-merge-fields-object.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/default-overflow-behaviour.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/duplicate-calendar-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/infinity-throws-rangeerror.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/limits.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/proto-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/calendar-tostring.js56
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-always.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-auto.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-critical.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-invalid-string.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-never.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-undefined.js56
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-wrong-type.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/options-object.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/options-undefined.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/options-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/order-of-operations.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/year-format.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toStringTag/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toStringTag/prop-desc.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toStringTag/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-builtin-calendar-no-array-iteration.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-casting.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-propertybag-calendar-case-insensitive.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-propertybag-calendar-leap-second.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-propertybag-calendar-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-propertybag-calendar-string.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-propertybag-calendar-wrong-type.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-propertybag-calendar-year-zero.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-calendar-annotation.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-critical-unknown-annotation.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-date-with-utc-offset.js56
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-invalid.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-multiple-calendar.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-multiple-time-zone.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-time-separators.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-time-zone-annotation.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-unknown-annotation.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-with-utc-designator.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-wrong-type.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/arguments-missing-throws.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/builtin-calendar-no-array-iteration.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-dateadd-called-with-plaindate-instance.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-datefromfields-called-with-null-prototype-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-datefromfields-called-with-options-undefined.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-dateuntil-called-with-null-prototype-options.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-dateuntil-called-with-singular-largestunit.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-fields-iterable.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-fromfields-called-with-null-prototype-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-temporal-object.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-yearmonthfromfields-called-with-options-undefined.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/constructor-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/duplicate-calendar-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/infinity-throws-rangeerror.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-auto.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-disallowed-units.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-invalid-string.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-months.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-plurals-accepted.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-smallestunit-mismatch.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-undefined.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-wrong-type.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-years.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/leap-second.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/mixed-calendar-invalid.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/options-invalid.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/options-object.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/options-undefined.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/options-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/order-of-operations.js193
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/proto-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/round-cross-unit-boundary.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/rounding-zero-year-month-length.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingincrement-as-expected.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingincrement-nan.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingincrement-non-integer.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingincrement-out-of-range.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingincrement-undefined.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingincrement-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-ceil.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-expand.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-floor.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-halfCeil.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-halfEven.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-halfExpand.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-halfFloor.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-halfTrunc.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-invalid-string.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-trunc.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-undefined.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-wrong-type.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/smallestunit-invalid-string.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/smallestunit-plurals-accepted.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/smallestunit-undefined.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/smallestunit-wrong-type.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/year-zero.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/basic.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/argument-calendar-field.js14
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/argument-missing-fields.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/argument-timezone-field.js14
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/basic.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/builtin-calendar-no-array-iteration.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/builtin-calendar-no-observable-calls.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/calendar-arguments.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/calendar-fields-iterable.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/calendar-fromfields-called-with-null-prototype-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/calendar-merge-fields-returns-primitive.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/calendar-mergefields-called-with-null-prototype-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/constructor-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/copies-merge-fields-object.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/copy-properties-not-undefined.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/duplicate-calendar-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/infinity-throws-rangeerror.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/options-object.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/options-undefined.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/options-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/order-of-operations.js74
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/overflow-invalid-string.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/overflow-undefined.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/overflow-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/proto-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/subclassing-ignored.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/year/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/year/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/year/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/year/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/year/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/year/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/year/validate-calendar-value.js54
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/refisoday-undefined.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/subclass.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/argument-wrong-type.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/basic.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/builtin.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/constructor.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/from/argument-object.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/from/argument-primitive.js52
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/from/argument-valid.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/from/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/from/builtin.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/from/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/from/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/from/not-a-constructor.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/from/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/from/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/from/subclassing-ignored.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/from/timezone-case-insensitive.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/from/timezone-string-datetime.js63
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/from/timezone-string-leap-second.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/from/timezone-string-multiple-offsets.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/from/timezone-string-year-zero.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/from/timezone-string.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/from/timezone-wrong-type.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/missing-arguments.js14
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/constructor.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/equals/argument-object.js106
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/equals/argument-primitive.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/equals/argument-valid.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/equals/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/equals/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/equals/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/equals/id-wrong-type.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/equals/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/equals/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/equals/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/equals/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/equals/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/equals/timezone-case-insensitive.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/equals/timezone-string-datetime.js64
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/equals/timezone-string-multiple-offsets.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/equals/timezone-wrong-type.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-builtin-calendar-no-array-iteration.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-not-datetime.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-plaindate.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-propertybag-calendar-case-insensitive.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-propertybag-calendar-leap-second.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-propertybag-calendar-number.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-propertybag-calendar-string.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-propertybag-calendar-wrong-type.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-propertybag-calendar-year-zero.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-string-calendar-annotation.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-string-critical-unknown-annotation.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-string-date-with-utc-offset.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-string-multiple-calendar.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-string-multiple-time-zone.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-string-time-separators.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-string-time-zone-annotation.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-string-unknown-annotation.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-string-with-utc-designator.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-wrong-type.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-zoneddatetime-balance-negative-time-units.js47
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-zoneddatetime-negative-epochnanoseconds.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/balance-negative-time-units.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/calendar-datefromfields-called-with-null-prototype-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/calendar-fields-iterable.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/calendar-temporal-object.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/constructor-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/disambiguation-invalid-string.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/disambiguation-undefined.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/disambiguation-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/duplicate-calendar-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/getpossibleinstantsfor-called-with-iso8601-calendar.js59
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/infinity-throws-rangeerror.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/leap-second.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/options-object.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/options-undefined.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/options-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/order-of-operations.js109
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/proto-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/read-time-fields-before-datefromfields.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/year-zero.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/argument-string-calendar-annotation.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/argument-string-critical-unknown-annotation.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/argument-string-date-with-utc-offset.js52
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/argument-string-invalid.js69
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/argument-string-multiple-calendar.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/argument-string-multiple-time-zone.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/argument-string-time-separators.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/argument-string-time-zone-annotation.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/argument-string-unknown-annotation.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/argument-wrong-type.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/argument-zoneddatetime.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/instant-string-limits.js47
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/instant-string.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/leap-second.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/year-zero.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/argument-string-calendar-annotation.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/argument-string-critical-unknown-annotation.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/argument-string-date-with-utc-offset.js52
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/argument-string-invalid.js69
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/argument-string-multiple-calendar.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/argument-string-multiple-time-zone.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/argument-string-time-separators.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/argument-string-time-zone-annotation.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/argument-string-unknown-annotation.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/argument-wrong-type.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/argument-zoneddatetime.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/instant-string-limits.js47
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/instant-string.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/leap-second.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/year-zero.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/argument-not-absolute-getOffsetNanosecondsFor-override.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/argument-string-calendar-annotation.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/argument-string-critical-unknown-annotation.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/argument-string-date-with-utc-offset.js52
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/argument-string-invalid.js69
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/argument-string-multiple-calendar.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/argument-string-multiple-time-zone.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/argument-string-time-separators.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/argument-string-time-zone-annotation.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/argument-string-unknown-annotation.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/argument-wrong-type.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/argument-zoneddatetime.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/basic.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/instant-string-limits.js47
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/instant-string.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/leap-second.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/timezone-getoffsetnanosecondsfor-non-integer.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/timezone-getoffsetnanosecondsfor-not-callable.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/timezone-getoffsetnanosecondsfor-out-of-range.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/timezone-getoffsetnanosecondsfor-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/year-zero.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-negative-epochnanoseconds.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-not-absolute-getOffsetNanosecondsFor-override.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-object-tostring.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-string-calendar-annotation.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-string-critical-unknown-annotation.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-string-date-with-utc-offset.js53
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-string-invalid.js69
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-string-multiple-calendar.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-string-multiple-time-zone.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-string-time-separators.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-string-time-zone-annotation.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-string-unknown-annotation.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-wrong-type.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-zoneddatetime.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/balance-negative-time-units.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/calendar-case-insensitive.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/calendar-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/calendar-string-leap-second.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/calendar-string.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/calendar-temporal-object.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/calendar-undefined.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/calendar-wrong-type.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/custom-timezone.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/instant-string-limits.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/instant-string-multiple-offsets.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/instant-string-sub-minute-offset.js68
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/instant-string.js47
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/leap-second.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/limits.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/pre-epoch.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/timezone-getoffsetnanosecondsfor-non-integer.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/timezone-getoffsetnanosecondsfor-not-callable.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/timezone-getoffsetnanosecondsfor-out-of-range.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/timezone-getoffsetnanosecondsfor-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/year-zero.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-builtin-calendar-no-array-iteration.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-not-datetime.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-plaindate.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-propertybag-calendar-case-insensitive.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-propertybag-calendar-leap-second.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-propertybag-calendar-number.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-propertybag-calendar-string.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-propertybag-calendar-wrong-type.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-propertybag-calendar-year-zero.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-string-calendar-annotation.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-string-critical-unknown-annotation.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-string-date-with-utc-offset.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-string-multiple-calendar.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-string-multiple-time-zone.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-string-time-separators.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-string-time-zone-annotation.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-string-unknown-annotation.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-string-with-utc-designator.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-wrong-type.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-zoneddatetime-balance-negative-time-units.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-zoneddatetime-negative-epochnanoseconds.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/calendar-datefromfields-called-with-null-prototype-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/calendar-fields-iterable.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/calendar-temporal-object.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/constructor-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/duplicate-calendar-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/fixed-offset-near-date-time-limits.js57
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/infinity-throws-rangeerror.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/leap-second.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/proto-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/read-time-fields-before-datefromfields.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/year-zero.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/argument-string-calendar-annotation.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/argument-string-critical-unknown-annotation.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/argument-string-date-with-utc-offset.js52
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/argument-string-invalid.js69
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/argument-string-multiple-calendar.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/argument-string-multiple-time-zone.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/argument-string-time-separators.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/argument-string-time-zone-annotation.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/argument-string-unknown-annotation.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/argument-wrong-type.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/argument-zoneddatetime.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/instant-string-limits.js47
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/instant-string.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/leap-second.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/year-zero.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/id/branding.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/id/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/id/custom-timezone.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/id/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/id/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/prop-desc.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toJSON/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toJSON/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toJSON/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toJSON/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toJSON/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toJSON/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toJSON/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toJSON/returns-identifier-slot.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toJSON/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toString/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toString/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toString/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toString/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toString/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toString/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toString/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toString/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toStringTag/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toStringTag/prop-desc.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toStringTag/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/TimeZone/subclass.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/builtin.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/calendar-case-insensitive.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/calendar-number.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/calendar-string.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/calendar-temporal-object.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/calendar-undefined.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/calendar-wrong-type.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-builtin-calendar-no-array-iteration.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-ambiguous-wall-clock-time.js138
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-calendar-case-insensitive.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-calendar-leap-second.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-calendar-number.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-calendar-string.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-calendar-wrong-type.js51
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-calendar-year-zero.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-getpossibleinstantsfor-called-with-iso8601-calendar.js61
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-invalid-offset-string.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-offset-not-agreeing-with-timezone.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-getoffsetnanosecondsfor-not-callable.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-string-datetime.js64
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-string-leap-second.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-string-multiple-offsets.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-string-year-zero.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-string.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-wrong-type.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-string-calendar-annotation.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-string-critical-unknown-annotation.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-string-date-with-utc-offset.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-string-multiple-calendar.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-string-multiple-time-zone.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-string-time-separators.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-string-time-zone-annotation.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-string-unknown-annotation.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-wrong-type.js51
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/builtin.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/calendar-datefromfields-called-with-null-prototype-fields.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/calendar-fields-iterable.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/calendar-temporal-object.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/constructor-in-calendar-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/duplicate-calendar-fields.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/exhaustive.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/infinity-throws-rangeerror.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/leap-second.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/not-a-constructor.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/order-of-operations.js192
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/proto-in-calendar-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/read-time-fields-before-datefromfields.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/timezone-getpossibleinstantsfor-iterable.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/year-zero.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/zoneddatetime-string-multiple-offsets.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/zoneddatetime-string.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/constructor.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-builtin-calendar-no-array-iteration.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-ambiguous-wall-clock-time.js138
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-calendar-case-insensitive.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-calendar-leap-second.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-calendar-number.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-calendar-string.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-calendar-wrong-type.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-calendar-year-zero.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-getpossibleinstantsfor-called-with-iso8601-calendar.js58
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-invalid-offset-string.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-offset-not-agreeing-with-timezone.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-getoffsetnanosecondsfor-not-callable.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-string-datetime.js63
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-string-leap-second.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-string-multiple-offsets.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-string-year-zero.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-string.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-wrong-type.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-string-calendar-annotation.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-string-critical-unknown-annotation.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-string-date-with-utc-offset.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-string-multiple-calendar.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-string-multiple-time-zone.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-string-time-separators.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-string-time-zone-annotation.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-string-unknown-annotation.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-wrong-type.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-zoneddatetime.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/balance-negative-time-units.js91
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/builtin.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/calendar-datefromfields-called-with-null-prototype-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/calendar-fields-iterable.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/calendar-temporal-object.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/constructor-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/disambiguation-invalid-string.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/disambiguation-undefined.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/disambiguation-wrong-type.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/duplicate-calendar-fields.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/infinity-throws-rangeerror.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/leap-second.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/not-a-constructor.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/observable-get-overflow-argument-primitive.js52
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/observable-get-overflow-argument-string-invalid.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/offset-invalid-string.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/offset-overrides-critical-flag.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/offset-undefined.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/offset-wrong-type.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/options-object.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/options-undefined.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/options-wrong-type.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/order-of-operations.js132
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/overflow-invalid-string.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/overflow-undefined.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/overflow-wrong-type.js66
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/proto-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/read-time-fields-before-datefromfields.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/subclassing-ignored.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/timezone-case-insensitive.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/timezone-getpossibleinstantsfor-iterable.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/year-zero.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/zoneddatetime-string-multiple-offsets.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/zoneddatetime-string.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/missing-arguments.js13
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/argument-duration-max.js57
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/argument-duration-out-of-range.js75
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/argument-invalid-property.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/argument-mixed-sign.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/argument-not-object.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/argument-singular-properties.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/argument-string-fractional-units-rounding-mode.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/argument-string-negative-fractional-units.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/builtin-timezone-no-observable-calls.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/calendar-dateadd.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/negative-epochnanoseconds.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/non-integer-throws-rangeerror.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/options-object.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/options-undefined.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/options-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/order-of-operations.js84
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/overflow-invalid-string.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/overflow-undefined.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/overflow-wrong-type.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/subclassing-ignored.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/timezone-getoffsetnanosecondsfor-non-integer.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/timezone-getoffsetnanosecondsfor-not-callable.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/timezone-getoffsetnanosecondsfor-out-of-range.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/timezone-getoffsetnanosecondsfor-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/calendarId/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/calendarId/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/calendarId/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/calendarId/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/calendarId/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/constructor.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/balance-negative-time-units.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/builtin-timezone-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/timezone-getoffsetnanosecondsfor-non-integer.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/timezone-getoffsetnanosecondsfor-not-callable.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/timezone-getoffsetnanosecondsfor-out-of-range.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/timezone-getoffsetnanosecondsfor-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/validate-calendar-value.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/builtin-timezone-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/timezone-getoffsetnanosecondsfor-non-integer.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/timezone-getoffsetnanosecondsfor-not-callable.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/timezone-getoffsetnanosecondsfor-out-of-range.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/timezone-getoffsetnanosecondsfor-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/validate-calendar-value.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/builtin-timezone-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/timezone-getoffsetnanosecondsfor-non-integer.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/timezone-getoffsetnanosecondsfor-not-callable.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/timezone-getoffsetnanosecondsfor-out-of-range.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/timezone-getoffsetnanosecondsfor-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/validate-calendar-value.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/builtin-timezone-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/timezone-getoffsetnanosecondsfor-non-integer.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/timezone-getoffsetnanosecondsfor-not-callable.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/timezone-getoffsetnanosecondsfor-out-of-range.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/timezone-getoffsetnanosecondsfor-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/validate-calendar-value.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/builtin-timezone-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/timezone-getoffsetnanosecondsfor-non-integer.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/timezone-getoffsetnanosecondsfor-not-callable.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/timezone-getoffsetnanosecondsfor-out-of-range.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/timezone-getoffsetnanosecondsfor-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/validate-calendar-value.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/basic.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/builtin-timezone-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/timezone-getoffsetnanosecondsfor-non-integer.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/timezone-getoffsetnanosecondsfor-not-callable.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/timezone-getoffsetnanosecondsfor-out-of-range.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/timezone-getoffsetnanosecondsfor-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/validate-calendar-value.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMicroseconds/basic.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMicroseconds/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMicroseconds/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMicroseconds/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMicroseconds/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMilliseconds/basic.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMilliseconds/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMilliseconds/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMilliseconds/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMilliseconds/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochNanoseconds/basic.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochNanoseconds/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochNanoseconds/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochNanoseconds/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochNanoseconds/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochSeconds/basic.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochSeconds/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochSeconds/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochSeconds/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochSeconds/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-builtin-calendar-no-array-iteration.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-ambiguous-wall-clock-time.js93
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-calendar-case-insensitive.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-calendar-leap-second.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-calendar-number.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-calendar-string.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-calendar-wrong-type.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-calendar-year-zero.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-getpossibleinstantsfor-called-with-iso8601-calendar.js59
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-invalid-offset-string.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-offset-not-agreeing-with-timezone.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-case-insensitive.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-getoffsetnanosecondsfor-not-callable.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-id-wrong-type.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-normalize-offset-strings.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-string-datetime.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-string-leap-second.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-string-multiple-offsets.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-string-year-zero.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-string.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-wrong-type.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-string-calendar-annotation.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-string-critical-unknown-annotation.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-string-date-with-utc-offset.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-string-multiple-calendar.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-string-multiple-time-zone.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-string-time-separators.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-string-time-zone-annotation.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-string-unknown-annotation.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-wrong-type.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/builtin-timezone-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/calendar-datefromfields-called-with-null-prototype-fields.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/calendar-fields-iterable.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/calendar-temporal-object.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/constructor-in-calendar-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/duplicate-calendar-fields.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/infinity-throws-rangeerror.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/leap-second.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/order-of-operations.js121
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/proto-in-calendar-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/read-time-fields-before-datefromfields.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/timezone-getpossibleinstantsfor-iterable.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/year-zero.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/zoneddatetime-string-multiple-offsets.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/zoneddatetime-string.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getCalendar/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getCalendar/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getCalendar/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getCalendar/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getCalendar/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getCalendar/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getCalendar/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getCalendar/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/balance-negative-time-units.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/builtin-timezone-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/custom.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/field-names.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/field-prop-desc.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/field-traversal-order.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/negative-epochnanoseconds.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/offset.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/order-of-operations.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/prototype.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/shell.js353
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/timezone-getoffsetnanosecondsfor-non-integer.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/timezone-getoffsetnanosecondsfor-not-callable.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/timezone-getoffsetnanosecondsfor-out-of-range.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/timezone-getoffsetnanosecondsfor-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getTimeZone/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getTimeZone/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getTimeZone/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getTimeZone/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getTimeZone/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getTimeZone/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getTimeZone/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getTimeZone/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/balance-negative-time-units.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/builtin-timezone-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/timezone-getoffsetnanosecondsfor-non-integer.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/timezone-getoffsetnanosecondsfor-not-callable.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/timezone-getoffsetnanosecondsfor-out-of-range.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/timezone-getoffsetnanosecondsfor-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/getpossibleinstantsfor-called-with-iso8601-calendar.js58
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/order-of-operations.js108
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/precision-exact-mathematical-values-2.js142
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/precision-exact-mathematical-values.js133
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/timezone-getoffsetnanosecondsfor-non-integer.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/timezone-getoffsetnanosecondsfor-not-callable.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/timezone-getoffsetnanosecondsfor-out-of-range.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/timezone-getoffsetnanosecondsfor-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/timezone-getpossibleinstantsfor-iterable.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/basic.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/builtin-timezone-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/timezone-getoffsetnanosecondsfor-non-integer.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/timezone-getoffsetnanosecondsfor-not-callable.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/timezone-getoffsetnanosecondsfor-out-of-range.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/timezone-getoffsetnanosecondsfor-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/validate-calendar-value.js56
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/balance-negative-time-units.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/builtin-timezone-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/negative-epochnanoseconds.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/timezone-getoffsetnanosecondsfor-non-integer.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/timezone-getoffsetnanosecondsfor-not-callable.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/timezone-getoffsetnanosecondsfor-out-of-range.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/timezone-getoffsetnanosecondsfor-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/balance-negative-time-units.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/builtin-timezone-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/negative-epochnanoseconds.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/timezone-getoffsetnanosecondsfor-non-integer.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/timezone-getoffsetnanosecondsfor-not-callable.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/timezone-getoffsetnanosecondsfor-out-of-range.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/timezone-getoffsetnanosecondsfor-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/balance-negative-time-units.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/builtin-timezone-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/timezone-getoffsetnanosecondsfor-non-integer.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/timezone-getoffsetnanosecondsfor-not-callable.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/timezone-getoffsetnanosecondsfor-out-of-range.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/timezone-getoffsetnanosecondsfor-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/builtin-timezone-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/timezone-getoffsetnanosecondsfor-non-integer.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/timezone-getoffsetnanosecondsfor-not-callable.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/timezone-getoffsetnanosecondsfor-out-of-range.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/timezone-getoffsetnanosecondsfor-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/validate-calendar-value.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/builtin-timezone-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/timezone-getoffsetnanosecondsfor-non-integer.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/timezone-getoffsetnanosecondsfor-not-callable.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/timezone-getoffsetnanosecondsfor-out-of-range.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/timezone-getoffsetnanosecondsfor-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/validate-calendar-value.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/builtin-timezone-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/timezone-getoffsetnanosecondsfor-non-integer.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/timezone-getoffsetnanosecondsfor-not-callable.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/timezone-getoffsetnanosecondsfor-out-of-range.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/timezone-getoffsetnanosecondsfor-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/validate-calendar-value.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/builtin-timezone-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/negative-epochnanoseconds.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/timezone-getoffsetnanosecondsfor-non-integer.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/timezone-getoffsetnanosecondsfor-not-callable.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/timezone-getoffsetnanosecondsfor-out-of-range.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/timezone-getoffsetnanosecondsfor-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/basic.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/builtin-timezone-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/timezone-getoffsetnanosecondsfor-non-integer.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/timezone-getoffsetnanosecondsfor-not-callable.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/timezone-getoffsetnanosecondsfor-out-of-range.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/timezone-getoffsetnanosecondsfor-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/builtin-timezone-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/timezone-getoffsetnanosecondsfor-non-integer.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/timezone-getoffsetnanosecondsfor-not-callable.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/timezone-getoffsetnanosecondsfor-out-of-range.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/timezone-getoffsetnanosecondsfor-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/prop-desc.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/builtin-timezone-no-observable-calls.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/div-zero.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/getpossibleinstantsfor-called-with-iso8601-calendar.js58
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/negative-epochnanoseconds.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/options-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/order-of-operations.js158
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/rounding-direction.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/rounding-is-noop.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingincrement-nan.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingincrement-non-integer.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingincrement-out-of-range.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingincrement-undefined.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingincrement-wrong-type.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-ceil.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-expand.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-floor.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-halfCeil.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-halfEven.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-halfExpand.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-halfFloor.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-halfTrunc.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-invalid-string.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-trunc.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-undefined.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-wrong-type.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundto-invalid-string.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/smallest-unit-day-daylength-too-large.js87
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/smallest-unit-day-daylength-zero-or-negative.js69
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/smallest-unit-day-rounding-modes.js97
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/smallestunit-invalid-string.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/smallestunit-plurals-accepted.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/smallestunit-string-shorthand.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/smallestunit-wrong-type.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/subclassing-ignored.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/timezone-getoffsetnanosecondsfor-non-integer.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/timezone-getoffsetnanosecondsfor-not-callable.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/timezone-getoffsetnanosecondsfor-out-of-range.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/timezone-getoffsetnanosecondsfor-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/timezone-getpossibleinstantsfor-iterable.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/balance-negative-time-units.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/builtin-timezone-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/timezone-getoffsetnanosecondsfor-non-integer.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/timezone-getoffsetnanosecondsfor-not-callable.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/timezone-getoffsetnanosecondsfor-out-of-range.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/timezone-getoffsetnanosecondsfor-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-builtin-calendar-no-array-iteration.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-ambiguous-wall-clock-time.js93
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-calendar-case-insensitive.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-calendar-leap-second.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-calendar-number.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-calendar-string.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-calendar-wrong-type.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-calendar-year-zero.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-getpossibleinstantsfor-called-with-iso8601-calendar.js59
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-invalid-offset-string.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-offset-not-agreeing-with-timezone.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-getoffsetnanosecondsfor-not-callable.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-id-wrong-type.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-string-datetime.js69
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-string-leap-second.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-string-multiple-offsets.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-string-year-zero.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-string.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-wrong-type.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-string-calendar-annotation.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-string-critical-unknown-annotation.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-string-date-with-utc-offset.js47
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-string-multiple-calendar.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-string-multiple-time-zone.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-string-time-separators.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-string-time-zone-annotation.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-string-unknown-annotation.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-wrong-type.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/balance-negative-time-units.js57
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/builtin-timezone-no-observable-calls.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/calendar-dateadd-called-with-options-undefined.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/calendar-datefromfields-called-with-null-prototype-fields.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/calendar-dateuntil-called-with-copy-of-options.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/calendar-dateuntil-called-with-null-prototype-options.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/calendar-dateuntil-called-with-singular-largestunit.js117
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/calendar-fields-iterable.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/calendar-temporal-object.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/constructor-in-calendar-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/date-and-time-durations-opposite-signs.js47
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/duplicate-calendar-fields.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/infinity-throws-rangeerror.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/largestunit-invalid-string.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/largestunit-plurals-accepted.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/largestunit-smallestunit-mismatch.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/largestunit-undefined.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/largestunit-wrong-type.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/largestunit.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/leap-second.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/negative-epochnanoseconds.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/normalized-time-duration-to-days-loop-arbitrarily.js78
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/normalized-time-duration-to-days-range-errors.js126
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/options-object.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/options-undefined.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/options-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/order-of-operations.js408
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/proto-in-calendar-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/read-time-fields-before-datefromfields.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/round-cross-unit-boundary.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/rounding-zero-year-month-week-length.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingincrement-nan.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingincrement-non-integer.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingincrement-out-of-range.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingincrement-undefined.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingincrement-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-ceil.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-expand.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-floor.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-halfCeil.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-halfEven.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-halfExpand.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-halfFloor.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-halfTrunc.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-invalid-string.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-trunc.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-undefined.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-wrong-type.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/smallestunit-invalid-string.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/smallestunit-plurals-accepted.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/smallestunit-undefined.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/smallestunit-wrong-type.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/timezone-getoffsetnanosecondsfor-non-integer.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/timezone-getoffsetnanosecondsfor-not-callable.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/timezone-getoffsetnanosecondsfor-out-of-range.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/timezone-getoffsetnanosecondsfor-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/timezone-getpossibleinstantsfor-iterable.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/year-zero.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/zoneddatetime-string-multiple-offsets.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/zoneddatetime-string.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/builtin-timezone-no-observable-calls.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/getpossibleinstantsfor-called-with-iso8601-calendar.js58
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/order-of-operations.js69
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/subclassing-ignored.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/timezone-getoffsetnanosecondsfor-non-integer.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/timezone-getoffsetnanosecondsfor-not-callable.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/timezone-getoffsetnanosecondsfor-out-of-range.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/timezone-getoffsetnanosecondsfor-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/argument-duration-max.js57
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/argument-duration-out-of-range.js75
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/argument-invalid-property.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/argument-mixed-sign.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/argument-not-object.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/argument-singular-properties.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/argument-string-fractional-units-rounding-mode.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/argument-string-negative-fractional-units.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/builtin-timezone-no-observable-calls.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/calendar-dateadd.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/negative-epochnanoseconds.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/non-integer-throws-rangeerror.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/options-object.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/options-undefined.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/options-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/order-of-operations.js84
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/overflow-invalid-string.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/overflow-undefined.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/overflow-wrong-type.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/subclassing-ignored.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/timezone-getoffsetnanosecondsfor-non-integer.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/timezone-getoffsetnanosecondsfor-not-callable.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/timezone-getoffsetnanosecondsfor-out-of-range.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/timezone-getoffsetnanosecondsfor-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/timeZoneId/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/timeZoneId/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/timeZoneId/builtin-timezone-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/timeZoneId/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/timeZoneId/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/timeZoneId/timezone-id-wrong-type.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toInstant/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toInstant/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toInstant/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toInstant/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toInstant/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toInstant/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toInstant/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toInstant/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/balance-negative-time-units.js44
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/basic.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/builtin-timezone-no-observable-calls.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/negative-epochnanoseconds.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/offset.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/order-of-operations.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/timezone-getoffsetnanosecondsfor-non-integer.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/timezone-getoffsetnanosecondsfor-not-callable.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/timezone-getoffsetnanosecondsfor-out-of-range.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/timezone-getoffsetnanosecondsfor-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/timezone-id-wrong-type.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/year-format.js58
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/builtin-timezone-no-observable-calls.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/return-string.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/timezone-id-wrong-type.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/builtin-timezone-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/timezone-getoffsetnanosecondsfor-non-integer.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/timezone-getoffsetnanosecondsfor-not-callable.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/timezone-getoffsetnanosecondsfor-out-of-range.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/timezone-getoffsetnanosecondsfor-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/balance-negative-time-units.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/builtin-timezone-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/negative-epochnanoseconds.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/plain-custom-timezone.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/timezone-getoffsetnanosecondsfor-non-integer.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/timezone-getoffsetnanosecondsfor-not-callable.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/timezone-getoffsetnanosecondsfor-out-of-range.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/timezone-getoffsetnanosecondsfor-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/builtin-calendar-no-array-iteration.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/builtin-calendar-no-observable-calls.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/builtin-timezone-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/calendar-arguments.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/calendar-fields-iterable.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/calendar-fromfields-called-with-null-prototype-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/calendar-monthdayfromfields-called-with-options-undefined.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/calendar-result.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/constructor-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/duplicate-calendar-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/order-of-operations.js51
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/proto-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/timezone-getoffsetnanosecondsfor-non-integer.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/timezone-getoffsetnanosecondsfor-not-callable.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/timezone-getoffsetnanosecondsfor-out-of-range.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/timezone-getoffsetnanosecondsfor-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/balance-negative-time-units.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/builtin-timezone-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/negative-epochnanoseconds.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/timezone-getoffsetnanosecondsfor-non-integer.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/timezone-getoffsetnanosecondsfor-not-callable.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/timezone-getoffsetnanosecondsfor-out-of-range.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/timezone-getoffsetnanosecondsfor-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/builtin-calendar-no-array-iteration.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/builtin-calendar-no-observable-calls.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/builtin-timezone-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/calendar-arguments.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/calendar-fields-iterable.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/calendar-fromfields-called-with-null-prototype-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/calendar-result.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/calendar-yearmonthfromfields-called-with-options-undefined.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/constructor-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/duplicate-calendar-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/order-of-operations.js51
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/proto-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/timezone-getoffsetnanosecondsfor-non-integer.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/timezone-getoffsetnanosecondsfor-not-callable.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/timezone-getoffsetnanosecondsfor-out-of-range.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/timezone-getoffsetnanosecondsfor-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/balance-negative-time-units.js44
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/builtin-timezone-no-observable-calls.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/calendar-tostring.js56
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/calendarname-always.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/calendarname-auto.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/calendarname-critical.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/calendarname-invalid-string.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/calendarname-never.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/calendarname-undefined.js56
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/calendarname-wrong-type.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-auto.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-invalid-string.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-nan.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-non-integer.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-number.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-out-of-range.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-undefined.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-wrong-type.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/negative-epochnanoseconds.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/offset-invalid-string.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/offset-undefined.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/offset-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/offset.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/options-object.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/options-undefined.js51
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/options-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/order-of-operations.js93
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/rounding-cross-midnight.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/rounding-direction.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-ceil.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-expand.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-floor.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-halfCeil.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-halfEven.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-halfExpand.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-halfFloor.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-halfTrunc.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-invalid-string.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-trunc.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-undefined.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-wrong-type.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/smallestunit-fractionalseconddigits.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/smallestunit-invalid-string.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/smallestunit-plurals-accepted.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/smallestunit-undefined.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/smallestunit-valid-units.js58
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/smallestunit-wrong-type.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezone-getoffsetnanosecondsfor-non-integer.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezone-getoffsetnanosecondsfor-not-callable.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezone-getoffsetnanosecondsfor-out-of-range.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezone-getoffsetnanosecondsfor-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezone-id-wrong-type.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezonename-auto.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezonename-critical.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezonename-invalid-string.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezonename-never.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezonename-undefined.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezonename-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/year-format.js58
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toStringTag/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toStringTag/prop-desc.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toStringTag/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-builtin-calendar-no-array-iteration.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-ambiguous-wall-clock-time.js93
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-calendar-case-insensitive.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-calendar-leap-second.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-calendar-number.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-calendar-string.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-calendar-wrong-type.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-calendar-year-zero.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-getpossibleinstantsfor-called-with-iso8601-calendar.js59
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-invalid-offset-string.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-offset-not-agreeing-with-timezone.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-getoffsetnanosecondsfor-not-callable.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-id-wrong-type.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-string-datetime.js69
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-string-leap-second.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-string-multiple-offsets.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-string-year-zero.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-string.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-wrong-type.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-string-calendar-annotation.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-string-critical-unknown-annotation.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-string-date-with-utc-offset.js47
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-string-multiple-calendar.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-string-multiple-time-zone.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-string-time-separators.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-string-time-zone-annotation.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-string-unknown-annotation.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-wrong-type.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/balance-negative-time-units.js57
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/builtin-timezone-no-observable-calls.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/calendar-dateadd-called-with-options-undefined.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/calendar-datefromfields-called-with-null-prototype-fields.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/calendar-dateuntil-called-with-copy-of-options.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/calendar-dateuntil-called-with-null-prototype-options.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/calendar-dateuntil-called-with-singular-largestunit.js117
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/calendar-fields-iterable.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/calendar-temporal-object.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/constructor-in-calendar-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/date-and-time-durations-opposite-signs.js47
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/duplicate-calendar-fields.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/infinity-throws-rangeerror.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/largestunit-invalid-string.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/largestunit-plurals-accepted.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/largestunit-smallestunit-mismatch.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/largestunit-undefined.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/largestunit-wrong-type.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/leap-second.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/negative-epochnanoseconds.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/normalized-time-duration-to-days-loop-arbitrarily.js78
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/normalized-time-duration-to-days-range-errors.js126
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/options-object.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/options-undefined.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/options-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/order-of-operations.js408
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/proto-in-calendar-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/read-time-fields-before-datefromfields.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/round-cross-unit-boundary.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/rounding-zero-year-month-week-length.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingincrement-nan.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingincrement-non-integer.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingincrement-out-of-range.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingincrement-undefined.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingincrement-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-ceil.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-expand.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-floor.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-halfCeil.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-halfEven.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-halfExpand.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-halfFloor.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-halfTrunc.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-invalid-string.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-trunc.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-undefined.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-wrong-type.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/smallestunit-invalid-string.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/smallestunit-plurals-accepted.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/smallestunit-undefined.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/smallestunit-wrong-type.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/timezone-getoffsetnanosecondsfor-non-integer.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/timezone-getoffsetnanosecondsfor-not-callable.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/timezone-getoffsetnanosecondsfor-out-of-range.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/timezone-getoffsetnanosecondsfor-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/timezone-getpossibleinstantsfor-iterable.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/year-zero.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/zoneddatetime-string-multiple-offsets.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/zoneddatetime-string.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/valueOf/basic.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/valueOf/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/valueOf/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/valueOf/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/valueOf/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/valueOf/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/valueOf/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/valueOf/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/valueOf/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/builtin-timezone-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/timezone-getoffsetnanosecondsfor-non-integer.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/timezone-getoffsetnanosecondsfor-not-callable.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/timezone-getoffsetnanosecondsfor-out-of-range.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/timezone-getoffsetnanosecondsfor-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/validate-calendar-value.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/balance-negative-time-units.js65
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/builtin-calendar-no-array-iteration.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/builtin-calendar-no-observable-calls.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/builtin-timezone-no-observable-calls.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/calendar-fields-iterable.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/calendar-fromfields-called-with-null-prototype-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/calendar-merge-fields-returns-primitive.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/calendar-mergefields-called-with-null-prototype-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/calendar-options.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/constructor-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/copies-merge-fields-object.js61
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/copy-properties-not-undefined.js52
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/disambiguation-invalid-string.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/disambiguation-undefined.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/disambiguation-wrong-type.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/duplicate-calendar-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/getpossibleinstantsfor-called-with-iso8601-calendar.js61
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/infinity-throws-rangeerror.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/minimum-instant-with-one-hour-offset.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/offset-invalid-string.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/offset-property-invalid-string.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/offset-undefined.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/offset-wrong-type.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/options-object.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/options-undefined.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/options-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/order-of-operations.js177
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/overflow-invalid-string.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/overflow-undefined.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/overflow-wrong-type.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/proto-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/read-time-fields-before-datefromfields.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/receiver-offset-broken.js59
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/subclassing-ignored.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/timezone-getoffsetnanosecondsfor-non-integer.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/timezone-getoffsetnanosecondsfor-not-callable.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/timezone-getoffsetnanosecondsfor-out-of-range.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/timezone-getoffsetnanosecondsfor-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/timezone-getpossibleinstantsfor-iterable.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/zoned-datetime-like-at-minimum-date-time-with-offset.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/zoned-datetime-like-at-minimum-date-time.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/calendar-case-insensitive.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/calendar-number.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/calendar-string-leap-second.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/calendar-string.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/calendar-temporal-object.js64
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/calendar-wrong-type.js64
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/missing-argument.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/subclassing-ignored.js57
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-builtin-calendar-no-array-iteration.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-calendar-datefromfields-called-with-null-prototype-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-constructor-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-duplicate-calendar-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-leap-second.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-plaindatetime.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-propertybag-calendar-case-insensitive.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-propertybag-calendar-leap-second.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-propertybag-calendar-number.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-propertybag-calendar-string.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-propertybag-calendar-wrong-type.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-propertybag-calendar-year-zero.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-proto-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-calendar-annotation.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-critical-unknown-annotation.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-date-with-utc-offset.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-invalid.js64
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-multiple-calendar.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-multiple-time-zone.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-time-separators.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-time-zone-annotation.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-unknown-annotation.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-with-utc-designator.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-wrong-type.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-zoneddatetime-convert.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-zoneddatetime-slots.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/builtin-timezone-no-observable-calls.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/calendar-datefromfields-called-with-options-undefined.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/calendar-fields-iterable.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/calendar-temporal-object.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/getpossibleinstantsfor-called-with-iso8601-calendar.js58
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/infinity-throws-rangeerror.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/negative-epochnanoseconds.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/order-of-operations.js114
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/subclassing-ignored.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/timezone-getoffsetnanosecondsfor-non-integer.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/timezone-getoffsetnanosecondsfor-not-callable.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/timezone-getoffsetnanosecondsfor-out-of-range.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/timezone-getoffsetnanosecondsfor-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/year-zero.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-calendar-annotation.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-critical-unknown-annotation.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-date-with-utc-offset.js53
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-multiple-calendar.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-multiple-time-zone.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-no-implicit-midnight.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-time-designator-required-for-disambiguation.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-time-separators.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-time-zone-annotation.js51
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-unknown-annotation.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-with-time-designator.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-with-utc-designator.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-wrong-type.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-zoneddatetime-balance-negative-time-units.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-zoneddatetime-negative-epochnanoseconds.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/builtin-timezone-no-observable-calls.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/getpossibleinstantsfor-called-with-iso8601-calendar.js58
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/leap-second.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/order-of-operations.js88
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/plaintime-propertybag-no-time-units.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/subclassing-ignored.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/time-undefined.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/timezone-getoffsetnanosecondsfor-non-integer.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/timezone-getoffsetnanosecondsfor-not-callable.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/timezone-getoffsetnanosecondsfor-out-of-range.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/timezone-getoffsetnanosecondsfor-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/year-zero.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/builtin-timezone-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/subclassing-ignored.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/timezone-case-insensitive.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/timezone-string-datetime.js65
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/timezone-string-leap-second.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/timezone-string-multiple-offsets.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/timezone-string-year-zero.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/timezone-string.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/timezone-wrong-type.js42
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/builtin-timezone-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/timezone-getoffsetnanosecondsfor-non-integer.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/timezone-getoffsetnanosecondsfor-not-callable.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/timezone-getoffsetnanosecondsfor-out-of-range.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/timezone-getoffsetnanosecondsfor-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/validate-calendar-value.js54
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/builtin-timezone-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/timezone-getoffsetnanosecondsfor-non-integer.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/timezone-getoffsetnanosecondsfor-not-callable.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/timezone-getoffsetnanosecondsfor-out-of-range.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/timezone-getoffsetnanosecondsfor-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/validate-calendar-value.js54
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/subclass.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/timezone-case-insensitive.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/timezone-string-datetime.js63
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/timezone-string-leap-second.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/timezone-string-multiple-offsets.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/timezone-string.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/ZonedDateTime/timezone-wrong-type.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/getOwnPropertyNames.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/keys.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/prop-desc.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/shell.js2158
-rw-r--r--js/src/tests/test262/built-ins/Temporal/toStringTag/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/toStringTag/prop-desc.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/toStringTag/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/toStringTag/string.js13
7092 files changed, 209297 insertions, 0 deletions
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Calendar/argument-wrong-type.js
new file mode 100644
index 0000000000..c78fd3f8cc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/argument-wrong-type.js
@@ -0,0 +1,33 @@
+// |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.calendar
+description: RangeError thrown when constructor invoked with the wrong type
+features: [Temporal]
+---*/
+
+const tests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [19761118, "number that would convert to a valid ISO string in other contexts"],
+ [1n, "bigint"],
+ [Symbol(), "symbol"],
+ [{}, "object not implementing any protocol"],
+ [new Temporal.Calendar("iso8601"), "calendar instance"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.ZonedDateTime.from("2020-01-01T00:00Z[UTC]"), "ZonedDateTime instance"],
+];
+
+for (const [arg, description] of tests) {
+ assert.throws(
+ typeof (arg) === "string" ? RangeError : TypeError,
+ () => new Temporal.Calendar(arg),
+ `${description} is not accepted by this constructor`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/browser.js b/js/src/tests/test262/built-ins/Temporal/Calendar/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/builtin.js b/js/src/tests/test262/built-ins/Temporal/Calendar/builtin.js
new file mode 100644
index 0000000000..1862b77310
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/builtin.js
@@ -0,0 +1,30 @@
+// |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.calendar
+description: Tests that Temporal.Calendar meets the requirements for built-in objects
+info: |
+ 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.Calendar),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Calendar),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Calendar),
+ Function.prototype, "prototype");
+
+assert.sameValue(typeof Temporal.Calendar.prototype,
+ "object", "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/constructor.js b/js/src/tests/test262/built-ins/Temporal/Calendar/constructor.js
new file mode 100644
index 0000000000..9061f4adce
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/constructor.js
@@ -0,0 +1,15 @@
+// |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.calendar
+description: Temporal.Calendar constructor cannot be called as a function
+info: |
+ 1. If NewTarget is undefined, throw a TypeError exception.
+features: [Temporal]
+---*/
+
+assert.throws(TypeError, () => Temporal.Calendar("iso8601"));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/from/browser.js b/js/src/tests/test262/built-ins/Temporal/Calendar/from/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/from/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/from/builtin.js b/js/src/tests/test262/built-ins/Temporal/Calendar/from/builtin.js
new file mode 100644
index 0000000000..7eef95f8f4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/from/builtin.js
@@ -0,0 +1,33 @@
+// |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.calendar.from
+description: Tests that Temporal.Calendar.from meets the requirements for built-in objects
+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.Calendar.from),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Calendar.from),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Calendar.from),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Calendar.from.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/from/calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/Calendar/from/calendar-case-insensitive.js
new file mode 100644
index 0000000000..a99f743a01
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/from/calendar-case-insensitive.js
@@ -0,0 +1,16 @@
+// |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.calendar.from
+description: Calendar names are case-insensitive
+features: [Temporal]
+---*/
+
+const arg = "iSo8601";
+
+const result = Temporal.Calendar.from(arg);
+assert.sameValue(result.id, "iso8601", "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/from/calendar-number.js b/js/src/tests/test262/built-ins/Temporal/Calendar/from/calendar-number.js
new file mode 100644
index 0000000000..4ae7fa5dc7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/from/calendar-number.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.calendar.from
+description: A number is not allowed to be a calendar
+features: [Temporal]
+---*/
+
+const numbers = [
+ 1,
+ -19761118,
+ 19761118,
+ 1234567890,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => Temporal.Calendar.from(arg),
+ "A number is not a valid ISO string for Calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/from/calendar-object.js b/js/src/tests/test262/built-ins/Temporal/Calendar/from/calendar-object.js
new file mode 100644
index 0000000000..fdbe8b5a47
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/from/calendar-object.js
@@ -0,0 +1,38 @@
+// |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.calendar.from
+description: >
+ Converting an object implementing the Calendar protocol to Temporal.Calendar
+ gives the same object
+features: [Temporal]
+---*/
+
+const custom = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "custom-calendar",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+assert.sameValue(Temporal.Calendar.from(custom), custom);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/from/calendar-string-builtin.js b/js/src/tests/test262/built-ins/Temporal/Calendar/from/calendar-string-builtin.js
new file mode 100644
index 0000000000..5fca567d39
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/from/calendar-string-builtin.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.calendar.from
+description: Calendar.from should support iso8601.
+features: [Temporal]
+---*/
+
+const tests = [
+ "iso8601",
+ "1994-11-05T08:15:30-05:00",
+];
+
+for (const item of tests) {
+ const calendar = Temporal.Calendar.from(item);
+ assert(calendar instanceof Temporal.Calendar);
+ assert.sameValue(calendar.id, "iso8601");
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/from/calendar-string-leap-second.js b/js/src/tests/test262/built-ins/Temporal/Calendar/from/calendar-string-leap-second.js
new file mode 100644
index 0000000000..f6d01b0160
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/from/calendar-string-leap-second.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.calendar.from
+description: Leap second is a valid ISO string for Calendar
+features: [Temporal]
+---*/
+
+const arg = "2016-12-31T23:59:60";
+const result = Temporal.Calendar.from(arg);
+assert.sameValue(
+ result.id,
+ "iso8601",
+ "leap second is a valid ISO string for Calendar"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/from/calendar-string-not-builtin.js b/js/src/tests/test262/built-ins/Temporal/Calendar/from/calendar-string-not-builtin.js
new file mode 100644
index 0000000000..bb4a361de3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/from/calendar-string-not-builtin.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.calendar.from
+description: from() throws if the argument is not a built-in calendar name.
+features: [Temporal]
+---*/
+
+const tests = [
+ "local",
+ "iso-8601",
+ "[u-ca=iso8601]",
+ "invalid-calendar",
+];
+
+for (const item of tests) {
+ assert.throws(RangeError, () => Temporal.Calendar.from(item));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/from/calendar-string.js b/js/src/tests/test262/built-ins/Temporal/Calendar/from/calendar-string.js
new file mode 100644
index 0000000000..b51ea04a48
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/from/calendar-string.js
@@ -0,0 +1,16 @@
+// |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.calendar.from
+description: A calendar ID is valid input for Calendar
+features: [Temporal]
+---*/
+
+const arg = "iso8601";
+
+const result = Temporal.Calendar.from(arg);
+assert.sameValue(result.id, "iso8601", `Calendar created from string "${arg}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/from/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/Calendar/from/calendar-temporal-object.js
new file mode 100644
index 0000000000..f1b5cfde92
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/from/calendar-temporal-object.js
@@ -0,0 +1,41 @@
+// |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.calendar.from
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal-totemporalcalendar step 1.b:
+ b. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const plainDate = new Temporal.PlainDate(2000, 5, 2);
+const plainDateTime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const plainMonthDay = new Temporal.PlainMonthDay(5, 2);
+const plainYearMonth = new Temporal.PlainYearMonth(2000, 5);
+const zonedDateTime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+
+[plainDate, plainDateTime, plainMonthDay, plainYearMonth, zonedDateTime].forEach((arg) => {
+ const actual = [];
+ const expected = [];
+
+ const calendar = arg.getISOFields().calendar;
+
+ Object.defineProperty(arg, "calendar", {
+ get() {
+ actual.push("get calendar");
+ return calendar;
+ },
+ });
+
+ const result = Temporal.Calendar.from(arg);
+ assert.sameValue(result.id, calendar, "Temporal object coerced to calendar");
+
+ assert.compareArray(actual, expected, "calendar getter not called");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/from/calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Calendar/from/calendar-wrong-type.js
new file mode 100644
index 0000000000..f28985ddb3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/from/calendar-wrong-type.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.calendar.from
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or object for Calendar
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const primitiveTests = [
+ [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,
+ () => Temporal.Calendar.from(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => Temporal.Calendar.from(arg), `${description} is not a valid object and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/from/length.js b/js/src/tests/test262/built-ins/Temporal/Calendar/from/length.js
new file mode 100644
index 0000000000..21dd13cb33
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/from/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.calendar.from
+description: Temporal.Calendar.from.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.Calendar.from, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/from/name.js b/js/src/tests/test262/built-ins/Temporal/Calendar/from/name.js
new file mode 100644
index 0000000000..4fb450918a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/from/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.calendar.from
+description: Temporal.Calendar.from.name is "from"
+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.Calendar.from, "name", {
+ value: "from",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/from/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Calendar/from/not-a-constructor.js
new file mode 100644
index 0000000000..6aa43c2f7c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/from/not-a-constructor.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.calendar.from
+description: Temporal.Calendar.from 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.Calendar.from();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Calendar.from), false,
+ "isConstructor(Temporal.Calendar.from)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/from/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Calendar/from/prop-desc.js
new file mode 100644
index 0000000000..2c46579d05
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/from/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.calendar.from
+description: The "from" property of Temporal.Calendar
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Calendar.from,
+ "function",
+ "`typeof Calendar.from` is `function`"
+);
+
+verifyProperty(Temporal.Calendar, "from", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/from/shell.js b/js/src/tests/test262/built-ins/Temporal/Calendar/from/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/from/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/Calendar/from/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/Calendar/from/subclassing-ignored.js
new file mode 100644
index 0000000000..a585c93929
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/from/subclassing-ignored.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.calendar.from
+description: The receiver is never called when calling from()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnoredStatic(
+ Temporal.Calendar,
+ "from",
+ ["iso8601"],
+ (result) => {
+ assert.sameValue(result.id, "iso8601", "id property of result");
+ assert.sameValue(result.toString(), "iso8601", "toString() of result");
+ },
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/length.js b/js/src/tests/test262/built-ins/Temporal/Calendar/length.js
new file mode 100644
index 0000000000..d4e68a9ec7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/length.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.calendar
+description: Temporal.Calendar.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.Calendar, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/missing-arguments.js b/js/src/tests/test262/built-ins/Temporal/Calendar/missing-arguments.js
new file mode 100644
index 0000000000..8d445ea24f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/missing-arguments.js
@@ -0,0 +1,14 @@
+// |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.calendar
+description: RangeError thrown when constructor invoked with no argument
+features: [Temporal]
+---*/
+
+assert.throws(TypeError, () => new Temporal.Calendar());
+assert.throws(TypeError, () => new Temporal.Calendar(undefined));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/name.js b/js/src/tests/test262/built-ins/Temporal/Calendar/name.js
new file mode 100644
index 0000000000..0c7faefa8b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/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.calendar
+description: Temporal.Calendar.name is "Calendar"
+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.Calendar, "name", {
+ value: "Calendar",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prop-desc.js
new file mode 100644
index 0000000000..20748688e9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/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.calendar
+description: The "Calendar" property of Temporal
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Calendar,
+ "function",
+ "`typeof Calendar` is `function`"
+);
+
+verifyProperty(Temporal, "Calendar", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/browser.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/constructor.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/constructor.js
new file mode 100644
index 0000000000..9b0bc3dc60
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/constructor.js
@@ -0,0 +1,20 @@
+// |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.calendar.prototype.constructor
+description: Test for Temporal.Calendar.prototype.constructor.
+info: The initial value of Temporal.Calendar.prototype.constructor is %Temporal.Calendar%.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Calendar.prototype, "constructor", {
+ value: Temporal.Calendar,
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/add-days.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/add-days.js
new file mode 100644
index 0000000000..edf4f9b2f4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/add-days.js
@@ -0,0 +1,39 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dateadd
+description: Temporal.Calendar.prototype.dateAdd add duration with days and calculate correctly..
+info: |
+ 8. Let result be ? AddISODate(date.[[ISOYear]], date.[[ISOMonth]], date.[[ISODay]], duration.[[Years]], duration.[[Months]], duration.[[Weeks]], duration.[[Days]], overflow).
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+let cal = new Temporal.Calendar("iso8601");
+
+let p10d = new Temporal.Duration(0,0,0,10);
+
+TemporalHelpers.assertPlainDate(
+ cal.dateAdd("2021-07-16", p10d), 2021, 7, "M07", 26,
+ "add 10 days and result into the same month");
+TemporalHelpers.assertPlainDate(
+ cal.dateAdd("2021-07-26", p10d), 2021, 8, "M08", 5,
+ "add 10 days and result into next month");
+TemporalHelpers.assertPlainDate(
+ cal.dateAdd("2021-12-26", p10d), 2022, 1, "M01", 5,
+ "add 10 days and result into next year");
+TemporalHelpers.assertPlainDate(
+ cal.dateAdd("2020-02-26", p10d), 2020, 3, "M03", 7,
+ "add 10 days from a leap year in Feb and result into March");
+TemporalHelpers.assertPlainDate(
+ cal.dateAdd("2021-02-26", p10d), 2021, 3, "M03", 8,
+ "add 10 days from a non leap year in Feb and result into March");
+TemporalHelpers.assertPlainDate(
+ cal.dateAdd("2020-02-19", p10d), 2020, 2, "M02", 29,
+ "add 10 days from a leap year in Feb 19 and result into Feb 29");
+TemporalHelpers.assertPlainDate(
+ cal.dateAdd("2021-02-19", p10d), 2021, 3, "M03", 1,
+ "add 10 days from a non leap year in Feb 19 and result into March 1");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/add-months-weeks.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/add-months-weeks.js
new file mode 100644
index 0000000000..7f3ac6e052
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/add-months-weeks.js
@@ -0,0 +1,39 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dateadd
+description: Temporal.Calendar.prototype.dateAdd add duration with months and weeks and calculate correctly.
+info: |
+ 8. Let result be ? AddISODate(date.[[ISOYear]], date.[[ISOMonth]], date.[[ISODay]], duration.[[Years]], duration.[[Months]], duration.[[Weeks]], duration.[[Days]], overflow).
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+let cal = new Temporal.Calendar("iso8601");
+
+let p2m3w = new Temporal.Duration(0,2,3);
+
+TemporalHelpers.assertPlainDate(
+ cal.dateAdd("2020-02-29", p2m3w), 2020, 5, "M05", 20,
+ "add two months 3 weeks from Feb 29 of a leap year");
+TemporalHelpers.assertPlainDate(
+ cal.dateAdd("2020-02-28", p2m3w), 2020, 5, "M05", 19,
+ "add two months 3 weeks from Feb 28 of a leap year");
+TemporalHelpers.assertPlainDate(
+ cal.dateAdd("2021-02-28", p2m3w), 2021, 5, "M05", 19,
+ "add two months 3 weeks from Feb 28 of a non leap year");
+TemporalHelpers.assertPlainDate(
+ cal.dateAdd("2020-12-28", p2m3w), 2021, 3, "M03", 21,
+ "add two months 3 weeks from end of year to non leap year");
+TemporalHelpers.assertPlainDate(
+ cal.dateAdd("2019-12-28", p2m3w), 2020, 3, "M03", 20,
+ "add two months 3 weeks from end of year to leap year");
+TemporalHelpers.assertPlainDate(
+ cal.dateAdd("2019-10-28", p2m3w), 2020, 1, "M01", 18,
+ "add two months 3 weeks and cause roll into a new year");
+TemporalHelpers.assertPlainDate(
+ cal.dateAdd("2019-10-31", p2m3w), 2020, 1, "M01", 21,
+ "add two months 3 weeks and cause roll into a new year");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/add-months.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/add-months.js
new file mode 100644
index 0000000000..592b16f2fe
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/add-months.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dateadd
+description: Temporal.Calendar.prototype.dateAdd add duration with months and calculate correctly
+info: |
+ 8. Let result be ? AddISODate(date.[[ISOYear]], date.[[ISOMonth]], date.[[ISODay]], duration.[[Years]], duration.[[Months]], duration.[[Weeks]], duration.[[Days]], overflow).
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+let cal = new Temporal.Calendar("iso8601");
+
+let p5m = new Temporal.Duration(0, 5);
+
+TemporalHelpers.assertPlainDate(
+ cal.dateAdd("2021-07-16", p5m), 2021, 12, "M12", 16,
+ "add five months and result in the same year");
+TemporalHelpers.assertPlainDate(
+ cal.dateAdd("2021-08-16", p5m), 2022, 1, "M01", 16,
+ "add five months and result in the next year");
+TemporalHelpers.assertPlainDate(
+ cal.dateAdd("2021-10-31", p5m), 2022, 3, "M03", 31,
+ "add five months and result in the next year in end of month");
+TemporalHelpers.assertPlainDate(
+ cal.dateAdd("2019-10-01", p5m), 2020, 3, "M03", 1,
+ "add five months and result in the next year in end of month on leap year");
+TemporalHelpers.assertPlainDate(
+ cal.dateAdd("2021-09-30", p5m), 2022, 2, "M02", 28,
+ "add five months and result in the nexdt year and constrain to Feb 28");
+TemporalHelpers.assertPlainDate(
+ cal.dateAdd("2019-09-30", p5m), 2020, 2, "M02", 29,
+ "add five months and result in the nexdt year and constrain to Feb 29 on leap year");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/add-weeks-days.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/add-weeks-days.js
new file mode 100644
index 0000000000..9489dd63ea
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/add-weeks-days.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dateadd
+description: Temporal.Calendar.prototype.dateAdd add duration with weeks and days and calculate correctly.
+info: |
+ 8. Let result be ? AddISODate(date.[[ISOYear]], date.[[ISOMonth]], date.[[ISODay]], duration.[[Years]], duration.[[Months]], duration.[[Weeks]], duration.[[Days]], overflow).
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+let cal = new Temporal.Calendar("iso8601");
+
+let p2w3d = new Temporal.Duration(0,0,2,3);
+TemporalHelpers.assertPlainDate(
+ cal.dateAdd("2020-02-29", p2w3d), 2020, 3, "M03", 17,
+ "add 2 weeks and 3 days (17 days) from Feb 29 in a leap year and cause rolling into March");
+TemporalHelpers.assertPlainDate(
+ cal.dateAdd("2021-02-28", p2w3d), 2021, 3, "M03", 17,
+ "add 2 weeks and 3 days (17 days) from Feb and cause rolling into March in a non leap year");
+TemporalHelpers.assertPlainDate(
+ cal.dateAdd("2020-02-28", p2w3d), 2020, 3, "M03", 16,
+ "add 2 weeks and 3 days (17 days) from Feb and cause rolling into March in a leap year");
+TemporalHelpers.assertPlainDate(
+ cal.dateAdd("2020-12-28", p2w3d), 2021, 1, "M01", 14,
+ "add 2 weeks and 3 days (17 days) and cause rolling into a new year");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/add-weeks.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/add-weeks.js
new file mode 100644
index 0000000000..671bd83bfe
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/add-weeks.js
@@ -0,0 +1,66 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dateadd
+description: Temporal.Calendar.prototype.dateAdd add duration with weeks and calculate correctly.
+info: |
+ 8. Let result be ? AddISODate(date.[[ISOYear]], date.[[ISOMonth]], date.[[ISODay]], duration.[[Years]], duration.[[Months]], duration.[[Weeks]], duration.[[Days]], overflow).
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+let cal = new Temporal.Calendar("iso8601");
+
+let p1w = new Temporal.Duration(0,0,1);
+let p6w = new Temporal.Duration(0,0,6);
+
+TemporalHelpers.assertPlainDate(
+ cal.dateAdd("2021-02-19", p1w), 2021, 2, "M02", 26,
+ "add one week in Feb");
+TemporalHelpers.assertPlainDate(
+ cal.dateAdd("2021-02-27", p1w), 2021, 3, "M03", 6,
+ "add one week in Feb and result in March");
+TemporalHelpers.assertPlainDate(
+ cal.dateAdd("2020-02-27", p1w), 2020, 3, "M03", 5,
+ "add one week in Feb and result in March in a leap year");
+TemporalHelpers.assertPlainDate(
+ cal.dateAdd("2021-12-24", p1w), 2021, 12, "M12", 31,
+ "add one week and result in the last day of a year");
+TemporalHelpers.assertPlainDate(
+ cal.dateAdd("2021-12-25", p1w), 2022, 1, "M01", 1,
+ "add one week and result in the first day of next year");
+
+TemporalHelpers.assertPlainDate(
+ cal.dateAdd("2021-01-27", p1w), 2021, 2, "M02", 3,
+ "add one week and result in next month from a month with 31 days");
+TemporalHelpers.assertPlainDate(
+ cal.dateAdd("2021-06-27", p1w), 2021, 7, "M07", 4,
+ "add one week and result in next month from a month with 30 days");
+TemporalHelpers.assertPlainDate(
+ cal.dateAdd("2021-07-27", p1w), 2021, 8, "M08", 3,
+ "add one week and result in next month from a month with 31 days");
+
+TemporalHelpers.assertPlainDate(
+ cal.dateAdd("2021-02-19", p6w), 2021, 4, "M04", 2,
+ "add six weeks and result in next month from Feb in a non leap year");
+TemporalHelpers.assertPlainDate(
+ cal.dateAdd("2020-02-19", p6w), 2020, 4, "M04", 1,
+ "add six weeks and result in next month from Feb in a leap year");
+TemporalHelpers.assertPlainDate(
+ cal.dateAdd("2021-12-24", p6w), 2022, 2, "M02", 4,
+ "add six weeks and result in the next year");
+TemporalHelpers.assertPlainDate(
+ cal.dateAdd("2021-01-27", p6w), 2021, 3, "M03", 10,
+ "add six weeks and result in the same year");
+TemporalHelpers.assertPlainDate(
+ cal.dateAdd("2020-01-27", p6w), 2020, 3, "M03", 9,
+ "add six weeks and result in the same year");
+TemporalHelpers.assertPlainDate(
+ cal.dateAdd("2021-06-27", p6w), 2021, 8, "M08", 8,
+ "add six weeks and result in the same year crossing month of 30 and 31 days");
+TemporalHelpers.assertPlainDate(
+ cal.dateAdd("2021-07-27", p6w), 2021, 9, "M09", 7,
+ "add six weeks and result in the same year crossing month of 31 and 31 days");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/add-years-months-days.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/add-years-months-days.js
new file mode 100644
index 0000000000..1a7c932a3d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/add-years-months-days.js
@@ -0,0 +1,48 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dateadd
+description: Temporal.Calendar.prototype.dateAdd add duration with years, months and days and calculate correctly.
+info: |
+ 8. Let result be ? AddISODate(date.[[ISOYear]], date.[[ISOMonth]], date.[[ISODay]], duration.[[Years]], duration.[[Months]], duration.[[Weeks]], duration.[[Days]], overflow).
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+let cal = new Temporal.Calendar("iso8601");
+
+let p1y2m4d = new Temporal.Duration(1,2,0,4);
+
+TemporalHelpers.assertPlainDate(
+ cal.dateAdd("2021-07-16", p1y2m4d), 2022, 9, "M09", 20,
+ "add one year two months and 4 days");
+TemporalHelpers.assertPlainDate(
+ cal.dateAdd("2021-02-27", p1y2m4d), 2022, 5, "M05", 1,
+ "add one year two months and 4 days and roll into new month from a month of 30 days");
+TemporalHelpers.assertPlainDate(
+ cal.dateAdd("2021-01-28", p1y2m4d), 2022, 4, "M04", 1,
+ "add one year two months and 4 days and roll into new month from a month of 31 days");
+TemporalHelpers.assertPlainDate(
+ cal.dateAdd("2021-02-26", p1y2m4d), 2022, 4, "M04", 30,
+ "add one year two months and 4 days which roll from March to April in a non leap year");
+TemporalHelpers.assertPlainDate(
+ cal.dateAdd("2023-02-26", p1y2m4d), 2024, 4, "M04", 30,
+ "add one year two months and 4 days which roll from March to April in a leap year");
+TemporalHelpers.assertPlainDate(
+ cal.dateAdd("2021-12-30", p1y2m4d), 2023, 3, "M03", 4,
+ "add one year two months and 4 days which roll month into new year and roll day into March in non leap year");
+TemporalHelpers.assertPlainDate(
+ cal.dateAdd("2022-12-30", p1y2m4d), 2024, 3, "M03", 4,
+ "add one year two months and 4 days which roll month into new year and roll day into March in leap year");
+TemporalHelpers.assertPlainDate(
+ cal.dateAdd("2022-12-29", p1y2m4d), 2024, 3, "M03", 4,
+ "add one year two months and 4 days which roll month into new year and roll day into March in leap year");
+TemporalHelpers.assertPlainDate(
+ cal.dateAdd("2021-07-30", p1y2m4d), 2022, 10, "M10", 4,
+ "add one year two months and 4 days which roll into a new month from a month with 30 days");
+TemporalHelpers.assertPlainDate(
+ cal.dateAdd("2021-06-30", p1y2m4d), 2022, 9, "M09", 3,
+ "add one year two months and 4 days which roll into a new month from a month with 31 days");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/add-years-months.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/add-years-months.js
new file mode 100644
index 0000000000..4a3d984335
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/add-years-months.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dateadd
+description: Temporal.Calendar.prototype.dateAdd add duration with years and months and calculate correctly.
+info: |
+ 8. Let result be ? AddISODate(date.[[ISOYear]], date.[[ISOMonth]], date.[[ISODay]], duration.[[Years]], duration.[[Months]], duration.[[Weeks]], duration.[[Days]], overflow).
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+let cal = new Temporal.Calendar("iso8601");
+
+let p1y2m = new Temporal.Duration(1,2);
+
+TemporalHelpers.assertPlainDate(
+ cal.dateAdd("2021-07-16", p1y2m), 2022, 9, "M09", 16,
+ "add one year and 2 months");
+TemporalHelpers.assertPlainDate(
+ cal.dateAdd("2021-11-30", p1y2m), 2023, 1, "M01", 30,
+ "add one year and 2 months roll into a new year");
+TemporalHelpers.assertPlainDate(
+ cal.dateAdd("2021-12-31", p1y2m), 2023, 2, "M02", 28,
+ "add one year and 2 months roll into a new year and constrain in Feb 28 of a non leap year");
+TemporalHelpers.assertPlainDate(
+ cal.dateAdd("2022-12-31", p1y2m), 2024, 2, "M02", 29,
+ "add one year and 2 months roll into a new year and constrain in Feb 29 of a leap year");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/add-years-weeks.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/add-years-weeks.js
new file mode 100644
index 0000000000..06bae12e5a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/add-years-weeks.js
@@ -0,0 +1,33 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dateadd
+description: Temporal.Calendar.prototype.dateAdd add duration with years and weeks and calculate correctly.
+info: |
+ 8. Let result be ? AddISODate(date.[[ISOYear]], date.[[ISOMonth]], date.[[ISODay]], duration.[[Years]], duration.[[Months]], duration.[[Weeks]], duration.[[Days]], overflow).
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+let cal = new Temporal.Calendar("iso8601");
+
+let p1y2w = new Temporal.Duration(1,0,2);
+
+TemporalHelpers.assertPlainDate(
+ cal.dateAdd("2020-02-28", p1y2w), 2021, 3, "M03", 14,
+ "add 1 year and 2 weeks to Feb 28 and cause roll into March in a non leap year");
+TemporalHelpers.assertPlainDate(
+ cal.dateAdd("2020-02-29", p1y2w), 2021, 3, "M03", 14,
+ "add 1 year and 2 weeks to Feb 29 and cause roll into March in a non leap year");
+TemporalHelpers.assertPlainDate(
+ cal.dateAdd("2019-02-28", p1y2w), 2020, 3, "M03", 13,
+ "add 1 year and 2 weeks to Feb 28 and cause roll into March in a leap year");
+TemporalHelpers.assertPlainDate(
+ cal.dateAdd("2021-02-28", p1y2w), 2022, 3, "M03", 14,
+ "add 1 year and 2 weeks to Feb 28 and cause roll into March in a non leap year");
+TemporalHelpers.assertPlainDate(
+ cal.dateAdd("2020-12-28", p1y2w), 2022, 1, "M01", 11,
+ "add 1 year and 2 weeks and cause roll into a new year");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/add-years.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/add-years.js
new file mode 100644
index 0000000000..c2e2eda6c5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/add-years.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dateadd
+description: Temporal.Calendar.prototype.dateAdd add duration with years only calculate correctly.
+info: |
+ 8. Let result be ? AddISODate(date.[[ISOYear]], date.[[ISOMonth]], date.[[ISODay]], duration.[[Years]], duration.[[Months]], duration.[[Weeks]], duration.[[Days]], overflow).
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+let cal = new Temporal.Calendar("iso8601");
+
+let p1y = new Temporal.Duration(1);
+let p4y = new Temporal.Duration(4);
+
+TemporalHelpers.assertPlainDate(
+ cal.dateAdd("2020-02-29", p1y), 2021, 2, "M02", 28,
+ "add one year on Feb 29");
+TemporalHelpers.assertPlainDate(
+ cal.dateAdd("2020-02-29", p4y), 2024, 2, "M02", 29,
+ "add four years on Feb 29");
+TemporalHelpers.assertPlainDate(
+ cal.dateAdd("2021-07-16", p1y), 2022, 7, "M07", 16,
+ "add one year on other date");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..2d9cb13d16
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-builtin-calendar-no-array-iteration.js
@@ -0,0 +1,24 @@
+// |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.calendar.prototype.dateadd
+description: >
+ Calling the method with a property bag argument with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.Calendar("iso8601");
+const arg = { year: 2000, month: 5, day: 2, calendar: "iso8601" };
+instance.dateAdd(arg, new Temporal.Duration());
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-calendar-datefromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..6a0f1f43b0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-calendar-datefromfields-called-with-null-prototype-fields.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.calendar.prototype.dateadd
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.Calendar("iso8601");
+const arg = { year: 2000, month: 5, day: 2, calendar };
+instance.dateAdd(arg, new Temporal.Duration());
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..897312501c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-constructor-in-calendar-fields.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.calendar.prototype.dateadd
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.Calendar("iso8601");
+
+assert.throws(RangeError, () => instance.dateAdd(arg, new Temporal.Duration()));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-duplicate-calendar-fields.js
new file mode 100644
index 0000000000..1ad3bc8141
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-duplicate-calendar-fields.js
@@ -0,0 +1,19 @@
+// |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.calendar.prototype.dateadd
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['day'], ['month'], ['monthCode'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar };
+ const instance = new Temporal.Calendar("iso8601");
+
+ assert.throws(RangeError, () => instance.dateAdd(arg, new Temporal.Duration()));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-duration-max.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-duration-max.js
new file mode 100644
index 0000000000..b6cc1edb0c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-duration-max.js
@@ -0,0 +1,58 @@
+// |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.calendar.prototype.dateadd
+description: Maximum allowed duration
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const maxCases = [
+ ["P273790Y8M12DT23H59M59.999999999S", "string with max years"],
+ [{ years: 273790, months: 8, days: 12, nanoseconds: 86399999999999 }, "property bag with max years"],
+ ["P3285488M12DT23H59M59.999999999S", "string with max months"],
+ [{ months: 3285488, days: 12, nanoseconds: 86399999999999 }, "property bag with max months"],
+ ["P14285714W2DT23H59M59.999999999S", "string with max weeks"],
+ [{ weeks: 14285714, days: 2, nanoseconds: 86399999999999 }, "property bag with max weeks"],
+ ["P100000000DT23H59M59.999999999S", "string with max days"],
+ [{ days: 100000000, nanoseconds: 86399999999999 }, "property bag with max days"],
+ ["PT2400000023H59M59.999999999S", "string with max hours"],
+ [{ hours: 2400000023, nanoseconds: 3599999999999 }, "property bag with max hours"],
+ ["PT144000001439M59.999999999S", "string with max minutes"],
+ [{ minutes: 144000001439, nanoseconds: 59999999999 }, "property bag with max minutes"],
+ ["PT8640000086399.999999999S", "string with max seconds"],
+ [{ seconds: 8640000086399, nanoseconds: 999999999 }, "property bag with max seconds"],
+];
+
+for (const [arg, descr] of maxCases) {
+ const result = instance.dateAdd(new Temporal.PlainDate(1970, 1, 1), arg);
+ TemporalHelpers.assertPlainDate(result, 275760, 9, "M09", 13, `operation succeeds with ${descr}`);
+}
+
+const minCases = [
+ ["-P273790Y8M12DT23H59M59.999999999S", "string with min years"],
+ [{ years: -273790, months: -8, days: -12, nanoseconds: -86399999999999 }, "property bag with min years"],
+ ["-P3285488M12DT23H59M59.999999999S", "string with min months"],
+ [{ months: -3285488, days: -12, nanoseconds: -86399999999999 }, "property bag with min months"],
+ ["-P14285714W3DT23H59M59.999999999S", "string with min weeks"],
+ [{ weeks: -14285714, days: -3, nanoseconds: -86399999999999 }, "property bag with min weeks"],
+ ["-P100000001DT23H59M59.999999999S", "string with min days"],
+ [{ days: -100000001, nanoseconds: -86399999999999 }, "property bag with min days"],
+ ["-PT2400000047H59M59.999999999S", "string with min hours"],
+ [{ hours: -2400000047, nanoseconds: -3599999999999 }, "property bag with min hours"],
+ ["-PT144000002879M59.999999999S", "string with min minutes"],
+ [{ minutes: -144000002879, nanoseconds: -59999999999 }, "property bag with min minutes"],
+ ["-PT8640000172799.999999999S", "string with min seconds"],
+ [{ seconds: -8640000172799, nanoseconds: -999999999 }, "property bag with min seconds"],
+];
+
+for (const [arg, descr] of minCases) {
+ const result = instance.dateAdd(new Temporal.PlainDate(1970, 1, 1), arg);
+ TemporalHelpers.assertPlainDate(result, -271821, 4, "M04", 19, `operation succeeds with ${descr}`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-duration-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-duration-out-of-range.js
new file mode 100644
index 0000000000..4368522992
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-duration-out-of-range.js
@@ -0,0 +1,75 @@
+// |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.calendar.prototype.dateadd
+description: Duration-like argument that is out of range
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const cases = [
+ // 2^32 = 4294967296
+ ["P4294967296Y", "string with years > max"],
+ [{ years: 4294967296 }, "property bag with years > max"],
+ ["-P4294967296Y", "string with years < min"],
+ [{ years: -4294967296 }, "property bag with years < min"],
+ ["P4294967296M", "string with months > max"],
+ [{ months: 4294967296 }, "property bag with months > max"],
+ ["-P4294967296M", "string with months < min"],
+ [{ months: -4294967296 }, "property bag with months < min"],
+ ["P4294967296W", "string with weeks > max"],
+ [{ weeks: 4294967296 }, "property bag with weeks > max"],
+ ["-P4294967296W", "string with weeks < min"],
+ [{ weeks: -4294967296 }, "property bag with weeks < min"],
+
+ // ceil(max safe integer / 86400) = 104249991375
+ ["P104249991375D", "string with days > max"],
+ [{ days: 104249991375 }, "property bag with days > max"],
+ ["P104249991374DT24H", "string where hours balance into days > max"],
+ [{ days: 104249991374, hours: 24 }, "property bag where hours balance into days > max"],
+ ["-P104249991375D", "string with days < min"],
+ [{ days: -104249991375 }, "property bag with days < min"],
+ ["-P104249991374DT24H", "string where hours balance into days < min"],
+ [{ days: -104249991374, hours: -24 }, "property bag where hours balance into days < min"],
+
+ // ceil(max safe integer / 3600) = 2501999792984
+ ["PT2501999792984H", "string with hours > max"],
+ [{ hours: 2501999792984 }, "property bag with hours > max"],
+ ["PT2501999792983H60M", "string where minutes balance into hours > max"],
+ [{ hours: 2501999792983, minutes: 60 }, "property bag where minutes balance into hours > max"],
+ ["-PT2501999792984H", "string with hours < min"],
+ [{ hours: -2501999792984 }, "property bag with hours < min"],
+ ["-PT2501999792983H60M", "string where minutes balance into hours < min"],
+ [{ hours: -2501999792983, minutes: -60 }, "property bag where minutes balance into hours < min"],
+
+ // ceil(max safe integer / 60) = 150119987579017
+ ["PT150119987579017M", "string with minutes > max"],
+ [{ minutes: 150119987579017 }, "property bag with minutes > max"],
+ ["PT150119987579016M60S", "string where seconds balance into minutes > max"],
+ [{ minutes: 150119987579016, seconds: 60 }, "property bag where seconds balance into minutes > max"],
+ ["-PT150119987579017M", "string with minutes < min"],
+ [{ minutes: -150119987579017 }, "property bag with minutes < min"],
+ ["-PT150119987579016M60S", "string where seconds balance into minutes < min"],
+ [{ minutes: -150119987579016, seconds: -60 }, "property bag where seconds balance into minutes < min"],
+
+ // 2^53 = 9007199254740992
+ ["PT9007199254740992S", "string with seconds > max"],
+ [{ seconds: 9007199254740992 }, "property bag with seconds > max"],
+ [{ seconds: 9007199254740991, milliseconds: 1000 }, "property bag where milliseconds balance into seconds > max"],
+ [{ seconds: 9007199254740991, microseconds: 1000000 }, "property bag where microseconds balance into seconds > max"],
+ [{ seconds: 9007199254740991, nanoseconds: 1000000000 }, "property bag where nanoseconds balance into seconds > max"],
+ ["-PT9007199254740992S", "string with seconds < min"],
+ [{ seconds: -9007199254740992 }, "property bag with seconds < min"],
+ [{ seconds: -9007199254740991, milliseconds: -1000 }, "property bag where milliseconds balance into seconds < min"],
+ [{ seconds: -9007199254740991, microseconds: -1000000 }, "property bag where microseconds balance into seconds < min"],
+ [{ seconds: -9007199254740991, nanoseconds: -1000000000 }, "property bag where nanoseconds balance into seconds < min"],
+];
+
+for (const [arg, descr] of cases) {
+ assert.throws(RangeError, () => instance.dateAdd(new Temporal.PlainDate(1970, 1, 1), arg), `${descr} is out of range`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-duration-years-and-months-number-max-value.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-duration-years-and-months-number-max-value.js
new file mode 100644
index 0000000000..dcbfed8b43
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-duration-years-and-months-number-max-value.js
@@ -0,0 +1,38 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dateadd
+description: >
+ Call BalanceISOYearMonth with 2³² - 1 and -(2³² - 1) for years/months.
+info: |
+ Temporal.Calendar.prototype.dateAdd ( date, duration [ , options ] )
+
+ ...
+ 9. Let result be ? AddISODate(date.[[ISOYear]], date.[[ISOMonth]], date.[[ISODay]],
+ duration.[[Years]], duration.[[Months]], duration.[[Weeks]], balanceResult.[[Days]],
+ overflow).
+ 10. Return ? CreateTemporalDate(result.[[Year]], result.[[Month]], result.[[Day]], calendar).
+
+ AddISODate ( year, month, day, years, months, weeks, days, overflow )
+
+ ...
+ 3. Let intermediate be ! BalanceISOYearMonth(year + years, month + months).
+ ...
+
+features: [Temporal]
+---*/
+
+var cal = new Temporal.Calendar("iso8601");
+var date = new Temporal.PlainDate(1970, 1, 1);
+
+const max = 4294967295; // 2³² - 1
+
+var maxValue = new Temporal.Duration(max, max);
+var minValue = new Temporal.Duration(-max, -max);
+
+assert.throws(RangeError, () => cal.dateAdd(date, maxValue), "years/months is +Number.MAX_VALUE");
+assert.throws(RangeError, () => cal.dateAdd(date, minValue), "years/months is -Number.MAX_VALUE");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-leap-second.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-leap-second.js
new file mode 100644
index 0000000000..edca559552
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-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.calendar.prototype.dateadd
+description: Leap second is a valid ISO string for PlainDate
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+let arg = "2016-12-31T23:59:60";
+const result1 = instance.dateAdd(arg, new Temporal.Duration());
+TemporalHelpers.assertPlainDate(
+ result1,
+ 2016, 12, "M12", 31,
+ "leap second is a valid ISO string for PlainDate"
+);
+
+arg = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+const result2 = instance.dateAdd(arg, new Temporal.Duration());
+TemporalHelpers.assertPlainDate(
+ result2,
+ 2016, 12, "M12", 31,
+ "second: 60 is ignored in property bag for PlainDate"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-number.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-number.js
new file mode 100644
index 0000000000..940952dd15
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/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.calendar.prototype.dateadd
+description: A number cannot be used in place of a Temporal.PlainDate
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const numbers = [
+ 1,
+ 19761118,
+ -19761118,
+ 1234567890,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => instance.dateAdd(arg, new Temporal.Duration()),
+ 'Numbers cannot be used in place of an ISO string for PlainDate'
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-plaindatetime.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-plaindatetime.js
new file mode 100644
index 0000000000..a613cfe71e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-plaindatetime.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.calendar.prototype.dateadd
+description: Fast path for converting Temporal.PlainDateTime to Temporal.PlainDate by reading internal slots
+info: |
+ sec-temporal.calendar.prototype.dateadd step 4:
+ 4. Set _date_ to ? ToTemporalDate(_date_).
+ sec-temporal-totemporaldate step 2.b:
+ b. If _item_ has an [[InitializedTemporalDateTime]] internal slot, then
+ i. Return ! CreateTemporalDate(_item_.[[ISOYear]], _item_.[[ISOMonth]], _item_.[[ISODay]], _item_.[[Calendar]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkPlainDateTimeConversionFastPath((datetime) => {
+ const calendar = new Temporal.Calendar("iso8601");
+ const duration = new Temporal.Duration(0, 1);
+ const result = calendar.dateAdd(datetime, duration);
+ TemporalHelpers.assertPlainDate(result, 2000, 6, "M06", 2);
+ assert.sameValue(result.hour, undefined, "instance of PlainDate returned, not PlainDateTime");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-propertybag-calendar-case-insensitive.js
new file mode 100644
index 0000000000..b09b2939c2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-propertybag-calendar-case-insensitive.js
@@ -0,0 +1,20 @@
+// |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.calendar.prototype.dateadd
+description: The calendar name is case-insensitive
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const calendar = "IsO8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.dateAdd(arg, new Temporal.Duration());
+TemporalHelpers.assertPlainDate(result, 1976, 11, "M11", 18, "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-propertybag-calendar-leap-second.js
new file mode 100644
index 0000000000..7517d61c18
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-propertybag-calendar-leap-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.calendar.prototype.dateadd
+description: Leap second is a valid ISO string for a calendar in a property bag
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const calendar = "2016-12-31T23:59:60";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.dateAdd(arg, new Temporal.Duration());
+TemporalHelpers.assertPlainDate(
+ result,
+ 1976, 11, "M11", 18,
+ "leap second is a valid ISO string for calendar"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-propertybag-calendar-number.js
new file mode 100644
index 0000000000..80b3fcd76f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-propertybag-calendar-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.calendar.prototype.dateadd
+description: A number as calendar in a property bag is not accepted
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(1976, 11, 18);
+
+const numbers = [
+ 1,
+ 19970327,
+ -19970327,
+ 1234567890,
+];
+for (const calendar of numbers) {
+ const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+ assert.throws(
+ TypeError,
+ () => instance.dateAdd(arg, new Temporal.Duration()),
+ "Numbers cannot be used as a calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-propertybag-calendar-string.js
new file mode 100644
index 0000000000..cb12bd134f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-propertybag-calendar-string.js
@@ -0,0 +1,20 @@
+// |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.calendar.prototype.dateadd
+description: A calendar ID is valid input for Calendar
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const calendar = "iso8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.dateAdd(arg, new Temporal.Duration());
+TemporalHelpers.assertPlainDate(result, 1976, 11, "M11", 18, `Calendar created from string "${calendar}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-propertybag-calendar-wrong-type.js
new file mode 100644
index 0000000000..72fbbce9da
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-propertybag-calendar-wrong-type.js
@@ -0,0 +1,46 @@
+// |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.calendar.prototype.dateadd
+description: >
+ Appropriate error thrown when a calendar property from a property bag cannot
+ be converted to a calendar object or string
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.Calendar("iso8601");
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [calendar, description] of primitiveTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ typeof calendar === 'string' ? RangeError : TypeError,
+ () => instance.dateAdd(arg, new Temporal.Duration()),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+ [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields()
+];
+
+for (const [calendar, description] of typeErrorTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(TypeError, () => instance.dateAdd(arg, new Temporal.Duration()), `${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/Calendar/prototype/dateAdd/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-propertybag-calendar-year-zero.js
new file mode 100644
index 0000000000..2a93ec4ea9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-propertybag-calendar-year-zero.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.calendar.prototype.dateadd
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T17:45",
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+01:00",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+const instance = new Temporal.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.dateAdd(arg, new Temporal.Duration()),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-proto-in-calendar-fields.js
new file mode 100644
index 0000000000..4e49748823
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-proto-in-calendar-fields.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.calendar.prototype.dateadd
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.Calendar("iso8601");
+
+assert.throws(RangeError, () => instance.dateAdd(arg, new Temporal.Duration()));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..c14ecfd721
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-string-calendar-annotation.js
@@ -0,0 +1,34 @@
+// |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.calendar.prototype.dateadd
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2000-05-02[u-ca=iso8601]", "without time or time zone"],
+ ["2000-05-02[UTC][u-ca=iso8601]", "with time zone and no time"],
+ ["2000-05-02T15:23[u-ca=iso8601]", "without time zone"],
+ ["2000-05-02T15:23[UTC][u-ca=iso8601]", "with time zone"],
+ ["2000-05-02T15:23[!u-ca=iso8601]", "with ! and no time zone"],
+ ["2000-05-02T15:23[UTC][!u-ca=iso8601]", "with ! and time zone"],
+ ["2000-05-02T15:23[u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+const instance = new Temporal.Calendar("iso8601");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.dateAdd(arg, new Temporal.Duration());
+
+ TemporalHelpers.assertPlainDate(
+ result,
+ 2000, 5, "M05", 2,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..46728937ab
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-string-critical-unknown-annotation.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.calendar.prototype.dateadd
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[!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.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.dateAdd(arg, new Temporal.Duration()),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..3d3cde244a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-string-date-with-utc-offset.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.calendar.prototype.dateadd
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const validStrings = [
+ "2000-05-02T00+00:00",
+ "2000-05-02T00+00:00[UTC]",
+ "2000-05-02T00+00:00[!UTC]",
+ "2000-05-02T00-02:30[America/St_Johns]",
+];
+
+for (const arg of validStrings) {
+ const result = instance.dateAdd(arg, new Temporal.Duration());
+
+ TemporalHelpers.assertPlainDate(
+ result,
+ 2000, 5, "M05", 2,
+ `"${arg}" is a valid UTC offset with time for PlainDate`
+ );
+}
+
+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.dateAdd(arg, new Temporal.Duration()),
+ `"${arg}" UTC offset without time is not valid for PlainDate`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-string-invalid.js
new file mode 100644
index 0000000000..94cd428d07
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-string-invalid.js
@@ -0,0 +1,64 @@
+// |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.calendar.prototype.dateadd
+description: >
+ RangeError thrown if an invalid ISO string (or syntactically valid ISO string
+ that is not supported) is used as a PlainDate
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ // invalid ISO strings:
+ "",
+ "invalid iso8601",
+ "2020-01-00",
+ "2020-01-32",
+ "2020-02-30",
+ "2021-02-29",
+ "2020-00-01",
+ "2020-13-01",
+ "2020-01-01T",
+ "2020-01-01T25:00:00",
+ "2020-01-01T01:60:00",
+ "2020-01-01T01:60:61",
+ "2020-01-01junk",
+ "2020-01-01T00:00:00junk",
+ "2020-01-01T00:00:00+00:00junk",
+ "2020-01-01T00:00:00+00:00[UTC]junk",
+ "2020-01-01T00:00:00+00:00[UTC][u-ca=iso8601]junk",
+ "02020-01-01",
+ "2020-001-01",
+ "2020-01-001",
+ "2020-01-01T001",
+ "2020-01-01T01:001",
+ "2020-01-01T01:01:001",
+ // valid, but forms not supported in Temporal:
+ "2020-W01-1",
+ "2020-001",
+ "+0002020-01-01",
+ // valid, but this calendar must not exist:
+ "2020-01-01[u-ca=notexist]",
+ // may be valid in other contexts, but insufficient information for PlainDate:
+ "2020-01",
+ "+002020-01",
+ "01-01",
+ "2020-W01",
+ "P1Y",
+ "-P12Y",
+ // valid, but outside the supported range:
+ "-999999-01-01",
+ "+999999-01-01",
+];
+const instance = new Temporal.Calendar("iso8601");
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => instance.dateAdd(arg, new Temporal.Duration()),
+ `"${arg}" should not be a valid ISO string for a PlainDate`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..edbc719aaf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/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.calendar.prototype.dateadd
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[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.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.dateAdd(arg, new Temporal.Duration()),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..dfd6888026
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-string-multiple-time-zone.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.calendar.prototype.dateadd
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[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.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.dateAdd(arg, new Temporal.Duration()),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-string-time-separators.js
new file mode 100644
index 0000000000..7480f0af4c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-string-time-separators.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.calendar.prototype.dateadd
+description: Time separator in string argument can vary
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2000-05-02T15:23", "uppercase T"],
+ ["2000-05-02t15:23", "lowercase T"],
+ ["2000-05-02 15:23", "space between date and time"],
+];
+
+const instance = new Temporal.Calendar("iso8601");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.dateAdd(arg, new Temporal.Duration());
+
+ TemporalHelpers.assertPlainDate(
+ result,
+ 2000, 5, "M05", 2,
+ `variant time separators (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..e436365b1c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-string-time-zone-annotation.js
@@ -0,0 +1,39 @@
+// |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.calendar.prototype.dateadd
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2000-05-02[Asia/Kolkata]", "named, with no time"],
+ ["2000-05-02[!Europe/Vienna]", "named, with ! and no time"],
+ ["2000-05-02[+00:00]", "numeric, with no time"],
+ ["2000-05-02[!-02:30]", "numeric, with ! and no time"],
+ ["2000-05-02T15:23[America/Sao_Paulo]", "named, with no offset"],
+ ["2000-05-02T15:23[!Asia/Tokyo]", "named, with ! and no offset"],
+ ["2000-05-02T15:23[-02:30]", "numeric, with no offset"],
+ ["2000-05-02T15:23[!+00:00]", "numeric, with ! and no offset"],
+ ["2000-05-02T15:23+00:00[America/New_York]", "named, with offset"],
+ ["2000-05-02T15:23+00:00[!UTC]", "named, with offset and !"],
+ ["2000-05-02T15:23+00:00[+01:00]", "numeric, with offset"],
+ ["2000-05-02T15:23+00:00[!-08:00]", "numeric, with offset and !"],
+];
+
+const instance = new Temporal.Calendar("iso8601");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.dateAdd(arg, new Temporal.Duration());
+
+ TemporalHelpers.assertPlainDate(
+ result,
+ 2000, 5, "M05", 2,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..f217d7e8b7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-string-unknown-annotation.js
@@ -0,0 +1,33 @@
+// |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.calendar.prototype.dateadd
+description: Various forms of unknown annotation
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2000-05-02[foo=bar]", "without time"],
+ ["2000-05-02T15:23[foo=bar]", "alone"],
+ ["2000-05-02T15:23[UTC][foo=bar]", "with time zone"],
+ ["2000-05-02T15:23[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["2000-05-02T15:23[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["2000-05-02T15:23[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+const instance = new Temporal.Calendar("iso8601");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.dateAdd(arg, new Temporal.Duration());
+
+ TemporalHelpers.assertPlainDate(
+ result,
+ 2000, 5, "M05", 2,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..0bf4e2177a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-string-with-utc-designator.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.calendar.prototype.dateadd
+description: RangeError thrown if a string with UTC designator is used as a PlainDate
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "2019-10-01T09:00:00Z",
+ "2019-10-01T09:00:00Z[UTC]",
+];
+const instance = new Temporal.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.dateAdd(arg, new Temporal.Duration()),
+ "String with UTC designator should not be valid as a PlainDate"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-wrong-type.js
new file mode 100644
index 0000000000..0578415add
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/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.calendar.prototype.dateadd
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainDate
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+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.dateAdd(arg, new Temporal.Duration()),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.PlainDate, "Temporal.PlainDate, object"],
+ [Temporal.PlainDate.prototype, "Temporal.PlainDate.prototype, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.dateAdd(arg, new Temporal.Duration()), `${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/Calendar/prototype/dateAdd/argument-zoneddatetime-convert.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-zoneddatetime-convert.js
new file mode 100644
index 0000000000..3908fb2f29
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-zoneddatetime-convert.js
@@ -0,0 +1,22 @@
+// |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.calendar.prototype.dateadd
+description: An exception from TimeZone#getOffsetNanosecondsFor() is propagated.
+features: [Temporal]
+---*/
+
+class TZ extends Temporal.TimeZone {
+ constructor() { super("UTC") }
+ getOffsetNanosecondsFor() { throw new Test262Error() }
+}
+
+const tz = new TZ();
+const arg = new Temporal.ZonedDateTime(0n, tz);
+const instance = new Temporal.Calendar("iso8601");
+
+assert.throws(Test262Error, () => instance.dateAdd(arg, new Temporal.Duration()));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-zoneddatetime-slots.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-zoneddatetime-slots.js
new file mode 100644
index 0000000000..ef5dea755a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-zoneddatetime-slots.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.calendar.prototype.dateadd
+description: Getters are not called when converting a ZonedDateTime to a PlainDate.
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.ZonedDateTime.prototype);
+const getters = ["year", "month", "monthCode", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond", "calendar"];
+
+for (const property of getters) {
+ Object.defineProperty(Temporal.ZonedDateTime.prototype, property, {
+ get() {
+ actual.push(`get ${property}`);
+ const value = prototypeDescrs[property].get.call(this);
+ return {
+ toString() {
+ actual.push(`toString ${property}`);
+ return value.toString();
+ },
+ valueOf() {
+ actual.push(`valueOf ${property}`);
+ return value;
+ },
+ };
+ },
+ });
+}
+
+const arg = new Temporal.ZonedDateTime(0n, "UTC");
+const instance = new Temporal.Calendar("iso8601");
+instance.dateAdd(arg, new Temporal.Duration());
+assert.compareArray(actual, []);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..9155340af0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.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.calendar.prototype.dateadd
+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 calendar = new Temporal.Calendar("iso8601");
+ const duration = new Temporal.Duration(1);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => calendar.dateAdd(datetime, duration));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..5356882df5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.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.calendar.prototype.dateadd
+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 calendar = new Temporal.Calendar("iso8601");
+ const duration = new Temporal.Duration(1);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => calendar.dateAdd(datetime, duration),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..441f379188
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.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.calendar.prototype.dateadd
+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 calendar = new Temporal.Calendar("iso8601");
+ const duration = new Temporal.Duration(1);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => calendar.dateAdd(datetime, duration));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..4f7e73bdf3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,29 @@
+// |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.calendar.prototype.dateadd
+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 calendar = new Temporal.Calendar("iso8601");
+ const duration = new Temporal.Duration(1);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => calendar.dateAdd(datetime, duration));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/balance-smaller-units.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/balance-smaller-units.js
new file mode 100644
index 0000000000..398ca6999f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/balance-smaller-units.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.calendar.prototype.dateadd
+description: Durations with units smaller than days are balanced before adding
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = new Temporal.Calendar("iso8601");
+const date = new Temporal.PlainDate(2000, 5, 2, calendar);
+const duration = new Temporal.Duration(0, 0, 0, 1, 24, 1440, 86400, 86400_000, 86400_000_000, 86400_000_000_000);
+
+const result = calendar.dateAdd(date, duration);
+TemporalHelpers.assertPlainDate(result, 2000, 5, "M05", 9, "units smaller than days are balanced");
+
+const resultString = calendar.dateAdd(date, "P1DT24H1440M86400S");
+TemporalHelpers.assertPlainDate(resultString, 2000, 5, "M05", 6, "units smaller than days are balanced");
+
+const resultPropBag = calendar.dateAdd(date, { days: 1, hours: 24, minutes: 1440, seconds: 86400, milliseconds: 86400_000, microseconds: 86400_000_000, nanoseconds: 86400_000_000_000 });
+TemporalHelpers.assertPlainDate(resultPropBag, 2000, 5, "M05", 9, "units smaller than days are balanced");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/basic.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/basic.js
new file mode 100644
index 0000000000..2424570c74
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/basic.js
@@ -0,0 +1,53 @@
+// |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.calendar.prototype.dateadd
+description: Basic tests for dateAdd().
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const iso = Temporal.Calendar.from("iso8601");
+const date = Temporal.PlainDate.from("1994-11-06");
+const positiveDuration = Temporal.Duration.from({ months: 1, weeks: 1 });
+const negativeDuration = Temporal.Duration.from({ months: -1, weeks: -1 });
+
+TemporalHelpers.assertPlainDate(
+ iso.dateAdd(Temporal.PlainDateTime.from("1994-11-06T08:15:30"), positiveDuration, {}),
+ 1994, 12, "M12", 13, "date: PlainDateTime");
+
+TemporalHelpers.assertPlainDate(
+ iso.dateAdd({ year: 1994, month: 11, day: 6 }, positiveDuration, {}),
+ 1994, 12, "M12", 13, "date: property bag");
+
+TemporalHelpers.assertPlainDate(
+ iso.dateAdd("1994-11-06", positiveDuration, {}),
+ 1994, 12, "M12", 13, "date: string");
+
+assert.throws(TypeError, () => iso.dateAdd({ month: 11 }, positiveDuration, {}), "date: missing property");
+
+TemporalHelpers.assertPlainDate(
+ iso.dateAdd(date, { months: 1, weeks: 1 }, {}),
+ 1994, 12, "M12", 13, "duration: property bag");
+
+TemporalHelpers.assertPlainDate(
+ iso.dateAdd(date, "P1M1W", {}),
+ 1994, 12, "M12", 13, "duration: string");
+
+assert.throws(TypeError, () => iso.dateAdd(date, { month: 1 }, {}), "duration: missing property");
+
+TemporalHelpers.assertPlainDate(
+ iso.dateAdd(Temporal.PlainDateTime.from("1994-11-06T08:15:30"), negativeDuration, {}),
+ 1994, 9, "M09", 29, "date: PlainDateTime, negative duration");
+
+TemporalHelpers.assertPlainDate(
+ iso.dateAdd({ year: 1994, month: 11, day: 6 }, negativeDuration, {}),
+ 1994, 9, "M09", 29, "date: property bag, negative duration");
+
+TemporalHelpers.assertPlainDate(
+ iso.dateAdd("1994-11-06", negativeDuration, {}),
+ 1994, 9, "M09", 29, "date: string, negative duration");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/branding.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/branding.js
new file mode 100644
index 0000000000..c1db178920
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/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.calendar.prototype.dateadd
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const dateAdd = Temporal.Calendar.prototype.dateAdd;
+
+assert.sameValue(typeof dateAdd, "function");
+
+const args = [new Temporal.PlainDate(2000, 1, 1), new Temporal.Duration(1)];
+
+assert.throws(TypeError, () => dateAdd.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => dateAdd.apply(null, args), "null");
+assert.throws(TypeError, () => dateAdd.apply(true, args), "true");
+assert.throws(TypeError, () => dateAdd.apply("", args), "empty string");
+assert.throws(TypeError, () => dateAdd.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => dateAdd.apply(1, args), "1");
+assert.throws(TypeError, () => dateAdd.apply({}, args), "plain object");
+assert.throws(TypeError, () => dateAdd.apply(Temporal.Calendar, args), "Temporal.Calendar");
+assert.throws(TypeError, () => dateAdd.apply(Temporal.Calendar.prototype, args), "Temporal.Calendar.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/browser.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/builtin.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/builtin.js
new file mode 100644
index 0000000000..37351aff5a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/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.calendar.prototype.dateadd
+description: >
+ Tests that Temporal.Calendar.prototype.dateAdd
+ 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.Calendar.prototype.dateAdd),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Calendar.prototype.dateAdd),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Calendar.prototype.dateAdd),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Calendar.prototype.dateAdd.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/calendar-datefromfields-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/calendar-datefromfields-called-with-options-undefined.js
new file mode 100644
index 0000000000..bfad861fe3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/calendar-datefromfields-called-with-options-undefined.js
@@ -0,0 +1,18 @@
+// |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.calendar.prototype.dateadd
+description: >
+ Calendar.dateFromFields method is called with undefined as the options value
+ when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
+calendar.dateAdd({ year: 2000, month: 5, day: 2, calendar }, new Temporal.Duration(1));
+assert.sameValue(calendar.dateFromFieldsCallCount, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/calendar-fields-iterable.js
new file mode 100644
index 0000000000..b644d3e992
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/calendar-fields-iterable.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.calendar.prototype.dateadd
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.calendar.prototype.dateadd step 4:
+ 4. Set _date_ to ? ToTemporalDate(_date_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToListOfType(_fieldsArray_, « String »).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const duration = new Temporal.Duration(0, 1);
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+calendar1.dateAdd({ year: 2000, month: 5, day: 2, calendar: calendar2 }, duration);
+
+assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/calendar-temporal-object.js
new file mode 100644
index 0000000000..44ba8d6c9a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/calendar-temporal-object.js
@@ -0,0 +1,30 @@
+// |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.calendar.prototype.dateadd
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.calendar.prototype.dateadd step 4:
+ 4. Set _date_ to ? ToTemporalDate(_date_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const calendar = new Temporal.Calendar("iso8601");
+ const duration = new Temporal.Duration(0, 1);
+ calendar.dateAdd({ year: 2000, month: 5, day: 2, calendar: temporalObject }, duration);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/date-infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/date-infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..c3ef141dc7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/date-infinity-throws-rangeerror.js
@@ -0,0 +1,29 @@
+// |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.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.calendar.prototype.dateadd
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+const duration = new Temporal.Duration(1);
+const base = { year: 2000, month: 5, day: 2 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day"].forEach((prop) => {
+ ["constrain", "reject"].forEach((overflow) => {
+ assert.throws(RangeError, () => instance.dateAdd({ ...base, [prop]: inf }, duration, { overflow }), `${prop} property cannot be ${inf} (overflow ${overflow}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.dateAdd({ ...base, [prop]: obj }, duration, { overflow }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/duration-argument-string-negative-fractional-units.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/duration-argument-string-negative-fractional-units.js
new file mode 100644
index 0000000000..e1b7dd7116
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/duration-argument-string-negative-fractional-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.calendar.prototype.dateadd
+description: Strings with fractional duration units are treated with the correct sign
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = new Temporal.Calendar("iso8601");
+const instance = new Temporal.PlainDate(2000, 5, 2, calendar);
+
+const resultHours = calendar.dateAdd(instance, "-PT24.567890123H");
+TemporalHelpers.assertPlainDate(resultHours, 2000, 5, "M05", 1, "negative fractional hours");
+
+const resultMinutes = calendar.dateAdd(instance, "-PT1440.567890123M");
+TemporalHelpers.assertPlainDate(resultMinutes, 2000, 5, "M05", 1, "negative fractional minutes");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/length.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/length.js
new file mode 100644
index 0000000000..4e13716808
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/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.calendar.prototype.dateadd
+description: Temporal.Calendar.prototype.dateAdd.length is 2
+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.Calendar.prototype.dateAdd, "length", {
+ value: 2,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/name.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/name.js
new file mode 100644
index 0000000000..e32dfcf838
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/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.calendar.prototype.dateadd
+description: Temporal.Calendar.prototype.dateAdd.name is "dateAdd".
+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.Calendar.prototype.dateAdd, "name", {
+ value: "dateAdd",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/not-a-constructor.js
new file mode 100644
index 0000000000..714fc59932
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/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.calendar.prototype.dateadd
+description: >
+ Temporal.Calendar.prototype.dateAdd 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.Calendar.prototype.dateAdd();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Calendar.prototype.dateAdd), false,
+ "isConstructor(Temporal.Calendar.prototype.dateAdd)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/options-object.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/options-object.js
new file mode 100644
index 0000000000..5e083cbdf4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/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.calendar.prototype.dateadd
+description: Empty or a function object may be used as options
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const result1 = instance.dateAdd(new Temporal.PlainDate(1976, 11, 18), new Temporal.Duration(1), {});
+TemporalHelpers.assertPlainDate(
+ result1, 1977, 11, "M11", 18,
+ "options may be an empty plain object"
+);
+
+const result2 = instance.dateAdd(new Temporal.PlainDate(1976, 11, 18), new Temporal.Duration(1), () => {});
+TemporalHelpers.assertPlainDate(
+ result2, 1977, 11, "M11", 18,
+ "options may be a function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/options-wrong-type.js
new file mode 100644
index 0000000000..68f08ff73a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/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.calendar.prototype.dateadd
+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.Calendar("iso8601");
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.dateAdd(new Temporal.PlainDate(1976, 11, 18), new Temporal.Duration(1), value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/order-of-operations.js
new file mode 100644
index 0000000000..c1a3409489
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/order-of-operations.js
@@ -0,0 +1,122 @@
+// |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.calendar.prototype.dateadd
+description: Properties on an object passed to dateAdd() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ // ToTemporalDate → GetTemporalCalendarSlotValueWithISODefault
+ "get date.calendar",
+ "has date.calendar.dateAdd",
+ "has date.calendar.dateFromFields",
+ "has date.calendar.dateUntil",
+ "has date.calendar.day",
+ "has date.calendar.dayOfWeek",
+ "has date.calendar.dayOfYear",
+ "has date.calendar.daysInMonth",
+ "has date.calendar.daysInWeek",
+ "has date.calendar.daysInYear",
+ "has date.calendar.fields",
+ "has date.calendar.id",
+ "has date.calendar.inLeapYear",
+ "has date.calendar.mergeFields",
+ "has date.calendar.month",
+ "has date.calendar.monthCode",
+ "has date.calendar.monthDayFromFields",
+ "has date.calendar.monthsInYear",
+ "has date.calendar.weekOfYear",
+ "has date.calendar.year",
+ "has date.calendar.yearMonthFromFields",
+ "has date.calendar.yearOfWeek",
+ // lookup
+ "get date.calendar.dateFromFields",
+ "get date.calendar.fields",
+ // ToTemporalDate → CalendarFields
+ "call date.calendar.fields",
+ // ToTemporalDate → PrepareTemporalFields
+ "get date.day",
+ "get date.day.valueOf",
+ "call date.day.valueOf",
+ "get date.month",
+ "get date.month.valueOf",
+ "call date.month.valueOf",
+ "get date.monthCode",
+ "get date.monthCode.toString",
+ "call date.monthCode.toString",
+ "get date.year",
+ "get date.year.valueOf",
+ "call date.year.valueOf",
+ // ToTemporalDate → CalendarDateFromFields
+ "call date.calendar.dateFromFields",
+ // ToTemporalDuration
+ "get duration.days",
+ "get duration.days.valueOf",
+ "call duration.days.valueOf",
+ "get duration.hours",
+ "get duration.hours.valueOf",
+ "call duration.hours.valueOf",
+ "get duration.microseconds",
+ "get duration.microseconds.valueOf",
+ "call duration.microseconds.valueOf",
+ "get duration.milliseconds",
+ "get duration.milliseconds.valueOf",
+ "call duration.milliseconds.valueOf",
+ "get duration.minutes",
+ "get duration.minutes.valueOf",
+ "call duration.minutes.valueOf",
+ "get duration.months",
+ "get duration.months.valueOf",
+ "call duration.months.valueOf",
+ "get duration.nanoseconds",
+ "get duration.nanoseconds.valueOf",
+ "call duration.nanoseconds.valueOf",
+ "get duration.seconds",
+ "get duration.seconds.valueOf",
+ "call duration.seconds.valueOf",
+ "get duration.weeks",
+ "get duration.weeks.valueOf",
+ "call duration.weeks.valueOf",
+ "get duration.years",
+ "get duration.years.valueOf",
+ "call duration.years.valueOf",
+ // ToTemporalOverflow
+ "get options.overflow",
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+];
+const actual = [];
+
+const instance = new Temporal.Calendar("iso8601");
+
+const date = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2000,
+ month: 5,
+ monthCode: "M05",
+ day: 2,
+ calendar: TemporalHelpers.calendarObserver(actual, "date.calendar"),
+}, "date");
+
+const duration = TemporalHelpers.propertyBagObserver(actual, {
+ years: 1,
+ months: 2,
+ weeks: 3,
+ days: 4,
+ hours: 5,
+ minutes: 6,
+ seconds: 7,
+ milliseconds: 8,
+ microseconds: 9,
+ nanoseconds: 10,
+}, "duration");
+
+const options = TemporalHelpers.propertyBagObserver(actual, { overflow: "constrain" }, "options");
+
+instance.dateAdd(date, duration, options);
+assert.compareArray(actual, expected, "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/overflow-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/overflow-invalid-string.js
new file mode 100644
index 0000000000..fe288222f2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/overflow-invalid-string.js
@@ -0,0 +1,30 @@
+// |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.calendar.prototype.dateadd
+description: RangeError thrown when overflow option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal.calendar.prototype.dateadd step 7:
+ 7. Let _overflow_ be ? ToTemporalOverflow(_options_).
+features: [Temporal, arrow-function]
+---*/
+
+const calendar = new Temporal.Calendar("iso8601");
+const date = new Temporal.PlainDate(2000, 5, 2, calendar);
+const duration = new Temporal.Duration(3, 3, 0, 3);
+const badOverflows = ["", "CONSTRAIN", "balance", "other string", "constra\u0131n", "reject\0"];
+for (const overflow of badOverflows) {
+ assert.throws(
+ RangeError,
+ () => calendar.dateAdd(date, duration, { overflow }),
+ `invalid overflow ("${overflow}")`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/overflow-undefined.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/overflow-undefined.js
new file mode 100644
index 0000000000..83728adf8c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/overflow-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.calendar.prototype.dateadd
+description: Fallback value for overflow option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal.calendar.prototype.dateadd step 7:
+ 7. Let _overflow_ be ? ToTemporalOverflow(_options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = new Temporal.Calendar("iso8601");
+const date = new Temporal.PlainDate(2000, 5, 31, calendar);
+const duration = new Temporal.Duration(3, 1);
+
+const explicit = calendar.dateAdd(date, duration, { overflow: undefined });
+TemporalHelpers.assertPlainDate(explicit, 2003, 6, "M06", 30, "default overflow is constrain");
+const implicit = calendar.dateAdd(date, duration, {});
+TemporalHelpers.assertPlainDate(implicit, 2003, 6, "M06", 30, "default overflow is constrain");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/overflow-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/overflow-wrong-type.js
new file mode 100644
index 0000000000..48da48d4a3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/overflow-wrong-type.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.calendar.prototype.dateadd
+description: Type conversions for overflow option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal.calendar.prototype.dateadd step 7:
+ 7. Let _overflow_ be ? ToTemporalOverflow(_options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = new Temporal.Calendar("iso8601");
+const date = new Temporal.PlainDate(2000, 5, 2, calendar);
+const duration = new Temporal.Duration(3, 3, 0, 3);
+TemporalHelpers.checkStringOptionWrongType("overflow", "constrain",
+ (overflow) => calendar.dateAdd(date, duration, { overflow }),
+ (result, descr) => TemporalHelpers.assertPlainDate(result, 2003, 8, "M08", 5, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/prop-desc.js
new file mode 100644
index 0000000000..72ef97adf8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/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.calendar.prototype.dateadd
+description: The "dateAdd" property of Temporal.Calendar.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Calendar.prototype.dateAdd,
+ "function",
+ "`typeof Calendar.prototype.dateAdd` is `function`"
+);
+
+verifyProperty(Temporal.Calendar.prototype, "dateAdd", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/shell.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/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/Calendar/prototype/dateAdd/throw-range-error-from-ToTemporalDate.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/throw-range-error-from-ToTemporalDate.js
new file mode 100644
index 0000000000..9292f126ad
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/throw-range-error-from-ToTemporalDate.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dateadd
+description: Temporal.Calendar.prototype.dateAdd should throw from ToTemporalDate.
+info: |
+ ...
+ 4. Set date to ? ToTemporalDate(date).
+features: [Temporal, arrow-function]
+---*/
+let cal = new Temporal.Calendar("iso8601");
+
+assert.throws(RangeError,
+ () => cal.dateAdd("invalid date string", new Temporal.Duration(1)),
+ 'cal.dateAdd("invalid date string", new Temporal.Duration(1)) throws a RangeError exception');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/throw-range-error-from-ToTemporalDuration.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/throw-range-error-from-ToTemporalDuration.js
new file mode 100644
index 0000000000..4c4ae43502
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/throw-range-error-from-ToTemporalDuration.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dateadd
+description: Temporal.Calendar.prototype.dateAdd should throw from ToTemporalDuration.
+info: |
+ ...
+ 5. Set duration to ? ToTemporalDuration(duration).
+features: [Temporal, arrow-function]
+---*/
+let cal = new Temporal.Calendar("iso8601");
+
+assert.throws(RangeError,
+ () => cal.dateAdd("2020-02-03", "invalid duration string"),
+ 'cal.dateAdd("2020-02-03", "invalid duration string") throws a RangeError exception');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/throw-type-error-from-GetOptionsObject.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/throw-type-error-from-GetOptionsObject.js
new file mode 100644
index 0000000000..c0a83de563
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/throw-type-error-from-GetOptionsObject.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.calendar.prototype.dateadd
+description: Temporal.Calendar.prototype.dateAdd should throw from GetOptionsObject.
+info: |
+ ...
+ 6. Set options to ? GetOptionsObject(options).
+features: [BigInt, Symbol, Temporal, arrow-function]
+---*/
+let cal = new Temporal.Calendar('iso8601');
+let invalidOptionsList = [null, 'invalid option', 234, 23n, Symbol('foo'), true, false, Infinity];
+
+invalidOptionsList.forEach(function(invalidOptions) {
+ assert.throws(
+ TypeError,
+ () => cal.dateAdd('2020-02-03', 'P1Y', invalidOptions),
+ 'cal.dateAdd("2020-02-03", "P1Y", invalidOptions) throws a TypeError exception'
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/year-zero.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/year-zero.js
new file mode 100644
index 0000000000..4d9c53e388
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateAdd/year-zero.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.calendar.prototype.dateadd
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T00:45",
+ "-000000-10-31T00:45+01:00",
+ "-000000-10-31T00:45+00:00[UTC]",
+];
+const instance = new Temporal.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.dateAdd(arg, new Temporal.Duration()),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/branding.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/branding.js
new file mode 100644
index 0000000000..6758e0cdd1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/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.calendar.prototype.datefromfields
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const dateFromFields = Temporal.Calendar.prototype.dateFromFields;
+
+assert.sameValue(typeof dateFromFields, "function");
+
+const args = [{ year: 2000, month: 1, day: 1 }];
+
+assert.throws(TypeError, () => dateFromFields.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => dateFromFields.apply(null, args), "null");
+assert.throws(TypeError, () => dateFromFields.apply(true, args), "true");
+assert.throws(TypeError, () => dateFromFields.apply("", args), "empty string");
+assert.throws(TypeError, () => dateFromFields.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => dateFromFields.apply(1, args), "1");
+assert.throws(TypeError, () => dateFromFields.apply({}, args), "plain object");
+assert.throws(TypeError, () => dateFromFields.apply(Temporal.Calendar, args), "Temporal.Calendar");
+assert.throws(TypeError, () => dateFromFields.apply(Temporal.Calendar.prototype, args), "Temporal.Calendar.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/browser.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/builtin.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/builtin.js
new file mode 100644
index 0000000000..f039b1d2cd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/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.calendar.prototype.datefromfields
+description: >
+ Tests that Temporal.Calendar.prototype.dateFromFields
+ 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.Calendar.prototype.dateFromFields),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Calendar.prototype.dateFromFields),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Calendar.prototype.dateFromFields),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Calendar.prototype.dateFromFields.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/fields-not-object.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/fields-not-object.js
new file mode 100644
index 0000000000..38475bb330
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/fields-not-object.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.calendar.prototype.datefromfields
+description: Throw a TypeError if the fields is not an object
+info: |
+ 4. If Type(_fields_) is not Object, throw a *TypeError* exception.
+features: [BigInt, Symbol, Temporal, arrow-function]
+---*/
+
+const tests = [undefined, null, true, false, "string", Symbol("sym"), Infinity, NaN, Math.PI, 42n];
+const iso = Temporal.Calendar.from("iso8601");
+for (const fields of tests) {
+ assert.throws(
+ TypeError,
+ () => iso.dateFromFields(fields, {})
+ `dateFromFields(${typeof fields}) throws a TypeError exception`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..3024c6466b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/infinity-throws-rangeerror.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.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.calendar.prototype.datefromfields
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+const base = { year: 2000, month: 5, day: 2 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day"].forEach((prop) => {
+ ["constrain", "reject"].forEach((overflow) => {
+ assert.throws(RangeError, () => instance.dateFromFields({ ...base, [prop]: inf }, { overflow }), `${prop} property cannot be ${inf} (overflow ${overflow}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.dateFromFields({ ...base, [prop]: obj }, { overflow }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/length.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/length.js
new file mode 100644
index 0000000000..3c7ec630d9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/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.calendar.prototype.datefromfields
+description: Temporal.Calendar.prototype.dateFromFields.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.Calendar.prototype.dateFromFields, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/missing-properties.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/missing-properties.js
new file mode 100644
index 0000000000..6a9ffd5dfc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/missing-properties.js
@@ -0,0 +1,48 @@
+// |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.calendar.prototype.datefromfields
+description: Errors due to missing properties on fields object are thrown in the correct order
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const missingDay = {
+ get year() {
+ TemporalHelpers.assertUnreachable("day should be checked first");
+ },
+ get month() {
+ TemporalHelpers.assertUnreachable("day should be checked first");
+ },
+ get monthCode() {
+ TemporalHelpers.assertUnreachable("day should be checked first");
+ },
+};
+assert.throws(TypeError, () => instance.dateFromFields(missingDay), "day should be checked before year and month");
+
+let getMonth = false;
+let getMonthCode = false;
+const missingYearAndMonth = {
+ day: 1,
+ get month() {
+ getMonth = true;
+ },
+ get monthCode() {
+ getMonthCode = true;
+ },
+};
+assert.throws(TypeError, () => instance.dateFromFields(missingYearAndMonth), "year should be checked after fetching but before resolving the month");
+assert(getMonth, "year is fetched after month");
+assert(getMonthCode, "year is fetched after monthCode");
+
+const missingMonth = {
+ day: 1,
+ year: 2000,
+};
+assert.throws(TypeError, () => instance.dateFromFields(missingMonth), "month should be resolved last");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/name.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/name.js
new file mode 100644
index 0000000000..f88fb05139
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/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.calendar.prototype.datefromfields
+description: Temporal.Calendar.prototype.dateFromFields.name is "dateFromFields".
+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.Calendar.prototype.dateFromFields, "name", {
+ value: "dateFromFields",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/not-a-constructor.js
new file mode 100644
index 0000000000..bc9a9a78c2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/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.calendar.prototype.datefromfields
+description: >
+ Temporal.Calendar.prototype.dateFromFields 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.Calendar.prototype.dateFromFields();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Calendar.prototype.dateFromFields), false,
+ "isConstructor(Temporal.Calendar.prototype.dateFromFields)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/one-of-era-erayear-undefined.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/one-of-era-erayear-undefined.js
new file mode 100644
index 0000000000..67d4befdce
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/one-of-era-erayear-undefined.js
@@ -0,0 +1,19 @@
+// |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.calendar.prototype.datefromfields
+description: Does not throw a RangeError if only one of era/eraYear fields is present
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const base = { year: 2000, month: 5, day: 2, era: 'ce' };
+const instance = new Temporal.Calendar('iso8601');
+TemporalHelpers.assertPlainDate(instance.dateFromFields({ ...base }), 2000, 5, 'M05', 2);
+
+const base2 = { year: 2000, month: 5, day: 2, eraYear: 1 };
+TemporalHelpers.assertPlainDate(instance.dateFromFields({ ...base }), 2000, 5, 'M05', 2);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/options-object.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/options-object.js
new file mode 100644
index 0000000000..2905c8ea94
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/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.calendar.prototype.datefromfields
+description: Empty or a function object may be used as options
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const result1 = instance.dateFromFields({ year: 1976, month: 11, day: 18 }, {});
+TemporalHelpers.assertPlainDate(
+ result1, 1976, 11, "M11", 18,
+ "options may be an empty plain object"
+);
+
+const result2 = instance.dateFromFields({ year: 1976, month: 11, day: 18 }, () => {});
+TemporalHelpers.assertPlainDate(
+ result2, 1976, 11, "M11", 18,
+ "options may be a function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/options-wrong-type.js
new file mode 100644
index 0000000000..9193330fdd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/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.calendar.prototype.datefromfields
+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.Calendar("iso8601");
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.dateFromFields({ year: 1976, month: 11, day: 18 }, value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/order-of-operations.js
new file mode 100644
index 0000000000..aff7bbdde3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/order-of-operations.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.calendar.prototype.datefromfields
+description: Properties on objects passed to dateFromFields() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "get fields.day",
+ "get fields.day.valueOf",
+ "call fields.day.valueOf",
+ "get fields.month",
+ "get fields.month.valueOf",
+ "call fields.month.valueOf",
+ "get fields.monthCode",
+ "get fields.monthCode.toString",
+ "call fields.monthCode.toString",
+ "get fields.year",
+ "get fields.year.valueOf",
+ "call fields.year.valueOf",
+ "get options.overflow",
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+];
+const actual = [];
+
+const instance = new Temporal.Calendar("iso8601");
+
+const fields = TemporalHelpers.propertyBagObserver(actual, {
+ year: 1.7,
+ month: 1.7,
+ monthCode: "M01",
+ day: 1.7,
+}, "fields");
+
+const options = TemporalHelpers.propertyBagObserver(actual, {
+ overflow: "reject",
+}, "options");
+
+const result = instance.dateFromFields(fields, options);
+TemporalHelpers.assertPlainDate(result, 1, 1, "M01", 1, "date result");
+assert.sameValue(result.getISOFields().calendar, "iso8601", "calendar slot should store a string");
+assert.compareArray(actual, expected, "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/overflow-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/overflow-invalid-string.js
new file mode 100644
index 0000000000..153e0361c2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/overflow-invalid-string.js
@@ -0,0 +1,31 @@
+// |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.calendar.prototype.datefromfields
+description: RangeError thrown when overflow option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-isodatefromfields step 2:
+ 2. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.calendar.prototype.datefromfields step 6:
+ 6. Let _result_ be ? ISODateFromFields(_fields_, _options_).
+features: [Temporal, arrow-function]
+---*/
+
+const calendar = new Temporal.Calendar("iso8601");
+const badOverflows = ["", "CONSTRAIN", "balance", "other string", "constra\u0131n", "reject\0"];
+for (const overflow of badOverflows) {
+ assert.throws(
+ RangeError,
+ () => calendar.dateFromFields({ year: 2000, month: 5, day: 2 },
+ { overflow }),
+ `invalid overflow ("${overflow}")`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/overflow-undefined.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/overflow-undefined.js
new file mode 100644
index 0000000000..3e4b9bb1d9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/overflow-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.calendar.prototype.datefromfields
+description: Fallback value for overflow option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-isodatefromfields step 2:
+ 2. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.calendar.prototype.datefromfields step 6:
+ 6. Let _result_ be ? ISODateFromFields(_fields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = new Temporal.Calendar("iso8601");
+
+const explicit = calendar.dateFromFields({ year: 2000, month: 15, day: 2 }, { overflow: undefined });
+TemporalHelpers.assertPlainDate(explicit, 2000, 12, "M12", 2, "default overflow is constrain");
+const implicit = calendar.dateFromFields({ year: 2000, month: 15, day: 2 }, {});
+TemporalHelpers.assertPlainDate(implicit, 2000, 12, "M12", 2, "default overflow is constrain");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/overflow-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/overflow-wrong-type.js
new file mode 100644
index 0000000000..b75d4beb59
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/overflow-wrong-type.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.calendar.prototype.datefromfields
+description: Type conversions for overflow option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-isodatefromfields step 2:
+ 2. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.calendar.prototype.datefromfields step 6:
+ 6. Let _result_ be ? ISODateFromFields(_fields_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = new Temporal.Calendar("iso8601");
+TemporalHelpers.checkStringOptionWrongType("overflow", "constrain",
+ (overflow) => calendar.dateFromFields({ year: 2000, month: 5, day: 2 }, { overflow }),
+ (result, descr) => TemporalHelpers.assertPlainDate(result, 2000, 5, "M05", 2, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/prop-desc.js
new file mode 100644
index 0000000000..50871e8d91
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/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.calendar.prototype.datefromfields
+description: The "dateFromFields" property of Temporal.Calendar.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Calendar.prototype.dateFromFields,
+ "function",
+ "`typeof Calendar.prototype.dateFromFields` is `function`"
+);
+
+verifyProperty(Temporal.Calendar.prototype, "dateFromFields", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/shell.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/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/Calendar/prototype/dateFromFields/throw-type-error-from-GetOptionsObject.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/throw-type-error-from-GetOptionsObject.js
new file mode 100644
index 0000000000..53393dae1e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/throw-type-error-from-GetOptionsObject.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.calendar.prototype.datefromfields
+description: Temporal.Calendar.prototype.dateFromFields should throw TypeError from GetOptionsObject.
+info: |
+ 4. If Type(fields) is not Object, throw a TypeError exception.
+features: [BigInt, Symbol, Temporal, arrow-function]
+---*/
+let cal = new Temporal.Calendar('iso8601');
+
+let fields = {
+ year: 2021,
+ month: 7,
+ day: 20
+};
+
+let notObjectList = [null, 'string', Symbol('efg'), true, false, Infinity, NaN, 123, 456n];
+
+notObjectList.forEach(function(options) {
+ assert.throws(
+ TypeError,
+ () => cal.dateFromFields(fields, options),
+ 'cal.dateFromFields(fields, options) throws a TypeError exception'
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/throws-range-error.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/throws-range-error.js
new file mode 100644
index 0000000000..3b11f18888
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/throws-range-error.js
@@ -0,0 +1,126 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.calendar.prototype.datefromfields
+description: >
+ Temporal.Calendar.prototype.dateFromFields should throw RangeError for
+ input not in valid range.
+info: |
+ 1. Let calendar be the this value.
+ 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
+ 3. Assert: calendar.[[Identifier]] is "iso8601".
+ 4. If Type(fields) is not Object, throw a TypeError exception.
+ 5. Set options to ? GetOptionsObject(options).
+ 6. Let result be ? ISODateFromFields(fields, options).
+ 7. Return ? CreateTemporalDate(result.[[Year]], result.[[Month]], result.[[Day]], calendar).
+features: [Temporal, arrow-function]
+---*/
+let cal = new Temporal.Calendar("iso8601")
+
+assert.throws(RangeError, () => cal.dateFromFields({year: 2021, monthCode: "m1", day: 17}),
+ 'cal.dateFromFields({year: 2021, monthCode: "m1", day: 17}) throws a RangeError exception');
+assert.throws(RangeError, () => cal.dateFromFields({year: 2021, monthCode: "M1", day: 17}),
+ 'cal.dateFromFields({year: 2021, monthCode: "M1", day: 17}) throws a RangeError exception');
+assert.throws(RangeError, () => cal.dateFromFields({year: 2021, monthCode: "m01", day: 17}),
+ 'cal.dateFromFields({year: 2021, monthCode: "m01", day: 17}) throws a RangeError exception');
+
+assert.throws(RangeError, () => cal.dateFromFields({year: 2021, month: 12, monthCode: "M11", day: 17}),
+ 'cal.dateFromFields({year: 2021, month: 12, monthCode: "M11", day: 17}) throws a RangeError exception');
+assert.throws(RangeError, () => cal.dateFromFields({year: 2021, monthCode: "M00", day: 17}),
+ 'cal.dateFromFields({year: 2021, monthCode: "M00", day: 17}) throws a RangeError exception');
+assert.throws(RangeError, () => cal.dateFromFields({year: 2021, monthCode: "M19", day: 17}),
+ 'cal.dateFromFields({year: 2021, monthCode: "M19", day: 17}) throws a RangeError exception');
+assert.throws(RangeError, () => cal.dateFromFields({year: 2021, monthCode: "M99", day: 17}),
+ 'cal.dateFromFields({year: 2021, monthCode: "M99", day: 17}) throws a RangeError exception');
+assert.throws(RangeError, () => cal.dateFromFields({year: 2021, monthCode: "M13", day: 17}),
+ 'cal.dateFromFields({year: 2021, monthCode: "M13", day: 17}) throws a RangeError exception');
+
+assert.throws(RangeError, () => cal.dateFromFields({year: 2021, month: -1, day: 17}),
+ 'cal.dateFromFields({year: 2021, month: -1, day: 17}) throws a RangeError exception');
+assert.throws(RangeError, () => cal.dateFromFields({year: 2021, month: -Infinity, day: 17}),
+ 'cal.dateFromFields({year: 2021, month: -Infinity, day: 17}) throws a RangeError exception');
+assert.throws(RangeError, () => cal.dateFromFields({year: 2021, month: 7, day: -17}),
+ 'cal.dateFromFields({year: 2021, month: 7, day: -17}) throws a RangeError exception');
+assert.throws(RangeError, () => cal.dateFromFields({year: 2021, month: 7, day: -Infinity}),
+ 'cal.dateFromFields({year: 2021, month: 7, day: -Infinity}) throws a RangeError exception');
+
+assert.throws(RangeError, () => cal.dateFromFields({year: 2021, month: 12, day: 0}, {overflow: "reject"}),
+ 'cal.dateFromFields({year: 2021, month: 12, day: 0}, {overflow: "reject"}) throws a RangeError exception');
+assert.throws(RangeError, () => cal.dateFromFields({year: 2021, month: 12, day: 32}, {overflow: "reject"}),
+ 'cal.dateFromFields({year: 2021, month: 12, day: 32}, {overflow: "reject"}) throws a RangeError exception');
+assert.throws(RangeError, () => cal.dateFromFields({year: 2021, month: 1, day: 32}, {overflow: "reject"}),
+ 'cal.dateFromFields({year: 2021, month: 1, day: 32}, {overflow: "reject"}) throws a RangeError exception');
+assert.throws(RangeError, () => cal.dateFromFields({year: 2021, month: 2, day: 29}, {overflow: "reject"}),
+ 'cal.dateFromFields({year: 2021, month: 2, day: 29}, {overflow: "reject"}) throws a RangeError exception');
+assert.throws(RangeError, () => cal.dateFromFields({year: 2021, month: 6, day: 31}, {overflow: "reject"}),
+ 'cal.dateFromFields({year: 2021, month: 6, day: 31}, {overflow: "reject"}) throws a RangeError exception');
+assert.throws(RangeError, () => cal.dateFromFields({year: 2021, month: 9, day: 31}, {overflow: "reject"}),
+ 'cal.dateFromFields({year: 2021, month: 9, day: 31}, {overflow: "reject"}) throws a RangeError exception');
+assert.throws(RangeError, () => cal.dateFromFields({year: 2021, month: 0, day: 5}, {overflow: "reject"}),
+ 'cal.dateFromFields({year: 2021, month: 0, day: 5}, {overflow: "reject"}) throws a RangeError exception');
+assert.throws(RangeError, () => cal.dateFromFields({year: 2021, month: 13, day: 5}, {overflow: "reject"}),
+ 'cal.dateFromFields({year: 2021, month: 13, day: 5}, {overflow: "reject"}) throws a RangeError exception');
+
+assert.throws(RangeError, () => cal.dateFromFields(
+ {year: 2021, monthCode: "M12", day: 0}, {overflow: "reject"}),
+ 'cal.dateFromFields( {year: 2021, monthCode: "M12", day: 0}, {overflow: "reject"}) throws a RangeError exception');
+assert.throws(RangeError, () => cal.dateFromFields(
+ {year: 2021, monthCode: "M12", day: 32}, {overflow: "reject"}),
+ 'cal.dateFromFields( {year: 2021, monthCode: "M12", day: 32}, {overflow: "reject"}) throws a RangeError exception');
+assert.throws(RangeError, () => cal.dateFromFields(
+ {year: 2021, monthCode: "M01", day: 32}, {overflow: "reject"}),
+ 'cal.dateFromFields( {year: 2021, monthCode: "M01", day: 32}, {overflow: "reject"}) throws a RangeError exception');
+assert.throws(RangeError, () => cal.dateFromFields(
+ {year: 2021, monthCode: "M02", day: 29}, {overflow: "reject"}),
+ 'cal.dateFromFields( {year: 2021, monthCode: "M02", day: 29}, {overflow: "reject"}) throws a RangeError exception');
+assert.throws(RangeError, () => cal.dateFromFields(
+ {year: 2021, monthCode: "M06", day: 31}, {overflow: "reject"}),
+ 'cal.dateFromFields( {year: 2021, monthCode: "M06", day: 31}, {overflow: "reject"}) throws a RangeError exception');
+assert.throws(RangeError, () => cal.dateFromFields(
+ {year: 2021, monthCode: "M09", day: 31}, {overflow: "reject"}),
+ 'cal.dateFromFields( {year: 2021, monthCode: "M09", day: 31}, {overflow: "reject"}) throws a RangeError exception');
+assert.throws(RangeError, () => cal.dateFromFields(
+ {year: 2021, monthCode: "M00", day: 5}, {overflow: "reject"}),
+ 'cal.dateFromFields( {year: 2021, monthCode: "M00", day: 5}, {overflow: "reject"}) throws a RangeError exception');
+assert.throws(RangeError, () => cal.dateFromFields(
+ {year: 2021, monthCode: "M13", day: 5}, {overflow: "reject"}),
+ 'cal.dateFromFields( {year: 2021, monthCode: "M13", day: 5}, {overflow: "reject"}) throws a RangeError exception');
+
+assert.throws(RangeError, () => cal.dateFromFields(
+ {year: 2021, month: 12, day: 0}), 'cal.dateFromFields( {year: 2021, month: 12, day: 0}) throws a RangeError exception');
+assert.throws(RangeError, () => cal.dateFromFields(
+ {year: 2021, month: 0, day: 3}), 'cal.dateFromFields( {year: 2021, month: 0, day: 3}) throws a RangeError exception');
+
+// Check throw for the second arg
+assert.throws(RangeError, () => cal.dateFromFields(
+ {year: 2021, month: 7, day: 13}, {overflow: "invalid"}), 'cal.dateFromFields( {year: 2021, month: 7, day: 13}, {overflow: "invalid"}) throws a RangeError exception');
+
+assert.throws(RangeError, () => cal.dateFromFields(
+ {year: 2021, month: 1, day: 32}, {overflow: "reject"}), 'cal.dateFromFields( {year: 2021, month: 1, day: 32}, {overflow: "reject"}) throws a RangeError exception');
+assert.throws(RangeError, () => cal.dateFromFields(
+ {year: 2021, month: 2, day: 29}, {overflow: "reject"}), 'cal.dateFromFields( {year: 2021, month: 2, day: 29}, {overflow: "reject"}) throws a RangeError exception');
+assert.throws(RangeError, () => cal.dateFromFields(
+ {year: 2021, month: 3, day: 32}, {overflow: "reject"}), 'cal.dateFromFields( {year: 2021, month: 3, day: 32}, {overflow: "reject"}) throws a RangeError exception');
+assert.throws(RangeError, () => cal.dateFromFields(
+ {year: 2021, month: 4, day: 31}, {overflow: "reject"}), 'cal.dateFromFields( {year: 2021, month: 4, day: 31}, {overflow: "reject"}) throws a RangeError exception');
+assert.throws(RangeError, () => cal.dateFromFields(
+ {year: 2021, month: 5, day: 32}, {overflow: "reject"}), 'cal.dateFromFields( {year: 2021, month: 5, day: 32}, {overflow: "reject"}) throws a RangeError exception');
+assert.throws(RangeError, () => cal.dateFromFields(
+ {year: 2021, month: 6, day: 31}, {overflow: "reject"}), 'cal.dateFromFields( {year: 2021, month: 6, day: 31}, {overflow: "reject"}) throws a RangeError exception');
+assert.throws(RangeError, () => cal.dateFromFields(
+ {year: 2021, month: 7, day: 32}, {overflow: "reject"}), 'cal.dateFromFields( {year: 2021, month: 7, day: 32}, {overflow: "reject"}) throws a RangeError exception');
+assert.throws(RangeError, () => cal.dateFromFields(
+ {year: 2021, month: 8, day: 32}, {overflow: "reject"}), 'cal.dateFromFields( {year: 2021, month: 8, day: 32}, {overflow: "reject"}) throws a RangeError exception');
+assert.throws(RangeError, () => cal.dateFromFields(
+ {year: 2021, month: 9, day: 31}, {overflow: "reject"}), 'cal.dateFromFields( {year: 2021, month: 9, day: 31}, {overflow: "reject"}) throws a RangeError exception');
+assert.throws(RangeError, () => cal.dateFromFields(
+ {year: 2021, month: 10, day: 32}, {overflow: "reject"}), 'cal.dateFromFields( {year: 2021, month: 10, day: 32}, {overflow: "reject"}) throws a RangeError exception');
+assert.throws(RangeError, () => cal.dateFromFields(
+ {year: 2021, month: 11, day: 31}, {overflow: "reject"}), 'cal.dateFromFields( {year: 2021, month: 11, day: 31}, {overflow: "reject"}) throws a RangeError exception');
+assert.throws(RangeError, () => cal.dateFromFields(
+ {year: 2021, month: 12, day: 32}, {overflow: "reject"}), 'cal.dateFromFields( {year: 2021, month: 12, day: 32}, {overflow: "reject"}) throws a RangeError exception');
+assert.throws(RangeError, () => cal.dateFromFields(
+ {year: 2021, month: 13, day: 5}, {overflow: "reject"}), 'cal.dateFromFields( {year: 2021, month: 13, day: 5}, {overflow: "reject"}) throws a RangeError exception');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/throws-type-error.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/throws-type-error.js
new file mode 100644
index 0000000000..83b69f4d9f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/throws-type-error.js
@@ -0,0 +1,58 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.calendar.prototype.datefromfields
+description: Temporal.Calendar.prototype.dateFromFields should throw TypeError with wrong type.
+info: |
+ 1. Let calendar be the this value.
+ 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
+ 3. Assert: calendar.[[Identifier]] is "iso8601".
+ 4. If Type(fields) is not Object, throw a TypeError exception.
+ 5. Set options to ? GetOptionsObject(options).
+ 6. Let result be ? ISODateFromFields(fields, options).
+ 7. Return ? CreateTemporalDate(result.[[Year]], result.[[Month]], result.[[Day]], calendar).
+features: [BigInt, Symbol, Temporal, arrow-function]
+---*/
+// Check throw for first arg
+let cal = new Temporal.Calendar('iso8601');
+assert.throws(TypeError, () => cal.dateFromFields(), 'cal.dateFromFields() throws a TypeError exception');
+
+[undefined, true, false, 123, 456n, Symbol(), 'string'].forEach(function(fields) {
+ assert.throws(
+ TypeError,
+ () => cal.dateFromFields(fields),
+ 'cal.dateFromFields(fields) throws a TypeError exception'
+ );
+
+ assert.throws(
+ TypeError,
+ () => cal.dateFromFields(fields, undefined),
+ 'cal.dateFromFields(fields, undefined) throws a TypeError exception'
+ );
+
+ assert.throws(TypeError, () => cal.dateFromFields(fields, {
+ overflow: 'constrain'
+ }), 'cal.dateFromFields(fields, {overflow: "constrain"}) throws a TypeError exception');
+
+ assert.throws(TypeError, () => cal.dateFromFields(fields, {
+ overflow: 'reject'
+ }), 'cal.dateFromFields(fields, {overflow: "reject"}) throws a TypeError exception');
+});
+
+assert.throws(TypeError, () => cal.dateFromFields({
+ month: 1,
+ day: 17
+}), 'cal.dateFromFields({month: 1, day: 17}) throws a TypeError exception');
+
+assert.throws(TypeError, () => cal.dateFromFields({
+ year: 2021,
+ day: 17
+}), 'cal.dateFromFields({year: 2021, day: 17}) throws a TypeError exception');
+
+assert.throws(TypeError, () => cal.dateFromFields({
+ year: 2021,
+ month: 12
+}), 'cal.dateFromFields({year: 2021, month: 12}) throws a TypeError exception');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/with-year-month-day-need-constrain.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/with-year-month-day-need-constrain.js
new file mode 100644
index 0000000000..d2e766146e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/with-year-month-day-need-constrain.js
@@ -0,0 +1,90 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.calendar.prototype.datefromfields
+description: Temporal.Calendar.prototype.dateFromFields with year/month/day and need constrain
+info: |
+ 1. Let calendar be the this value.
+ 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
+ 3. Assert: calendar.[[Identifier]] is "iso8601".
+ 4. If Type(fields) is not Object, throw a TypeError exception.
+ 5. Set options to ? GetOptionsObject(options).
+ 6. Let result be ? ISODateFromFields(fields, options).
+ 7. Return ? CreateTemporalDate(result.[[Year]], result.[[Month]], result.[[Day]], calendar).
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+let cal = new Temporal.Calendar("iso8601")
+
+TemporalHelpers.assertPlainDate(
+ cal.dateFromFields({year: 2021, month: 1, day: 133}),
+ 2021, 1, "M01", 31,
+ "year/month/day with day need to be constrained in Jan");
+
+TemporalHelpers.assertPlainDate(
+ cal.dateFromFields({year: 2021, month: 2, day: 133}),
+ 2021, 2, "M02", 28,
+ "year/month/day with day need to be constrained in Feb");
+
+TemporalHelpers.assertPlainDate(
+ cal.dateFromFields({year: 2021, month: 3, day: 133}),
+ 2021, 3, "M03", 31,
+ "year/month/day with day need to be constrained in March");
+
+TemporalHelpers.assertPlainDate(
+ cal.dateFromFields({year: 2021, month: 4, day: 133}),
+ 2021, 4, "M04", 30,
+ "year/month/day with day need to be constrained in April");
+
+TemporalHelpers.assertPlainDate(
+ cal.dateFromFields({year: 2021, month: 5, day: 133}),
+ 2021, 5, "M05", 31,
+ "year/month/day with day need to be constrained in May");
+
+TemporalHelpers.assertPlainDate(
+ cal.dateFromFields({year: 2021, month: 6, day: 133}),
+ 2021, 6, "M06", 30,
+ "year/month/day with day need to be constrained in Jun");
+
+TemporalHelpers.assertPlainDate(
+ cal.dateFromFields({year: 2021, month: 7, day: 133}),
+ 2021, 7, "M07", 31,
+ "year/month/day with day need to be constrained in July");
+
+TemporalHelpers.assertPlainDate(
+ cal.dateFromFields({year: 2021, month: 8, day: 133}),
+ 2021, 8, "M08", 31,
+ "year/month/day with day need to be constrained in Aug");
+
+TemporalHelpers.assertPlainDate(
+ cal.dateFromFields({year: 2021, month: 9, day: 133}),
+ 2021, 9, "M09", 30,
+ "year/month/day with day need to be constrained in Sept.");
+
+TemporalHelpers.assertPlainDate(
+ cal.dateFromFields({year: 2021, month: 10, day: 133}),
+ 2021, 10, "M10", 31,
+ "year/month/day with day need to be constrained in Oct.");
+
+TemporalHelpers.assertPlainDate(
+ cal.dateFromFields({year: 2021, month: 11, day: 133}),
+ 2021, 11, "M11", 30,
+ "year/month/day with day need to be constrained in Nov.");
+
+TemporalHelpers.assertPlainDate(
+ cal.dateFromFields({year: 2021, month: 12, day: 133}),
+ 2021, 12, "M12", 31,
+ "year/month/day with day need to be constrained in Dec.");
+
+TemporalHelpers.assertPlainDate(
+ cal.dateFromFields({year: 2021, month: 13, day: 500}),
+ 2021, 12, "M12", 31,
+ "year/month/day with month and day need to be constrained");
+
+TemporalHelpers.assertPlainDate(
+ cal.dateFromFields({year: 2021, month: 999999, day: 500}),
+ 2021, 12, "M12", 31,
+ "year/month/day with month and day need to be constrained");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/with-year-month-day.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/with-year-month-day.js
new file mode 100644
index 0000000000..e24b574004
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/with-year-month-day.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.calendar.prototype.datefromfields
+description: Temporal.Calendar.prototype.dateFromFields with year/month/day
+info: |
+ 1. Let calendar be the this value.
+ 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
+ 3. Assert: calendar.[[Identifier]] is "iso8601".
+ 4. If Type(fields) is not Object, throw a TypeError exception.
+ 5. Set options to ? GetOptionsObject(options).
+ 6. Let result be ? ISODateFromFields(fields, options).
+ 7. Return ? CreateTemporalDate(result.[[Year]], result.[[Month]], result.[[Day]], calendar).
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+let cal = new Temporal.Calendar("iso8601")
+
+TemporalHelpers.assertPlainDate(
+ cal.dateFromFields({year: 2021, month: 7, day: 15}),
+ 2021, 7, "M07", 15,
+ "year/month/day");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/with-year-monthCode-day-need-constrain.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/with-year-monthCode-day-need-constrain.js
new file mode 100644
index 0000000000..175c34a601
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/with-year-monthCode-day-need-constrain.js
@@ -0,0 +1,80 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.calendar.prototype.datefromfields
+description: Temporal.Calendar.prototype.dateFromFields with year, monthCode and day and need constrain
+info: |
+ 1. Let calendar be the this value.
+ 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
+ 3. Assert: calendar.[[Identifier]] is "iso8601".
+ 4. If Type(fields) is not Object, throw a TypeError exception.
+ 5. Set options to ? GetOptionsObject(options).
+ 6. Let result be ? ISODateFromFields(fields, options).
+ 7. Return ? CreateTemporalDate(result.[[Year]], result.[[Month]], result.[[Day]], calendar).
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+let cal = new Temporal.Calendar("iso8601")
+
+TemporalHelpers.assertPlainDate(
+ cal.dateFromFields({year: 2021, monthCode: "M01", day: 133}),
+ 2021, 1, "M01", 31,
+ "year/monthCode/day with day need to be constrained in Jan");
+
+TemporalHelpers.assertPlainDate(
+ cal.dateFromFields({year: 2021, monthCode: "M02", day: 133}),
+ 2021, 2, "M02", 28,
+ "year/monthCode/day with day need to be constrained in Feb");
+
+TemporalHelpers.assertPlainDate(
+ cal.dateFromFields({year: 2021, monthCode: "M03", day: 133}),
+ 2021, 3, "M03", 31,
+ "year/monthCode/day with day need to be constrained in March");
+
+TemporalHelpers.assertPlainDate(
+ cal.dateFromFields({year: 2021, monthCode: "M04", day: 133}),
+ 2021, 4, "M04", 30,
+ "year/monthCode/day with day need to be constrained in April");
+
+TemporalHelpers.assertPlainDate(
+ cal.dateFromFields({year: 2021, monthCode: "M05", day: 133}),
+ 2021, 5, "M05", 31,
+ "year/monthCode/day with day need to be constrained in May");
+
+TemporalHelpers.assertPlainDate(
+ cal.dateFromFields({year: 2021, monthCode: "M06", day: 133}),
+ 2021, 6, "M06", 30,
+ "year/monthCode/day with day need to be constrained in Jun");
+
+TemporalHelpers.assertPlainDate(
+ cal.dateFromFields({year: 2021, monthCode: "M07", day: 133}),
+ 2021, 7, "M07", 31,
+ "year/monthCode/day with day need to be constrained in July");
+
+TemporalHelpers.assertPlainDate(
+ cal.dateFromFields({year: 2021, monthCode: "M08", day: 133}),
+ 2021, 8, "M08", 31,
+ "year/monthCode/day with day need to be constrained in Aug");
+
+TemporalHelpers.assertPlainDate(
+ cal.dateFromFields({year: 2021, monthCode: "M09", day: 133}),
+ 2021, 9, "M09", 30,
+ "year/monthCode/day with day need to be constrained in Sept.");
+
+TemporalHelpers.assertPlainDate(
+ cal.dateFromFields({year: 2021, monthCode: "M10", day: 133}),
+ 2021, 10, "M10", 31,
+ "year/monthCode/day with day need to be constrained in Oct.");
+
+TemporalHelpers.assertPlainDate(
+ cal.dateFromFields({year: 2021, monthCode: "M11", day: 133}),
+ 2021, 11, "M11", 30,
+ "year/monthCode/day with day need to be constrained in Nov.");
+
+TemporalHelpers.assertPlainDate(
+ cal.dateFromFields({year: 2021, monthCode: "M12", day: 133}),
+ 2021, 12, "M12", 31,
+ "year/monthCode/day with day need to be constrained in Dec.");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/with-year-monthCode-day.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/with-year-monthCode-day.js
new file mode 100644
index 0000000000..ff07ed1935
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateFromFields/with-year-monthCode-day.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.calendar.prototype.datefromfields
+description: Temporal.Calendar.prototype.dateFromFields with year, monthCode and day.
+info: |
+ 1. Let calendar be the this value.
+ 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
+ 3. Assert: calendar.[[Identifier]] is "iso8601".
+ 4. If Type(fields) is not Object, throw a TypeError exception.
+ 5. Set options to ? GetOptionsObject(options).
+ 6. Let result be ? ISODateFromFields(fields, options).
+ 7. Return ? CreateTemporalDate(result.[[Year]], result.[[Month]], result.[[Day]], calendar).
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+let cal = new Temporal.Calendar("iso8601")
+
+TemporalHelpers.assertPlainDate(
+ cal.dateFromFields({year: 2021, monthCode: "M07", day: 15}),
+ 2021, 7, "M07", 15,
+ "year/monthCode/day");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..ef09180179
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-builtin-calendar-no-array-iteration.js
@@ -0,0 +1,25 @@
+// |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.calendar.prototype.dateuntil
+description: >
+ Calling the method with a property bag argument with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.Calendar("iso8601");
+const arg = { year: 2000, month: 5, day: 2, calendar: "iso8601" };
+instance.dateUntil(arg, new Temporal.PlainDate(1977, 11, 19));
+instance.dateUntil(new Temporal.PlainDate(1977, 11, 19), arg);
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-calendar-datefromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..0385c15b1e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-calendar-datefromfields-called-with-null-prototype-fields.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.calendar.prototype.dateuntil
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.Calendar("iso8601");
+const arg1 = { year: 2000, month: 5, day: 2, calendar };
+const arg2 = new Temporal.PlainDate(1977, 11, 19);
+
+instance.dateUntil(arg1, arg2);
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar (first argument)");
+
+calendar.dateFromFieldsCallCount = 0;
+
+instance.dateUntil(arg2, arg1);
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar (second argument)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..1f24241be6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-constructor-in-calendar-fields.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.calendar.prototype.dateuntil
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.Calendar("iso8601");
+
+assert.throws(RangeError, () => instance.dateUntil(arg, new Temporal.PlainDate(1977, 11, 19)));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-duplicate-calendar-fields.js
new file mode 100644
index 0000000000..5becde2c5a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-duplicate-calendar-fields.js
@@ -0,0 +1,20 @@
+// |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.calendar.prototype.dateuntil
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['day'], ['month'], ['monthCode'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar };
+ const instance = new Temporal.Calendar("iso8601");
+
+ assert.throws(RangeError, () => instance.dateUntil(arg, new Temporal.PlainDate(1977, 11, 19)));
+ assert.throws(RangeError, () => instance.dateUntil(new Temporal.PlainDate(1977, 11, 19), arg));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..5123301475
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-infinity-throws-rangeerror.js
@@ -0,0 +1,34 @@
+// |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.
+
+/*---
+description: Throws if any value in a property bag for either argument is Infinity or -Infinity
+esid: sec-temporal.calendar.prototype.dateuntil
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+const other = new Temporal.PlainDate(2001, 6, 3);
+const base = { year: 2000, month: 5, day: 2 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.dateUntil({ ...base, [prop]: inf }, other), `${prop} property cannot be ${inf}`);
+
+ assert.throws(RangeError, () => instance.dateUntil(other, { ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls1 = [];
+ const obj1 = TemporalHelpers.toPrimitiveObserver(calls1, inf, prop);
+ assert.throws(RangeError, () => instance.dateUntil({ ...base, [prop]: obj1 }, other));
+ assert.compareArray(calls1, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+
+ const calls2 = [];
+ const obj2 = TemporalHelpers.toPrimitiveObserver(calls2, inf, prop);
+ assert.throws(RangeError, () => instance.dateUntil(other, { ...base, [prop]: obj2 }));
+ assert.compareArray(calls2, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-number.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-number.js
new file mode 100644
index 0000000000..4a8ba0f55d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-number.js
@@ -0,0 +1,33 @@
+// |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.calendar.prototype.dateuntil
+description: A number cannot be used in place of a Temporal.PlainDate
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const numbers = [
+ 1,
+ 19761118,
+ -19761118,
+ 1234567890,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => instance.dateUntil(arg, new Temporal.PlainDate(1977, 11, 18)),
+ "A number is not a valid ISO string for PlainDate (first argument)"
+ );
+ assert.throws(
+ TypeError,
+ () => instance.dateUntil(new Temporal.PlainDate(1977, 11, 18), arg),
+ "A number is not a valid ISO string for PlainDate (second argument)"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-plaindatetime.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-plaindatetime.js
new file mode 100644
index 0000000000..526b151206
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-plaindatetime.js
@@ -0,0 +1,33 @@
+// |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.calendar.prototype.dateuntil
+description: Fast path for converting Temporal.PlainDateTime to Temporal.PlainDate by reading internal slots
+info: |
+ sec-temporal.calendar.prototype.dateuntil steps 4–5:
+ 4. Set _one_ to ? ToTemporalDate(_one_).
+ 5. Set _two_ to ? ToTemporalDate(_two_).
+ sec-temporal-totemporaldate step 2.b:
+ b. If _item_ has an [[InitializedTemporalDateTime]] internal slot, then
+ i. Return ! CreateTemporalDate(_item_.[[ISOYear]], _item_.[[ISOMonth]], _item_.[[ISODay]], _item_.[[Calendar]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(2000, 5, 2);
+
+TemporalHelpers.checkPlainDateTimeConversionFastPath((datetime) => {
+ const calendar = new Temporal.Calendar("iso8601");
+ const result = calendar.dateUntil(datetime, date);
+ assert.sameValue(result.total({ unit: "nanoseconds" }), 0, "time part dropped");
+});
+
+TemporalHelpers.checkPlainDateTimeConversionFastPath((datetime) => {
+ const calendar = new Temporal.Calendar("iso8601");
+ const result = calendar.dateUntil(date, datetime);
+ assert.sameValue(result.total({ unit: "nanoseconds" }), 0, "time part dropped");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-propertybag-calendar-case-insensitive.js
new file mode 100644
index 0000000000..89b104bb17
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-propertybag-calendar-case-insensitive.js
@@ -0,0 +1,22 @@
+// |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.calendar.prototype.dateuntil
+description: The calendar name is case-insensitive
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const calendar = "IsO8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result1 = instance.dateUntil(arg, new Temporal.PlainDate(1976, 11, 19));
+TemporalHelpers.assertDuration(result1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, "Calendar is case-insensitive (first argument)");
+const result2 = instance.dateUntil(new Temporal.PlainDate(1976, 11, 19), arg);
+TemporalHelpers.assertDuration(result2, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, "Calendar is case-insensitive (second argument)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-propertybag-calendar-leap-second.js
new file mode 100644
index 0000000000..712bf74a46
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-propertybag-calendar-leap-second.js
@@ -0,0 +1,22 @@
+// |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.calendar.prototype.dateuntil
+description: Leap second is a valid ISO string for a calendar in a property bag
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const calendar = "2016-12-31T23:59:60";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result1 = instance.dateUntil(arg, new Temporal.PlainDate(1976, 11, 19));
+TemporalHelpers.assertDuration(result1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, "leap second is a valid ISO string for calendar (first argument)");
+const result2 = instance.dateUntil(new Temporal.PlainDate(1976, 11, 19), arg);
+TemporalHelpers.assertDuration(result2, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, "leap second is a valid ISO string for calendar (second argument)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-propertybag-calendar-number.js
new file mode 100644
index 0000000000..9d2fd6a583
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-propertybag-calendar-number.js
@@ -0,0 +1,34 @@
+// |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.calendar.prototype.dateuntil
+description: A number as calendar in a property bag is not accepted
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const numbers = [
+ 1,
+ 19970327,
+ -19970327,
+ 1234567890,
+];
+
+for (const calendar of numbers) {
+ const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+ assert.throws(
+ TypeError,
+ () => instance.dateUntil(arg, new Temporal.PlainDate(1977, 11, 19)),
+ "A number is not a valid ISO string for calendar (first argument)"
+ );
+ assert.throws(
+ TypeError,
+ () => instance.dateUntil(new Temporal.PlainDate(1977, 11, 19), arg),
+ "A number is not a valid ISO string for calendar (second argument)"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-propertybag-calendar-string.js
new file mode 100644
index 0000000000..037111eaa5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-propertybag-calendar-string.js
@@ -0,0 +1,31 @@
+// |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.calendar.prototype.dateuntil
+description: A calendar ID is valid input for Calendar
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+Object.defineProperty(instance, "dateFromFields", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("dateFromFields should not be looked up on receiver");
+ },
+});
+
+const calendar = "iso8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+
+const result1 = instance.dateUntil(arg, new Temporal.PlainDate(1976, 11, 18));
+TemporalHelpers.assertDuration(result1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, `Calendar created from string "${arg} (first argument)"`);
+
+const result2 = instance.dateUntil(new Temporal.PlainDate(1976, 11, 18), arg);
+TemporalHelpers.assertDuration(result2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, `Calendar created from string "${arg} (second argument)"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-propertybag-calendar-wrong-type.js
new file mode 100644
index 0000000000..245fbf46da
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-propertybag-calendar-wrong-type.js
@@ -0,0 +1,52 @@
+// |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.calendar.prototype.dateuntil
+description: >
+ Appropriate error thrown when a calendar property from a property bag cannot
+ be converted to a calendar object or string
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.Calendar("iso8601");
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [calendar, description] of primitiveTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ typeof calendar === "string" ? RangeError : TypeError,
+ () => instance.dateUntil(arg, new Temporal.PlainDate(1977, 11, 19)),
+ `${description} does not convert to a valid ISO string (first argument)`
+ );
+ assert.throws(
+ typeof calendar === "string" ? RangeError : TypeError,
+ () => instance.dateUntil(new Temporal.PlainDate(1977, 11, 19), arg),
+ `${description} does not convert to a valid ISO string (second argument)`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+ [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields()
+];
+
+for (const [calendar, description] of typeErrorTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(TypeError, () => instance.dateUntil(arg, new Temporal.PlainDate(1977, 11, 19)), `${description} is not a valid property bag and does not convert to a string (first argument)`);
+ assert.throws(TypeError, () => instance.dateUntil(new Temporal.PlainDate(1977, 11, 19), arg), `${description} is not a valid property bag and does not convert to a string (second argument)`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-propertybag-calendar-year-zero.js
new file mode 100644
index 0000000000..117c3e627f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-propertybag-calendar-year-zero.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.calendar.prototype.dateuntil
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T17:45",
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+01:00",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+const instance = new Temporal.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.dateUntil(arg, new Temporal.PlainDate(1977, 11, 19)),
+ "reject minus zero as extended year (first argument)"
+ );
+ assert.throws(
+ RangeError,
+ () => instance.dateUntil(new Temporal.PlainDate(1977, 11, 19), arg),
+ "reject minus zero as extended year (second argument)"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-proto-in-calendar-fields.js
new file mode 100644
index 0000000000..34e8f2c28c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-proto-in-calendar-fields.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.calendar.prototype.dateuntil
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.Calendar("iso8601");
+
+assert.throws(RangeError, () => instance.dateUntil(arg, new Temporal.PlainDate(1977, 11, 19)));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..069f193cc9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-string-calendar-annotation.js
@@ -0,0 +1,34 @@
+// |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.calendar.prototype.dateuntil
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2000-05-02[u-ca=iso8601]", "without time or time zone"],
+ ["2000-05-02[UTC][u-ca=iso8601]", "with time zone and no time"],
+ ["2000-05-02T15:23[u-ca=iso8601]", "without time zone"],
+ ["2000-05-02T15:23[UTC][u-ca=iso8601]", "with time zone"],
+ ["2000-05-02T15:23[!u-ca=iso8601]", "with ! and no time zone"],
+ ["2000-05-02T15:23[UTC][!u-ca=iso8601]", "with ! and time zone"],
+ ["2000-05-02T15:23[u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+const instance = new Temporal.Calendar("iso8601");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.dateUntil(arg, 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/Calendar/prototype/dateUntil/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..97efa5c7e6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-string-critical-unknown-annotation.js
@@ -0,0 +1,33 @@
+// |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.calendar.prototype.dateuntil
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[!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.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.dateUntil(arg, new Temporal.PlainDate(1977, 11, 19)),
+ `reject unknown annotation with critical flag: ${arg} (first argument)`
+ );
+ assert.throws(
+ RangeError,
+ () => instance.dateUntil(new Temporal.PlainDate(1977, 11, 19), arg),
+ `reject unknown annotation with critical flag: ${arg} (second argument)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..47db3e2de7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-string-date-with-utc-offset.js
@@ -0,0 +1,52 @@
+// |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.calendar.prototype.dateuntil
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const validStrings = [
+ "2000-05-02T00+00:00",
+ "2000-05-02T00+00:00[UTC]",
+ "2000-05-02T00+00:00[!UTC]",
+ "2000-05-02T00-02:30[America/St_Johns]",
+];
+
+for (const arg of validStrings) {
+ TemporalHelpers.assertDuration(
+ instance.dateUntil(arg, arg),
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ `"${arg}" is a valid UTC offset with time for PlainDate`
+ );
+}
+
+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.dateUntil(arg, new Temporal.PlainDate(1977, 11, 19)),
+ `"${arg}" UTC offset without time is not valid for PlainDate (first argument)`
+ );
+ assert.throws(
+ RangeError,
+ () => instance.dateUntil(new Temporal.PlainDate(1977, 11, 19), arg),
+ `"${arg}" UTC offset without time is not valid for PlainDate (second argument)`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-string-invalid.js
new file mode 100644
index 0000000000..31c39e90a0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-string-invalid.js
@@ -0,0 +1,70 @@
+// |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.calendar.prototype.dateuntil
+description: >
+ RangeError thrown if an invalid ISO string (or syntactically valid ISO string
+ that is not supported) is used as a PlainDate
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ // invalid ISO strings:
+ "",
+ "invalid iso8601",
+ "2020-01-00",
+ "2020-01-32",
+ "2020-02-30",
+ "2021-02-29",
+ "2020-00-01",
+ "2020-13-01",
+ "2020-01-01T",
+ "2020-01-01T25:00:00",
+ "2020-01-01T01:60:00",
+ "2020-01-01T01:60:61",
+ "2020-01-01junk",
+ "2020-01-01T00:00:00junk",
+ "2020-01-01T00:00:00+00:00junk",
+ "2020-01-01T00:00:00+00:00[UTC]junk",
+ "2020-01-01T00:00:00+00:00[UTC][u-ca=iso8601]junk",
+ "02020-01-01",
+ "2020-001-01",
+ "2020-01-001",
+ "2020-01-01T001",
+ "2020-01-01T01:001",
+ "2020-01-01T01:01:001",
+ // valid, but forms not supported in Temporal:
+ "2020-W01-1",
+ "2020-001",
+ "+0002020-01-01",
+ // valid, but this calendar must not exist:
+ "2020-01-01[u-ca=notexist]",
+ // may be valid in other contexts, but insufficient information for PlainDate:
+ "2020-01",
+ "+002020-01",
+ "01-01",
+ "2020-W01",
+ "P1Y",
+ "-P12Y",
+ // valid, but outside the supported range:
+ "-999999-01-01",
+ "+999999-01-01",
+];
+const instance = new Temporal.Calendar("iso8601");
+const other = new Temporal.PlainDate(2020, 1, 1, instance);
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => instance.dateUntil(arg, other),
+ `"${arg}" should not be a valid ISO string for a PlainDate (first argument)`
+ );
+ assert.throws(
+ RangeError,
+ () => instance.dateUntil(other, arg),
+ `"${arg}" should not be a valid ISO string for a PlainDate (second argument)`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..e8ba951085
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-string-multiple-calendar.js
@@ -0,0 +1,37 @@
+// |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.calendar.prototype.dateuntil
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[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.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.dateUntil(arg, new Temporal.PlainDate(1977, 11, 19)),
+ `reject more than one calendar annotation if any critical: ${arg} (first argument)`
+ );
+ assert.throws(
+ RangeError,
+ () => instance.dateUntil(new Temporal.PlainDate(1977, 11, 19), arg),
+ `reject more than one calendar annotation if any critical: ${arg} (second argument)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..db2cbbf925
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-string-multiple-time-zone.js
@@ -0,0 +1,33 @@
+// |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.calendar.prototype.dateuntil
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[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.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.dateUntil(arg, new Temporal.PlainDate(1977, 11, 19)),
+ `reject more than one time zone annotation: ${arg} (first argument)`
+ );
+ assert.throws(
+ RangeError,
+ () => instance.dateUntil(new Temporal.PlainDate(1977, 11, 19), arg),
+ `reject more than one time zone annotation: ${arg} (second argument)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-string-time-separators.js
new file mode 100644
index 0000000000..06a8f07274
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-string-time-separators.js
@@ -0,0 +1,35 @@
+// |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.calendar.prototype.dateuntil
+description: Time separator in string argument can vary
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const date = new Temporal.PlainDate(2000, 5, 3);
+const tests = [
+ ["2000-05-02T15:23", "uppercase T"],
+ ["2000-05-02t15:23", "lowercase T"],
+ ["2000-05-02 15:23", "space between date and time"],
+];
+
+const instance = new Temporal.Calendar("iso8601");
+
+tests.forEach(([arg, description]) => {
+ TemporalHelpers.assertDuration(
+ instance.dateUntil(arg, date),
+ 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
+ `variant time separators (${description}), first argument`
+ );
+
+ TemporalHelpers.assertDuration(
+ instance.dateUntil(date, arg),
+ 0, 0, 0, -1, 0, 0, 0, 0, 0, 0,
+ `variant time separators (${description}), second argument`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..66159880e5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-string-time-zone-annotation.js
@@ -0,0 +1,39 @@
+// |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.calendar.prototype.dateuntil
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ['2000-05-02[Asia/Kolkata]', 'named, with no time'],
+ ['2000-05-02[!Europe/Vienna]', 'named, with ! and no time'],
+ ['2000-05-02[+00:00]', 'numeric, with no time'],
+ ['2000-05-02[!-02:30]', 'numeric, with ! and no time'],
+ ["2000-05-02T15:23[America/Sao_Paulo]", "named, with no offset"],
+ ["2000-05-02T15:23[!Asia/Tokyo]", "named, with ! and no offset"],
+ ["2000-05-02T15:23[-02:30]", "numeric, with no offset"],
+ ["2000-05-02T15:23[!+00:00]", "numeric, with ! and no offset"],
+ ["2000-05-02T15:23+00:00[America/New_York]", "named, with offset"],
+ ["2000-05-02T15:23+00:00[!UTC]", "named, with offset and !"],
+ ["2000-05-02T15:23+00:00[+01:00]", "numeric, with offset"],
+ ["2000-05-02T15:23+00:00[!-08:00]", "numeric, with offset and !"],
+];
+
+const instance = new Temporal.Calendar("iso8601");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.dateUntil(arg, 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/Calendar/prototype/dateUntil/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..5f151e4bf8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-string-unknown-annotation.js
@@ -0,0 +1,33 @@
+// |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.calendar.prototype.dateuntil
+description: Various forms of unknown annotation
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2000-05-02[foo=bar]", "without time"],
+ ["2000-05-02T15:23[foo=bar]", "alone"],
+ ["2000-05-02T15:23[UTC][foo=bar]", "with time zone"],
+ ["2000-05-02T15:23[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["2000-05-02T15:23[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["2000-05-02T15:23[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+const instance = new Temporal.Calendar("iso8601");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.dateUntil(arg, 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/Calendar/prototype/dateUntil/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..2a7ae86b76
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-string-with-utc-designator.js
@@ -0,0 +1,30 @@
+// |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.calendar.prototype.dateuntil
+description: RangeError thrown if a string with UTC designator is used as a PlainDate
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "2019-10-01T09:00:00Z",
+ "2019-10-01T09:00:00Z[UTC]",
+];
+const instance = new Temporal.Calendar("iso8601");
+const plainDate = new Temporal.PlainDate(2000, 5, 2);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.dateUntil(arg, plainDate),
+ "String with UTC designator should not be valid as a PlainDate (first argument)"
+ );
+ assert.throws(
+ RangeError,
+ () => instance.dateUntil(plainDate, arg),
+ "String with UTC designator should not be valid as a PlainDate (second argument)"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-wrong-type.js
new file mode 100644
index 0000000000..da9c257fc0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-wrong-type.js
@@ -0,0 +1,50 @@
+// |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.calendar.prototype.dateuntil
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainDate
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.Calendar("iso8601");
+
+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.dateUntil(arg, new Temporal.PlainDate(1977, 11, 19)),
+ `${description} does not convert to a valid ISO string (first argument)`
+ );
+ assert.throws(
+ typeof arg === 'string' ? RangeError : TypeError,
+ () => instance.dateUntil(new Temporal.PlainDate(1977, 11, 19), arg),
+ `${description} does not convert to a valid ISO string (second argument)`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.PlainDate, "Temporal.PlainDate, object"],
+ [Temporal.PlainDate.prototype, "Temporal.PlainDate.prototype, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.dateUntil(arg, new Temporal.PlainDate(1977, 11, 19)), `${description} is not a valid property bag and does not convert to a string (first argument)`);
+ assert.throws(TypeError, () => instance.dateUntil(new Temporal.PlainDate(1977, 11, 19), arg), `${description} is not a valid property bag and does not convert to a string (second argument)`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-zoneddatetime-slots.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-zoneddatetime-slots.js
new file mode 100644
index 0000000000..6a09cb6ed5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-zoneddatetime-slots.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.calendar.prototype.dateuntil
+description: Getters are not called when converting a ZonedDateTime to a PlainDate.
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.ZonedDateTime.prototype);
+const getters = ["year", "month", "monthCode", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond", "calendar"];
+
+for (const property of getters) {
+ Object.defineProperty(Temporal.ZonedDateTime.prototype, property, {
+ get() {
+ actual.push(`get ${property}`);
+ const value = prototypeDescrs[property].get.call(this);
+ return {
+ toString() {
+ actual.push(`toString ${property}`);
+ return value.toString();
+ },
+ valueOf() {
+ actual.push(`valueOf ${property}`);
+ return value;
+ },
+ };
+ },
+ });
+}
+
+const arg = new Temporal.ZonedDateTime(0n, "UTC");
+const instance = new Temporal.Calendar("iso8601");
+instance.dateUntil(arg, new Temporal.PlainDate(1977, 11, 19));
+instance.dateUntil(new Temporal.PlainDate(1977, 11, 19), arg);
+assert.compareArray(actual, []);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..70c9fc1a12
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.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.calendar.prototype.dateuntil
+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 calendar = new Temporal.Calendar("iso8601");
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+
+ assert.throws(RangeError, () => calendar.dateUntil(datetime, date));
+ assert.throws(RangeError, () => calendar.dateUntil(date, datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..ebc1ebbf01
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
@@ -0,0 +1,30 @@
+// |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.calendar.prototype.dateuntil
+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 calendar = new Temporal.Calendar("iso8601");
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+
+ assert.throws(
+ TypeError,
+ () => calendar.dateUntil(datetime, date),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+ assert.throws(
+ TypeError,
+ () => calendar.dateUntil(date, datetime),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..ff81410145
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.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.calendar.prototype.dateuntil
+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 calendar = new Temporal.Calendar("iso8601");
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+
+ assert.throws(RangeError, () => calendar.dateUntil(datetime, date));
+ assert.throws(RangeError, () => calendar.dateUntil(date, datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..446a1a6ea3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,31 @@
+// |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.calendar.prototype.dateuntil
+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 calendar = new Temporal.Calendar("iso8601");
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+
+ assert.throws(TypeError, () => calendar.dateUntil(datetime, date));
+ assert.throws(TypeError, () => calendar.dateUntil(date, datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/basic.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/basic.js
new file mode 100644
index 0000000000..99f4e9059b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/basic.js
@@ -0,0 +1,48 @@
+// |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.calendar.prototype.dateuntil
+description: Basic tests for dateUntil().
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const iso = Temporal.Calendar.from("iso8601");
+const date1 = Temporal.PlainDate.from("1999-09-03");
+const date2 = Temporal.PlainDate.from("2000-01-01");
+
+TemporalHelpers.assertDuration(
+ iso.dateUntil(date1, date2, {}),
+ 0, 0, 0, 120, 0, 0, 0, 0, 0, 0, "two PlainDates");
+
+TemporalHelpers.assertDuration(
+ iso.dateUntil(Temporal.PlainDateTime.from("1999-09-03T08:15:30"), date2, {}),
+ 0, 0, 0, 120, 0, 0, 0, 0, 0, 0, "first argument: PlainDateTime");
+
+TemporalHelpers.assertDuration(
+ iso.dateUntil({ year: 1999, month: 9, day: 3 }, date2, {}),
+ 0, 0, 0, 120, 0, 0, 0, 0, 0, 0, "first argument: property bag");
+
+TemporalHelpers.assertDuration(
+ iso.dateUntil("1999-09-03", date2, {}),
+ 0, 0, 0, 120, 0, 0, 0, 0, 0, 0, "first argument: string");
+
+assert.throws(TypeError, () => iso.dateUntil({ month: 11 }, date2, {}), "first argument: missing property");
+
+TemporalHelpers.assertDuration(
+ iso.dateUntil(date1, Temporal.PlainDateTime.from("2000-01-01T08:15:30"), {}),
+ 0, 0, 0, 120, 0, 0, 0, 0, 0, 0, "second argument: PlainDateTime");
+
+TemporalHelpers.assertDuration(
+ iso.dateUntil(date1, { year: 2000, month: 1, day: 1 }, {}),
+ 0, 0, 0, 120, 0, 0, 0, 0, 0, 0, "second argument: property bag");
+
+TemporalHelpers.assertDuration(
+ iso.dateUntil(date1, "2000-01-01", {}),
+ 0, 0, 0, 120, 0, 0, 0, 0, 0, 0, "second argument: string");
+
+assert.throws(TypeError, () => iso.dateUntil(date1, { month: 11 }, {}), "second argument: missing property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/branding.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/branding.js
new file mode 100644
index 0000000000..ea4dec80e6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/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.calendar.prototype.dateuntil
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const dateUntil = Temporal.Calendar.prototype.dateUntil;
+
+assert.sameValue(typeof dateUntil, "function");
+
+const args = [new Temporal.PlainDate(2021, 7, 16), new Temporal.PlainDate(2021, 7, 17)];
+
+assert.throws(TypeError, () => dateUntil.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => dateUntil.apply(null, args), "null");
+assert.throws(TypeError, () => dateUntil.apply(true, args), "true");
+assert.throws(TypeError, () => dateUntil.apply("", args), "empty string");
+assert.throws(TypeError, () => dateUntil.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => dateUntil.apply(1, args), "1");
+assert.throws(TypeError, () => dateUntil.apply({}, args), "plain object");
+assert.throws(TypeError, () => dateUntil.apply(Temporal.Calendar, args), "Temporal.Calendar");
+assert.throws(TypeError, () => dateUntil.apply(Temporal.Calendar.prototype, args), "Temporal.Calendar.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/browser.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/builtin.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/builtin.js
new file mode 100644
index 0000000000..9cc12208df
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/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.calendar.prototype.dateuntil
+description: >
+ Tests that Temporal.Calendar.prototype.dateUntil
+ 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.Calendar.prototype.dateUntil),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Calendar.prototype.dateUntil),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Calendar.prototype.dateUntil),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Calendar.prototype.dateUntil.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/calendar-datefromfields-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/calendar-datefromfields-called-with-options-undefined.js
new file mode 100644
index 0000000000..cc19ddb323
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/calendar-datefromfields-called-with-options-undefined.js
@@ -0,0 +1,18 @@
+// |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.calendar.prototype.dateuntil
+description: >
+ Calendar.dateFromFields method is called with undefined as the options value
+ when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
+calendar.dateUntil({ year: 2000, month: 5, day: 2, calendar }, { year: 2000, month: 5, day: 3, calendar });
+assert.sameValue(calendar.dateFromFieldsCallCount, 2);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/calendar-fields-iterable.js
new file mode 100644
index 0000000000..dc32b6b936
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/calendar-fields-iterable.js
@@ -0,0 +1,43 @@
+// |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.calendar.prototype.dateuntil
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.calendar.prototype.dateuntil steps 4–5:
+ 4. Set _one_ to ? ToTemporalDate(_one_).
+ 5. Set _two_ to ? ToTemporalDate(_two_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToListOfType(_fieldsArray_, « String »).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+const calendar3 = TemporalHelpers.calendarFieldsIterable();
+calendar1.dateUntil(
+ { year: 2000, month: 5, day: 2, calendar: calendar2 },
+ { year: 2005, month: 6, day: 3, calendar: calendar3 },
+);
+
+assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.sameValue(calendar3.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
+assert.compareArray(calendar3.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar3.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/calendar-temporal-object.js
new file mode 100644
index 0000000000..a42cf66f0a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/calendar-temporal-object.js
@@ -0,0 +1,33 @@
+// |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.calendar.prototype.dateuntil
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.calendar.prototype.dateuntil steps 4–5:
+ 4. Set _one_ to ? ToTemporalDate(_one_).
+ 5. Set _two_ to ? ToTemporalDate(_two_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const calendar = new Temporal.Calendar("iso8601");
+ calendar.dateUntil(
+ { year: 2000, month: 5, day: 2, calendar: temporalObject },
+ { year: 2005, month: 6, day: 3, calendar: temporalObject },
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/largest-unit-day.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/largest-unit-day.js
new file mode 100644
index 0000000000..5d5aaecbb9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/largest-unit-day.js
@@ -0,0 +1,62 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dateuntil
+description: Temporal.Calendar.prototype.dateUntil with largestUnit is "day"
+info: |
+ 1. Let calendar be the this value.
+ 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
+ 3. Assert: calendar.[[Identifier]] is "iso8601".
+ 4. Set one to ? ToTemporalDate(one).
+ 5. Set two to ? ToTemporalDate(two).
+ 6. Set options to ? GetOptionsObject(options).
+ 7. Let largestUnit be ? ToLargestTemporalUnit(options, « "hour", "minute", "second", "millisecond", "microsecond", "nanosecond" », "auto", "day").
+ 8. Let result be ! DifferenceISODate(one.[[ISOYear]], one.[[ISOMonth]], one.[[ISODay]], two.[[ISOYear]], two.[[ISOMonth]], two.[[ISODay]], largestUnit).
+ 9. Return ? CreateTemporalDuration(result.[[Years]], result.[[Months]], result.[[Weeks]], result.[[Days]], 0, 0, 0, 0, 0, 0).
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+let cal = new Temporal.Calendar("iso8601");
+
+["day", "days"].forEach(function(largestUnit) {
+ let opt = {largestUnit};
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-07-16", "2021-07-16", opt),
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "same day");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-07-16", "2021-07-17", opt),
+ 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, "one day");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-07-16", "2021-08-17", opt),
+ 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, "32 days");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-07-16", "2021-09-16", opt),
+ 0, 0, 0, 62, 0, 0, 0, 0, 0, 0, "62 days");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-07-16", "2022-07-16", opt),
+ 0, 0, 0, 365, 0, 0, 0, 0, 0, 0, "365 days");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-07-16", "2031-07-16", opt),
+ 0, 0, 0, 3652, 0, 0, 0, 0, 0, 0, "3652 days");
+
+
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-07-17", "2021-07-16", opt),
+ 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, "negative one day");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-08-17", "2021-07-16", opt),
+ 0, 0, 0, -32, 0, 0, 0, 0, 0, 0, "negative 32 days");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-09-16", "2021-07-16", opt),
+ 0, 0, 0, -62, 0, 0, 0, 0, 0, 0, "negative 62 days");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2022-07-16", "2021-07-16", opt),
+ 0, 0, 0, -365, 0, 0, 0, 0, 0, 0, "negative 365 days");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2031-07-16", "2021-07-16", opt),
+ 0, 0, 0, -3652, 0, 0, 0, 0, 0, 0, "negative 3652 days");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/largest-unit-month.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/largest-unit-month.js
new file mode 100644
index 0000000000..127391dc73
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/largest-unit-month.js
@@ -0,0 +1,97 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dateuntil
+description: Temporal.Calendar.prototype.dateUntil with largestUnit is "month"
+info: |
+ 1. Let calendar be the this value.
+ 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
+ 3. Assert: calendar.[[Identifier]] is "iso8601".
+ 4. Set one to ? ToTemporalDate(one).
+ 5. Set two to ? ToTemporalDate(two).
+ 6. Set options to ? GetOptionsObject(options).
+ 7. Let largestUnit be ? ToLargestTemporalUnit(options, « "hour", "minute", "second", "millisecond", "microsecond", "nanosecond" », "auto", "day").
+ 8. Let result be ! DifferenceISODate(one.[[ISOYear]], one.[[ISOMonth]], one.[[ISODay]], two.[[ISOYear]], two.[[ISOMonth]], two.[[ISODay]], largestUnit).
+ 9. Return ? CreateTemporalDuration(result.[[Years]], result.[[Months]], result.[[Weeks]], result.[[Days]], 0, 0, 0, 0, 0, 0).
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+let cal = new Temporal.Calendar("iso8601");
+
+["month", "months"].forEach(function(largestUnit) {
+ let opt = {largestUnit};
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-07-16", "2021-07-16", opt),
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "same day");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-07-16", "2021-07-17", opt),
+ 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, "one day");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-07-16", "2021-07-23", opt),
+ 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, "7 days");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-07-16", "2021-08-16", opt),
+ 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, "1 month in same year");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2020-12-16", "2021-01-16", opt),
+ 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, "1 month in different year");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-01-05", "2021-02-05", opt),
+ 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, "1 month in same year");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-01-07", "2021-03-07", opt),
+ 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, "2 month in same year across Feb 28");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-07-16", "2021-08-17", opt),
+ 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, "1 month and 1 day in a month with 31 days");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-07-16", "2021-08-13", opt),
+ 0, 0, 0, 28, 0, 0, 0, 0, 0, 0, "28 days roll across a month which has 31 days");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-07-16", "2021-09-16", opt),
+ 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, "2 months with both months which have 31 days");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-07-16", "2022-07-16", opt),
+ 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, "12 months");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-07-16", "2031-07-16", opt),
+ 0, 120, 0, 0, 0, 0, 0, 0, 0, 0, "120 months");
+
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-07-17", "2021-07-16", opt),
+ 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, "negative one day");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-07-23", "2021-07-16", opt),
+ 0, 0, 0, -7, 0, 0, 0, 0, 0, 0, "negative 7 days");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-08-16", "2021-07-16", opt),
+ 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, "negative 1 month in same year");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-01-16", "2020-12-16", opt),
+ 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, "negative 1 month in different year");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-02-05", "2021-01-05", opt),
+ 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, "negative 1 month in same year");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-03-07", "2021-01-07", opt),
+ 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, "negative 2 month in same year across Feb 28");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-08-17", "2021-07-16", opt),
+ 0, -1, 0, -1, 0, 0, 0, 0, 0, 0, "negative 1 month and 1 day in a month with 31 days");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-08-13", "2021-07-16", opt),
+ 0, 0, 0, -28, 0, 0, 0, 0, 0, 0, "negative 28 days roll across a month which has 31 days");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-09-16", "2021-07-16", opt),
+ 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, "negative 2 months with both months which have 31 days");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2022-07-16", "2021-07-16", opt),
+ 0, -12, 0, 0, 0, 0, 0, 0, 0, 0, "negative 12 months");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2031-07-16", "2021-07-16", opt),
+ 0, -120, 0, 0, 0, 0, 0, 0, 0, 0, "negative 120 months");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/largest-unit-week.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/largest-unit-week.js
new file mode 100644
index 0000000000..c3bf258482
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/largest-unit-week.js
@@ -0,0 +1,74 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dateuntil
+description: Temporal.Calendar.prototype.dateUntil with largestUnit is "week"
+info: |
+ 1. Let calendar be the this value.
+ 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
+ 3. Assert: calendar.[[Identifier]] is "iso8601".
+ 4. Set one to ? ToTemporalDate(one).
+ 5. Set two to ? ToTemporalDate(two).
+ 6. Set options to ? GetOptionsObject(options).
+ 7. Let largestUnit be ? ToLargestTemporalUnit(options, « "hour", "minute", "second", "millisecond", "microsecond", "nanosecond" », "auto", "day").
+ 8. Let result be ! DifferenceISODate(one.[[ISOYear]], one.[[ISOMonth]], one.[[ISODay]], two.[[ISOYear]], two.[[ISOMonth]], two.[[ISODay]], largestUnit).
+ 9. Return ? CreateTemporalDuration(result.[[Years]], result.[[Months]], result.[[Weeks]], result.[[Days]], 0, 0, 0, 0, 0, 0).
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+let cal = new Temporal.Calendar("iso8601");
+
+["week", "weeks"].forEach(function(largestUnit) {
+ let opt = {largestUnit};
+
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-07-16", "2021-07-16", opt),
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "same day");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-07-16", "2021-07-17", opt),
+ 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, "one day");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-07-16", "2021-07-23", opt),
+ 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, "7 days");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-07-16", "2021-08-16", opt),
+ 0, 0, 4, 3, 0, 0, 0, 0, 0, 0, "4 weeks and 3 days");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-07-16", "2021-08-13", opt),
+ 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, "4 weeks");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-07-16", "2021-09-16", opt),
+ 0, 0, 8, 6, 0, 0, 0, 0, 0, 0, "8 weeks and 6 days");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-07-16", "2022-07-16", opt),
+ 0, 0, 52, 1, 0, 0, 0, 0, 0, 0, "52 weeks and 1 day");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-07-16", "2031-07-16", opt),
+ 0, 0, 521, 5, 0, 0, 0, 0, 0, 0, "521 weeks and 5 days");
+
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-07-17", "2021-07-16", opt),
+ 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, "negative one day");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-07-23", "2021-07-16", opt),
+ 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, "negative 7 days");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-08-16", "2021-07-16", opt),
+ 0, 0, -4, -3, 0, 0, 0, 0, 0, 0, "negative 4 weeks and 3 days");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-08-13", "2021-07-16", opt),
+ 0, 0, -4, 0, 0, 0, 0, 0, 0, 0, "negative 4 weeks");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-09-16", "2021-07-16", opt),
+ 0, 0, -8, -6, 0, 0, 0, 0, 0, 0, "negative 8 weeks and 6 days");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2022-07-16", "2021-07-16", opt),
+ 0, 0, -52, -1, 0, 0, 0, 0, 0, 0, "negative 52 weeks and 1 day");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2031-07-16", "2021-07-16", opt),
+ 0, 0, -521, -5, 0, 0, 0, 0, 0, 0, "negative 521 weeks and 5 days");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/largest-unit-year.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/largest-unit-year.js
new file mode 100644
index 0000000000..fb29a8c493
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/largest-unit-year.js
@@ -0,0 +1,207 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dateuntil
+description: Temporal.Calendar.prototype.dateUntil with largestUnit is "year"
+info: |
+ 1. Let calendar be the this value.
+ 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
+ 3. Assert: calendar.[[Identifier]] is "iso8601".
+ 4. Set one to ? ToTemporalDate(one).
+ 5. Set two to ? ToTemporalDate(two).
+ 6. Set options to ? GetOptionsObject(options).
+ 7. Let largestUnit be ? ToLargestTemporalUnit(options, « "hour", "minute", "second", "millisecond", "microsecond", "nanosecond" », "auto", "day").
+ 8. Let result be ! DifferenceISODate(one.[[ISOYear]], one.[[ISOMonth]], one.[[ISODay]], two.[[ISOYear]], two.[[ISOMonth]], two.[[ISODay]], largestUnit).
+ 9. Return ? CreateTemporalDuration(result.[[Years]], result.[[Months]], result.[[Weeks]], result.[[Days]], 0, 0, 0, 0, 0, 0).
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+let cal = new Temporal.Calendar("iso8601");
+
+["year", "years"].forEach(function(largestUnit) {
+ let opt = {largestUnit};
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-07-16", "2021-07-16", opt),
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "same day");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-07-16", "2021-07-17", opt),
+ 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, "one day");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-07-16", "2021-07-23", opt),
+ 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, "7 days");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-07-16", "2021-08-16", opt),
+ 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, "1 month in same year");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2020-12-16", "2021-01-16", opt),
+ 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, "1 month in different year");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-01-05", "2021-02-05", opt),
+ 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, "1 month in same year");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-01-07", "2021-03-07", opt),
+ 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, "2 month in same year across Feb 28");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-07-16", "2021-08-17", opt),
+ 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, "1 month and 1 day in a month with 31 days");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-07-16", "2021-08-13", opt),
+ 0, 0, 0, 28, 0, 0, 0, 0, 0, 0, "28 days roll across a month which has 31 days");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-07-16", "2021-09-16", opt),
+ 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, "2 months with both months which have 31 days");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-07-16", "2022-07-16", opt),
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "1 year");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-07-16", "2031-07-16", opt),
+ 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, "10 years");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-07-16", "2022-07-19", opt),
+ 1, 0, 0, 3, 0, 0, 0, 0, 0, 0, "1 year and 3 days");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-07-16", "2022-09-19", opt),
+ 1, 2, 0, 3, 0, 0, 0, 0, 0, 0, "1 year 2 months and 3 days");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-07-16", "2031-12-16", opt),
+ 10, 5, 0, 0, 0, 0, 0, 0, 0, 0, "10 years and 5 months");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("1997-12-16", "2021-07-16", opt),
+ 23, 7, 0, 0, 0, 0, 0, 0, 0, 0, "23 years and 7 months");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("1997-07-16", "2021-07-16", opt),
+ 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, "24 years");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("1997-07-16", "2021-07-15", opt),
+ 23, 11, 0, 29, 0, 0, 0, 0, 0, 0, "23 years, 11 months and 29 days");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("1997-06-16", "2021-06-15", opt),
+ 23, 11, 0, 30, 0, 0, 0, 0, 0, 0, "23 years, 11 months and 30 days");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("1960-02-16", "2020-03-16", opt),
+ 60, 1, 0, 0, 0, 0, 0, 0, 0, 0, "60 years, 1 month");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("1960-02-16", "2021-03-15", opt),
+ 61, 0, 0, 27, 0, 0, 0, 0, 0, 0, "61 years, 27 days in non leap year");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("1960-02-16", "2020-03-15", opt),
+ 60, 0, 0, 28, 0, 0, 0, 0, 0, 0, "60 years, 28 days in leap year");
+
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-03-30", "2021-07-16", opt),
+ 0, 3, 0, 16, 0, 0, 0, 0, 0, 0, "3 months and 16 days");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2020-03-30", "2021-07-16", opt),
+ 1, 3, 0, 16, 0, 0, 0, 0, 0, 0, "1 year, 3 months and 16 days");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("1960-03-30", "2021-07-16", opt),
+ 61, 3, 0, 16, 0, 0, 0, 0, 0, 0, "61 years, 3 months and 16 days");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2019-12-30", "2021-07-16", opt),
+ 1, 6, 0, 16, 0, 0, 0, 0, 0, 0, "1 year, 6 months and 16 days");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2020-12-30", "2021-07-16", opt),
+ 0, 6, 0, 16, 0, 0, 0, 0, 0, 0, "6 months and 16 days");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("1997-12-30", "2021-07-16", opt),
+ 23, 6, 0, 16, 0, 0, 0, 0, 0, 0, "23 years, 6 months and 16 days");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("0001-12-25", "2021-07-16", opt),
+ 2019, 6, 0, 21, 0, 0, 0, 0, 0, 0, "2019 years, 6 months and 21 days");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2019-12-30", "2021-03-05", opt),
+ 1, 2, 0, 5, 0, 0, 0, 0, 0, 0, "1 year, 2 months and 5 days");
+
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-07-17", "2021-07-16", opt),
+ 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, "negative one day");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-07-23", "2021-07-16", opt),
+ 0, 0, 0, -7, 0, 0, 0, 0, 0, 0, "negative 7 days");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-08-16", "2021-07-16", opt),
+ 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, "negative 1 month in same year");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-01-16", "2020-12-16", opt),
+ 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, "negative 1 month in different year");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-02-05", "2021-01-05", opt),
+ 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, "negative 1 month in same year");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-03-07", "2021-01-07", opt),
+ 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, "negative 2 month in same year across Feb 28");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-08-17", "2021-07-16", opt),
+ 0, -1, 0, -1, 0, 0, 0, 0, 0, 0, "negative 1 month and 1 day in a month with 31 days");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-08-13", "2021-07-16", opt),
+ 0, 0, 0, -28, 0, 0, 0, 0, 0, 0, "negative 28 days roll across a month which has 31 days");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-09-16", "2021-07-16", opt),
+ 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, "negative 2 months with both months which have 31 days");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2022-07-16", "2021-07-16", opt),
+ -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "negative 1 year");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2031-07-16", "2021-07-16", opt),
+ -10, 0, 0, 0, 0, 0, 0, 0, 0, 0, "negative 10 years");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2022-07-19", "2021-07-16", opt),
+ -1, 0, 0, -3, 0, 0, 0, 0, 0, 0, "negative 1 year and 3 days");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2022-09-19", "2021-07-16", opt),
+ -1, -2, 0, -3, 0, 0, 0, 0, 0, 0, "negative 1 year 2 months and 3 days");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2031-12-16", "2021-07-16", opt),
+ -10, -5, 0, 0, 0, 0, 0, 0, 0, 0, "negative 10 years and 5 months");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-07-16", "1997-12-16", opt),
+ -23, -7, 0, 0, 0, 0, 0, 0, 0, 0, "negative 23 years and 7 months");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-07-16", "1997-07-16", opt),
+ -24, 0, 0, 0, 0, 0, 0, 0, 0, 0, "negative 24 years");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-07-15", "1997-07-16", opt),
+ -23, -11, 0, -30, 0, 0, 0, 0, 0, 0, "negative 23 years, 11 months and 30 days");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-06-15", "1997-06-16", opt),
+ -23, -11, 0, -29, 0, 0, 0, 0, 0, 0, "negative 23 years, 11 months and 29 days");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2020-03-16", "1960-02-16", opt),
+ -60, -1, 0, 0, 0, 0, 0, 0, 0, 0, "negative 60 years, 1 month");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-03-15", "1960-02-16", opt),
+ -61, 0, 0, -28, 0, 0, 0, 0, 0, 0, "negative 61 years, 28 days in non leap year");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2020-03-15", "1960-02-16", opt),
+ -60, 0, 0, -28, 0, 0, 0, 0, 0, 0, "negative 60 years, 28 days in leap year");
+
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-07-16", "2021-03-30", opt),
+ 0, -3, 0, -17, 0, 0, 0, 0, 0, 0, "negative 3 months and 17 days");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-07-16", "2020-03-30", opt),
+ -1, -3, 0, -17, 0, 0, 0, 0, 0, 0, "negative 1 year, 3 months and 17 days");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-07-16", "1960-03-30", opt),
+ -61, -3, 0, -17, 0, 0, 0, 0, 0, 0, "negative 61 years, 3 months and 17 days");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-07-16", "2019-12-30", opt),
+ -1, -6, 0, -17, 0, 0, 0, 0, 0, 0, "negative 1 year, 6 months and 17 days");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-07-16", "2020-12-30", opt),
+ 0, -6, 0, -17, 0, 0, 0, 0, 0, 0, "negative 6 months and 17 days");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-07-16", "1997-12-30", opt),
+ -23, -6, 0, -17, 0, 0, 0, 0, 0, 0, "negative 23 years, 6 months and 17 days");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-07-16", "0001-12-25", opt),
+ -2019, -6, 0, -22, 0, 0, 0, 0, 0, 0, "negative 2019 years, 6 months and 22 days");
+ TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-03-05", "2019-12-30", opt),
+ -1, -2, 0, -6, 0, 0, 0, 0, 0, 0, "negative 1 year, 2 months and 6 days");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/largestunit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/largestunit-plurals-accepted.js
new file mode 100644
index 0000000000..097b975550
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/largestunit-plurals-accepted.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.calendar.prototype.dateuntil
+description: Plural units are accepted as well for the largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2001, 6, 12);
+const calendar = new Temporal.Calendar("iso8601");
+const validUnits = [
+ "year",
+ "month",
+ "week",
+ "day",
+];
+TemporalHelpers.checkPluralUnitsAccepted((largestUnit) => calendar.dateUntil(earlier, later, { largestUnit }), validUnits);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/leap-second.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/leap-second.js
new file mode 100644
index 0000000000..e40554c226
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/leap-second.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.calendar.prototype.dateuntil
+description: Leap second is a valid ISO string for PlainDate
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+let arg = "2016-12-31T23:59:60";
+let result = instance.dateUntil(arg, new Temporal.PlainDate(2017, 1, 1));
+TemporalHelpers.assertDuration(result, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, "leap second is a valid ISO string for PlainDate (first argument)");
+result = instance.dateUntil(new Temporal.PlainDate(2017, 1, 1), arg);
+TemporalHelpers.assertDuration(result, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, "leap second is a valid ISO string for PlainDate (second argument)");
+
+arg = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+result = instance.dateUntil(arg, new Temporal.PlainDate(2017, 1, 1));
+TemporalHelpers.assertDuration(result, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, "second: 60 is ignored in property bag for PlainDate (first argument)");
+result = instance.dateUntil(new Temporal.PlainDate(2017, 1, 1), arg);
+TemporalHelpers.assertDuration(result, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, "second: 60 is ignored in property bag for PlainDate (second argument)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/length.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/length.js
new file mode 100644
index 0000000000..03ccddfc49
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/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.calendar.prototype.dateuntil
+description: Temporal.Calendar.prototype.dateUntil.length is 2
+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.Calendar.prototype.dateUntil, "length", {
+ value: 2,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/name.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/name.js
new file mode 100644
index 0000000000..653e2aee58
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/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.calendar.prototype.dateuntil
+description: Temporal.Calendar.prototype.dateUntil.name is "dateUntil".
+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.Calendar.prototype.dateUntil, "name", {
+ value: "dateUntil",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/no-options.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/no-options.js
new file mode 100644
index 0000000000..76e14a3ca2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/no-options.js
@@ -0,0 +1,59 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dateuntil
+description: Temporal.Calendar.prototype.dateUntil with no options
+info: |
+ 1. Let calendar be the this value.
+ 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
+ 3. Assert: calendar.[[Identifier]] is "iso8601".
+ 4. Set one to ? ToTemporalDate(one).
+ 5. Set two to ? ToTemporalDate(two).
+ 6. Set options to ? GetOptionsObject(options).
+ 7. Let largestUnit be ? ToLargestTemporalUnit(options, « "hour", "minute", "second", "millisecond", "microsecond", "nanosecond" », "auto", "day").
+ 8. Let result be ! DifferenceISODate(one.[[ISOYear]], one.[[ISOMonth]], one.[[ISODay]], two.[[ISOYear]], two.[[ISOMonth]], two.[[ISODay]], largestUnit).
+ 9. Return ? CreateTemporalDuration(result.[[Years]], result.[[Months]], result.[[Weeks]], result.[[Days]], 0, 0, 0, 0, 0, 0).
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+let cal = new Temporal.Calendar("iso8601");
+
+TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-07-16", "2021-07-16"),
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "same day");
+TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-07-16", "2021-07-17"),
+ 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, "one day");
+TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-07-16", "2021-08-17"),
+ 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, "32 days");
+TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-07-16", "2021-09-16"),
+ 0, 0, 0, 62, 0, 0, 0, 0, 0, 0, "62 days");
+TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-07-16", "2022-07-16"),
+ 0, 0, 0, 365, 0, 0, 0, 0, 0, 0, "365 days");
+TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-07-16", "2031-07-16"),
+ 0, 0, 0, 3652, 0, 0, 0, 0, 0, 0, "3652 days");
+
+
+TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-07-17", "2021-07-16"),
+ 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, "negative one day");
+TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-08-17", "2021-07-16"),
+ 0, 0, 0, -32, 0, 0, 0, 0, 0, 0, "negative 32 days");
+TemporalHelpers.assertDuration(
+ cal.dateUntil("2021-09-16", "2021-07-16"),
+ 0, 0, 0, -62, 0, 0, 0, 0, 0, 0, "negative 62 days");
+TemporalHelpers.assertDuration(
+ cal.dateUntil("2022-07-16", "2021-07-16"),
+ 0, 0, 0, -365, 0, 0, 0, 0, 0, 0, "negative 365 days");
+TemporalHelpers.assertDuration(
+ cal.dateUntil("2031-07-16", "2021-07-16"),
+ 0, 0, 0, -3652, 0, 0, 0, 0, 0, 0, "negative 3652 days");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/not-a-constructor.js
new file mode 100644
index 0000000000..af2b9d1165
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/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.calendar.prototype.dateuntil
+description: >
+ Temporal.Calendar.prototype.dateUntil 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.Calendar.prototype.dateUntil();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Calendar.prototype.dateUntil), false,
+ "isConstructor(Temporal.Calendar.prototype.dateUntil)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/options-object.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/options-object.js
new file mode 100644
index 0000000000..003988ef80
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/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.calendar.prototype.dateuntil
+description: Empty or a function object may be used as options
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const result1 = instance.dateUntil(new Temporal.PlainDate(1976, 11, 18), new Temporal.PlainDate(1984, 5, 31), {});
+TemporalHelpers.assertDuration(
+ result1, 0, 0, 0, 2751, 0, 0, 0, 0, 0, 0,
+ "options may be an empty plain object"
+);
+
+const result2 = instance.dateUntil(new Temporal.PlainDate(1976, 11, 18), new Temporal.PlainDate(1984, 5, 31), () => {});
+TemporalHelpers.assertDuration(
+ result2, 0, 0, 0, 2751, 0, 0, 0, 0, 0, 0,
+ "options may be a function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/options-wrong-type.js
new file mode 100644
index 0000000000..0bd2ca80fc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/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.calendar.prototype.dateuntil
+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.Calendar("iso8601");
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.dateUntil(new Temporal.PlainDate(1976, 11, 18), new Temporal.PlainDate(1984, 5, 31), value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/order-of-operations.js
new file mode 100644
index 0000000000..3daaaa71fe
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/order-of-operations.js
@@ -0,0 +1,129 @@
+// |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.calendar.prototype.dateuntil
+description: Properties on an object passed to dateUntil() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ // ToTemporalDate 1 → GetTemporalCalendarSlotValueWithISODefault
+ "get one.calendar",
+ "has one.calendar.dateAdd",
+ "has one.calendar.dateFromFields",
+ "has one.calendar.dateUntil",
+ "has one.calendar.day",
+ "has one.calendar.dayOfWeek",
+ "has one.calendar.dayOfYear",
+ "has one.calendar.daysInMonth",
+ "has one.calendar.daysInWeek",
+ "has one.calendar.daysInYear",
+ "has one.calendar.fields",
+ "has one.calendar.id",
+ "has one.calendar.inLeapYear",
+ "has one.calendar.mergeFields",
+ "has one.calendar.month",
+ "has one.calendar.monthCode",
+ "has one.calendar.monthDayFromFields",
+ "has one.calendar.monthsInYear",
+ "has one.calendar.weekOfYear",
+ "has one.calendar.year",
+ "has one.calendar.yearMonthFromFields",
+ "has one.calendar.yearOfWeek",
+ // lookup
+ "get one.calendar.dateFromFields",
+ "get one.calendar.fields",
+ // ToTemporalDate 1 → CalendarFields
+ "call one.calendar.fields",
+ // ToTemporalDate 1 → PrepareTemporalFields
+ "get one.day",
+ "get one.day.valueOf",
+ "call one.day.valueOf",
+ "get one.month",
+ "get one.month.valueOf",
+ "call one.month.valueOf",
+ "get one.monthCode",
+ "get one.monthCode.toString",
+ "call one.monthCode.toString",
+ "get one.year",
+ "get one.year.valueOf",
+ "call one.year.valueOf",
+ // ToTemporalDate 1 → CalendarDateFromFields
+ "call one.calendar.dateFromFields",
+ // ToTemporalDate 2 → GetTemporalCalendarSlotValueWithISODefault
+ "get two.calendar",
+ "has two.calendar.dateAdd",
+ "has two.calendar.dateFromFields",
+ "has two.calendar.dateUntil",
+ "has two.calendar.day",
+ "has two.calendar.dayOfWeek",
+ "has two.calendar.dayOfYear",
+ "has two.calendar.daysInMonth",
+ "has two.calendar.daysInWeek",
+ "has two.calendar.daysInYear",
+ "has two.calendar.fields",
+ "has two.calendar.id",
+ "has two.calendar.inLeapYear",
+ "has two.calendar.mergeFields",
+ "has two.calendar.month",
+ "has two.calendar.monthCode",
+ "has two.calendar.monthDayFromFields",
+ "has two.calendar.monthsInYear",
+ "has two.calendar.weekOfYear",
+ "has two.calendar.year",
+ "has two.calendar.yearMonthFromFields",
+ "has two.calendar.yearOfWeek",
+ // lookup
+ "get two.calendar.dateFromFields",
+ "get two.calendar.fields",
+ // ToTemporalDate 2 → CalendarFields
+ "call two.calendar.fields",
+ // ToTemporalDate 2 → PrepareTemporalFields
+ "get two.day",
+ "get two.day.valueOf",
+ "call two.day.valueOf",
+ "get two.month",
+ "get two.month.valueOf",
+ "call two.month.valueOf",
+ "get two.monthCode",
+ "get two.monthCode.toString",
+ "call two.monthCode.toString",
+ "get two.year",
+ "get two.year.valueOf",
+ "call two.year.valueOf",
+ // ToTemporalDate 2 → CalendarDateFromFields
+ "call two.calendar.dateFromFields",
+ // GetTemporalUnit
+ "get options.largestUnit",
+ "get options.largestUnit.toString",
+ "call options.largestUnit.toString",
+];
+const actual = [];
+
+const instance = new Temporal.Calendar("iso8601");
+
+const one = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2000,
+ month: 5,
+ monthCode: "M05",
+ day: 2,
+ calendar: TemporalHelpers.calendarObserver(actual, "one.calendar"),
+}, "one");
+
+const two = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2001,
+ month: 10,
+ monthCode: "M10",
+ day: 4,
+ calendar: TemporalHelpers.calendarObserver(actual, "two.calendar"),
+}, "two");
+
+const options = TemporalHelpers.propertyBagObserver(actual, { largestUnit: "day" }, "options");
+
+instance.dateUntil(one, two, options);
+assert.compareArray(actual, expected, "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/prop-desc.js
new file mode 100644
index 0000000000..ca1c358c89
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/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.calendar.prototype.dateuntil
+description: The "dateUntil" property of Temporal.Calendar.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Calendar.prototype.dateUntil,
+ "function",
+ "`typeof Calendar.prototype.dateUntil` is `function`"
+);
+
+verifyProperty(Temporal.Calendar.prototype, "dateUntil", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/shell.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/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/Calendar/prototype/dateUntil/throws-range-error-ToLargestTemporalUnit.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/throws-range-error-ToLargestTemporalUnit.js
new file mode 100644
index 0000000000..1934d14874
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/throws-range-error-ToLargestTemporalUnit.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dateuntil
+description: Temporal.Calendar.prototype.dateUntil throw RangeError on ToLargestTemporalUnit with invalide or disallowed unit
+info: |
+ 7. Let largestUnit be ? ToLargestTemporalUnit(options, « "hour", "minute", "second", "millisecond", "microsecond", "nanosecond" », "auto", "day").
+features: [Temporal, arrow-function]
+---*/
+let cal = new Temporal.Calendar("iso8601");
+
+["invalid", "hour", "minute", "second", "millisecond", "microsecond",
+ "nanosecond"].forEach(function(largestUnit) {
+ assert.throws(RangeError, () => cal.dateUntil("2021-07-16", "2022-03-04", {largestUnit}),
+ 'cal.dateUntil("2021-07-16", "2022-03-04", {largestUnit}) throws a RangeError exception');
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/throws-range-error-ToTemporalDate.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/throws-range-error-ToTemporalDate.js
new file mode 100644
index 0000000000..f67da59b3e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/throws-range-error-ToTemporalDate.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dateuntil
+description: Temporal.Calendar.prototype.dateUntil throw RangeError on ToTemporalDate
+info: |
+ 1. Let calendar be the this value.
+ 4. Set one to ? ToTemporalDate(one).
+ 5. Set two to ? ToTemporalDate(two).
+features: [Temporal, arrow-function]
+---*/
+let cal = new Temporal.Calendar("iso8601");
+
+assert.throws(RangeError, () => cal.dateUntil("2021-07-16", "invalide date"),
+ 'cal.dateUntil("2021-07-16", "invalide date") throws a RangeError exception');
+assert.throws(RangeError, () => cal.dateUntil("invalide date", "2021-07-16"),
+ 'cal.dateUntil("invalide date", "2021-07-16") throws a RangeError exception');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/throws-type-error-GetOptionsObject.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/throws-type-error-GetOptionsObject.js
new file mode 100644
index 0000000000..cb98140f26
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/throws-type-error-GetOptionsObject.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.calendar.prototype.dateuntil
+description: Temporal.Calendar.prototype.dateUntil throw TypeError on GetOptionsObject
+info: |
+ 6. Set options to ? GetOptionsObject(options).
+features: [BigInt, Symbol, Temporal, arrow-function]
+---*/
+let cal = new Temporal.Calendar('iso8601');
+
+['string', null, true, false, 123, 456n, Symbol(), Infinity, NaN].forEach(function(opt) {
+ assert.throws(
+ TypeError,
+ () => cal.dateUntil('2021-07-16', '2021-08-11', opt),
+ 'cal.dateUntil("2021-07-16", "2021-08-11", opt) throws a TypeError exception'
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/year-zero.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/year-zero.js
new file mode 100644
index 0000000000..16009d4b8d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dateUntil/year-zero.js
@@ -0,0 +1,34 @@
+// |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.calendar.prototype.dateuntil
+description: Negative zero, as extended year, is invalid
+features: [Temporal]
+---*/
+
+const calendar = new Temporal.Calendar("iso8601");
+const date = new Temporal.PlainDate(2000, 5, 2);
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T00:45",
+ "-000000-10-31T00:45+01:00",
+ "-000000-10-31T00:45+00:00[UTC]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => calendar.dateUntil(arg, date),
+ "cannot use minus zero as extended date (first argument)"
+ );
+
+ assert.throws(
+ RangeError,
+ () => calendar.dateUntil(date, arg),
+ "cannot use minus zero as extended date (second argument)"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..9b0728c95d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-builtin-calendar-no-array-iteration.js
@@ -0,0 +1,24 @@
+// |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.calendar.prototype.day
+description: >
+ Calling the method with a property bag argument with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.Calendar("iso8601");
+const arg = { year: 2000, month: 5, day: 2, calendar: "iso8601" };
+instance.day(arg);
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-calendar-datefromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..7238a8789b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-calendar-datefromfields-called-with-null-prototype-fields.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.calendar.prototype.day
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.Calendar("iso8601");
+const arg = { year: 2000, month: 5, day: 2, calendar };
+instance.day(arg);
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..9ba373f078
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-constructor-in-calendar-fields.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.calendar.prototype.day
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.Calendar("iso8601");
+
+assert.throws(RangeError, () => instance.day(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-duplicate-calendar-fields.js
new file mode 100644
index 0000000000..4a285f17fe
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-duplicate-calendar-fields.js
@@ -0,0 +1,19 @@
+// |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.calendar.prototype.day
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['day'], ['month'], ['monthCode'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar };
+ const instance = new Temporal.Calendar("iso8601");
+
+ assert.throws(RangeError, () => instance.day(arg));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-leap-second.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-leap-second.js
new file mode 100644
index 0000000000..d8407aa8a4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-leap-second.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.calendar.prototype.day
+description: Leap second is a valid ISO string for PlainDate
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+let arg = "2016-12-31T23:59:60";
+const result1 = instance.day(arg);
+assert.sameValue(
+ result1,
+ 31,
+ "leap second is a valid ISO string for PlainDate"
+);
+
+arg = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+const result2 = instance.day(arg);
+assert.sameValue(
+ result2,
+ 31,
+ "second: 60 is ignored in property bag for PlainDate"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-number.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-number.js
new file mode 100644
index 0000000000..131c280ca7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/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.calendar.prototype.day
+description: A number cannot be used in place of a Temporal.PlainDate
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const numbers = [
+ 1,
+ 19761118,
+ -19761118,
+ 1234567890,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => instance.day(arg),
+ 'Numbers cannot be used in place of an ISO string for PlainDate'
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-propertybag-calendar-case-insensitive.js
new file mode 100644
index 0000000000..0879d4d819
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-propertybag-calendar-case-insensitive.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.calendar.prototype.day
+description: The calendar name is case-insensitive
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const calendar = "IsO8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.day(arg);
+assert.sameValue(result, 18, "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-propertybag-calendar-leap-second.js
new file mode 100644
index 0000000000..a1b5f9f765
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-propertybag-calendar-leap-second.js
@@ -0,0 +1,23 @@
+// |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.calendar.prototype.day
+description: Leap second is a valid ISO string for a calendar in a property bag
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const calendar = "2016-12-31T23:59:60";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.day(arg);
+assert.sameValue(
+ result,
+ 18,
+ "leap second is a valid ISO string for calendar"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-propertybag-calendar-number.js
new file mode 100644
index 0000000000..080af485a6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-propertybag-calendar-number.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.calendar.prototype.day
+description: A number as calendar in a property bag is not accepted
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const numbers = [
+ 1,
+ 19970327,
+ -19970327,
+ 1234567890,
+];
+
+for (const calendar of numbers) {
+ const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+ assert.throws(
+ TypeError,
+ () => instance.day(arg),
+ "Numbers cannot be used as a calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-propertybag-calendar-string.js
new file mode 100644
index 0000000000..e397d5ede6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-propertybag-calendar-string.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.calendar.prototype.day
+description: A calendar ID is valid input for Calendar
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const calendar = "iso8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.day(arg);
+assert.sameValue(result, 18, `Calendar created from string "${calendar}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-propertybag-calendar-wrong-type.js
new file mode 100644
index 0000000000..cef7b6ff05
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-propertybag-calendar-wrong-type.js
@@ -0,0 +1,46 @@
+// |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.calendar.prototype.day
+description: >
+ Appropriate error thrown when a calendar property from a property bag cannot
+ be converted to a calendar object or string
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.Calendar("iso8601");
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [calendar, description] of primitiveTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ typeof calendar === 'string' ? RangeError : TypeError,
+ () => instance.day(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+ [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields()
+];
+
+for (const [calendar, description] of typeErrorTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(TypeError, () => instance.day(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/Calendar/prototype/day/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-propertybag-calendar-year-zero.js
new file mode 100644
index 0000000000..454c6a68de
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-propertybag-calendar-year-zero.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.calendar.prototype.day
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T17:45",
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+01:00",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+const instance = new Temporal.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.day(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-proto-in-calendar-fields.js
new file mode 100644
index 0000000000..eaf3311eba
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-proto-in-calendar-fields.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.calendar.prototype.day
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.Calendar("iso8601");
+
+assert.throws(RangeError, () => instance.day(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..2272f0dd1d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-string-calendar-annotation.js
@@ -0,0 +1,33 @@
+// |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.calendar.prototype.day
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02[u-ca=iso8601]", "without time or time zone"],
+ ["2000-05-02[UTC][u-ca=iso8601]", "with time zone and no time"],
+ ["2000-05-02T15:23[u-ca=iso8601]", "without time zone"],
+ ["2000-05-02T15:23[UTC][u-ca=iso8601]", "with time zone"],
+ ["2000-05-02T15:23[!u-ca=iso8601]", "with ! and no time zone"],
+ ["2000-05-02T15:23[UTC][!u-ca=iso8601]", "with ! and time zone"],
+ ["2000-05-02T15:23[u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+const instance = new Temporal.Calendar("iso8601");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.day(arg);
+
+ assert.sameValue(
+ result,
+ 2,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..bbb53bca6a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-string-critical-unknown-annotation.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.calendar.prototype.day
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[!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.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.day(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..e6a0d706cd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-string-date-with-utc-offset.js
@@ -0,0 +1,48 @@
+// |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.calendar.prototype.day
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const validStrings = [
+ "2000-05-02T00+00:00",
+ "2000-05-02T00+00:00[UTC]",
+ "2000-05-02T00+00:00[!UTC]",
+ "2000-05-02T00-02:30[America/St_Johns]",
+];
+
+for (const arg of validStrings) {
+ const result = instance.day(arg);
+
+ assert.sameValue(
+ result,
+ 2,
+ `"${arg}" is a valid UTC offset with time for PlainDate`
+ );
+}
+
+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.day(arg),
+ `"${arg}" UTC offset without time is not valid for PlainDate`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-string-invalid.js
new file mode 100644
index 0000000000..9b4cdb9bef
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-string-invalid.js
@@ -0,0 +1,64 @@
+// |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.calendar.prototype.day
+description: >
+ RangeError thrown if an invalid ISO string (or syntactically valid ISO string
+ that is not supported) is used as a PlainDate
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ // invalid ISO strings:
+ "",
+ "invalid iso8601",
+ "2020-01-00",
+ "2020-01-32",
+ "2020-02-30",
+ "2021-02-29",
+ "2020-00-01",
+ "2020-13-01",
+ "2020-01-01T",
+ "2020-01-01T25:00:00",
+ "2020-01-01T01:60:00",
+ "2020-01-01T01:60:61",
+ "2020-01-01junk",
+ "2020-01-01T00:00:00junk",
+ "2020-01-01T00:00:00+00:00junk",
+ "2020-01-01T00:00:00+00:00[UTC]junk",
+ "2020-01-01T00:00:00+00:00[UTC][u-ca=iso8601]junk",
+ "02020-01-01",
+ "2020-001-01",
+ "2020-01-001",
+ "2020-01-01T001",
+ "2020-01-01T01:001",
+ "2020-01-01T01:01:001",
+ // valid, but forms not supported in Temporal:
+ "2020-W01-1",
+ "2020-001",
+ "+0002020-01-01",
+ // valid, but this calendar must not exist:
+ "2020-01-01[u-ca=notexist]",
+ // may be valid in other contexts, but insufficient information for PlainDate:
+ "2020-01",
+ "+002020-01",
+ "01-01",
+ "2020-W01",
+ "P1Y",
+ "-P12Y",
+ // valid, but outside the supported range:
+ "-999999-01-01",
+ "+999999-01-01",
+];
+const instance = new Temporal.Calendar("iso8601");
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => instance.day(arg),
+ `"${arg}" should not be a valid ISO string for a PlainDate`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..15643ab93c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/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.calendar.prototype.day
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[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.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.day(arg),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..3d84d9f531
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-string-multiple-time-zone.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.calendar.prototype.day
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[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.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.day(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-string-time-separators.js
new file mode 100644
index 0000000000..4fafb7248b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-string-time-separators.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.calendar.prototype.day
+description: Time separator in string argument can vary
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02T15:23", "uppercase T"],
+ ["2000-05-02t15:23", "lowercase T"],
+ ["2000-05-02 15:23", "space between date and time"],
+];
+
+const instance = new Temporal.Calendar("iso8601");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.day(arg);
+
+ assert.sameValue(
+ result,
+ 2,
+ `variant time separators (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..e688ec555f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-string-time-zone-annotation.js
@@ -0,0 +1,38 @@
+// |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.calendar.prototype.day
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02[Asia/Kolkata]", "named, with no time"],
+ ["2000-05-02[!Europe/Vienna]", "named, with ! and no time"],
+ ["2000-05-02[+00:00]", "numeric, with no time"],
+ ["2000-05-02[!-02:30]", "numeric, with ! and no time"],
+ ["2000-05-02T15:23[America/Sao_Paulo]", "named, with no offset"],
+ ["2000-05-02T15:23[!Asia/Tokyo]", "named, with ! and no offset"],
+ ["2000-05-02T15:23[-02:30]", "numeric, with no offset"],
+ ["2000-05-02T15:23[!+00:00]", "numeric, with ! and no offset"],
+ ["2000-05-02T15:23+00:00[America/New_York]", "named, with offset"],
+ ["2000-05-02T15:23+00:00[!UTC]", "named, with offset and !"],
+ ["2000-05-02T15:23+00:00[+01:00]", "numeric, with offset"],
+ ["2000-05-02T15:23+00:00[!-08:00]", "numeric, with offset and !"],
+];
+
+const instance = new Temporal.Calendar("iso8601");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.day(arg);
+
+ assert.sameValue(
+ result,
+ 2,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..7c98fe71e4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-string-unknown-annotation.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.calendar.prototype.day
+description: Various forms of unknown annotation
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02[foo=bar]", "without time"],
+ ["2000-05-02T15:23[foo=bar]", "alone"],
+ ["2000-05-02T15:23[UTC][foo=bar]", "with time zone"],
+ ["2000-05-02T15:23[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["2000-05-02T15:23[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["2000-05-02T15:23[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+const instance = new Temporal.Calendar("iso8601");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.day(arg);
+
+ assert.sameValue(
+ result,
+ 2,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..b1deda8354
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-string-with-utc-designator.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.calendar.prototype.day
+description: RangeError thrown if a string with UTC designator is used as a PlainDate
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "2019-10-01T09:00:00Z",
+ "2019-10-01T09:00:00Z[UTC]",
+];
+const instance = new Temporal.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.day(arg),
+ "String with UTC designator should not be valid as a PlainDate"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-wrong-type.js
new file mode 100644
index 0000000000..8e225e2bcb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/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.calendar.prototype.day
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainDate
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+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.day(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.PlainDate, "Temporal.PlainDate, object"],
+ [Temporal.PlainDate.prototype, "Temporal.PlainDate.prototype, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.day(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/Calendar/prototype/day/argument-zoneddatetime-convert.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-zoneddatetime-convert.js
new file mode 100644
index 0000000000..51bf0e2db4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-zoneddatetime-convert.js
@@ -0,0 +1,22 @@
+// |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.calendar.prototype.day
+description: An exception from TimeZone#getOffsetNanosecondsFor() is propagated.
+features: [Temporal]
+---*/
+
+class TZ extends Temporal.TimeZone {
+ constructor() { super("UTC") }
+ getOffsetNanosecondsFor() { throw new Test262Error() }
+}
+
+const tz = new TZ();
+const arg = new Temporal.ZonedDateTime(0n, tz);
+const instance = new Temporal.Calendar("iso8601");
+
+assert.throws(Test262Error, () => instance.day(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-zoneddatetime-slots.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-zoneddatetime-slots.js
new file mode 100644
index 0000000000..87f13d142c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-zoneddatetime-slots.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.calendar.prototype.day
+description: Getters are not called when converting a ZonedDateTime to a PlainDate.
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.ZonedDateTime.prototype);
+const getters = ["year", "month", "monthCode", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond", "calendar"];
+
+for (const property of getters) {
+ Object.defineProperty(Temporal.ZonedDateTime.prototype, property, {
+ get() {
+ actual.push(`get ${property}`);
+ const value = prototypeDescrs[property].get.call(this);
+ return {
+ toString() {
+ actual.push(`toString ${property}`);
+ return value.toString();
+ },
+ valueOf() {
+ actual.push(`valueOf ${property}`);
+ return value;
+ },
+ };
+ },
+ });
+}
+
+const arg = new Temporal.ZonedDateTime(0n, "UTC");
+const instance = new Temporal.Calendar("iso8601");
+instance.day(arg);
+assert.compareArray(actual, []);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..7f3731cfd4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/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.calendar.prototype.day
+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 calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => calendar.day(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..9809d9a0f7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/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.calendar.prototype.day
+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 calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => calendar.day(datetime),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..71d7ae998a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/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.calendar.prototype.day
+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 calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => calendar.day(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..d9964911f5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/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.calendar.prototype.day
+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 calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => calendar.day(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/basic.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/basic.js
new file mode 100644
index 0000000000..710f903710
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/basic.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.calendar.prototype.day
+description: Basic tests for day().
+features: [Temporal]
+---*/
+
+const iso = Temporal.Calendar.from("iso8601");
+const res = 5;
+assert.sameValue(iso.day(Temporal.PlainDate.from("1994-11-05")), res, "PlainDate");
+assert.sameValue(iso.day(Temporal.PlainDateTime.from("1994-11-05T08:15:30")), res, "PlainDateTime");
+assert.sameValue(iso.day(Temporal.PlainMonthDay.from("11-05")), res, "PlainMonthDay");
+assert.sameValue(iso.day({ year: 1994, month: 11, day: 5 }), res, "property bag");
+assert.sameValue(iso.day("1994-11-05"), res, "string");
+assert.throws(TypeError, () => iso.day({ year: 2000 }), "property bag with missing properties");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/branding.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/branding.js
new file mode 100644
index 0000000000..b77d8a8109
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/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.calendar.prototype.day
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const day = Temporal.Calendar.prototype.day;
+
+assert.sameValue(typeof day, "function");
+
+const args = [new Temporal.PlainDate(2000, 1, 1)];
+
+assert.throws(TypeError, () => day.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => day.apply(null, args), "null");
+assert.throws(TypeError, () => day.apply(true, args), "true");
+assert.throws(TypeError, () => day.apply("", args), "empty string");
+assert.throws(TypeError, () => day.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => day.apply(1, args), "1");
+assert.throws(TypeError, () => day.apply({}, args), "plain object");
+assert.throws(TypeError, () => day.apply(Temporal.Calendar, args), "Temporal.Calendar");
+assert.throws(TypeError, () => day.apply(Temporal.Calendar.prototype, args), "Temporal.Calendar.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/browser.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/builtin.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/builtin.js
new file mode 100644
index 0000000000..5930579457
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/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.calendar.prototype.day
+description: >
+ Tests that Temporal.Calendar.prototype.day
+ 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.Calendar.prototype.day),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Calendar.prototype.day),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Calendar.prototype.day),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Calendar.prototype.day.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/calendar-datefromfields-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/calendar-datefromfields-called-with-options-undefined.js
new file mode 100644
index 0000000000..46b9873fc8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/calendar-datefromfields-called-with-options-undefined.js
@@ -0,0 +1,18 @@
+// |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.calendar.prototype.day
+description: >
+ Calendar.dateFromFields method is called with undefined as the options value
+ when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
+calendar.day({ year: 2000, month: 5, day: 3, calendar });
+assert.sameValue(calendar.dateFromFieldsCallCount, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/calendar-fields-iterable.js
new file mode 100644
index 0000000000..4410ec4006
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/calendar-fields-iterable.js
@@ -0,0 +1,37 @@
+// |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.calendar.prototype.day
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.calendar.prototype.day step 4:
+ 4. Return ? ISODay(_dateOrDateTime_).
+ sec-temporal-isoday step 1.a:
+ a. Set _dateOrDateTime_ to ? ToTemporalDate(_dateOrDateTime_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(calendar, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToListOfType(_fieldsArray_, « String »).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+calendar1.day({ year: 2000, month: 5, day: 2, calendar: calendar2 });
+
+assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/calendar-temporal-object.js
new file mode 100644
index 0000000000..b40cc5dcd5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/calendar-temporal-object.js
@@ -0,0 +1,31 @@
+// |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.calendar.prototype.day
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.calendar.prototype.day step 4:
+ 4. Return ? ISODay(_dateOrDateTime_).
+ sec-temporal-isoday step 1.a:
+ a. Set _dateOrDateTime_ to ? ToTemporalDate(_dateOrDateTime_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const calendar = new Temporal.Calendar("iso8601");
+ calendar.day({ year: 2000, month: 5, day: 2, calendar: temporalObject });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/date-time.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/date-time.js
new file mode 100644
index 0000000000..d664a65afa
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/date-time.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.day
+description: >
+ Temporal.Calendar.prototype.day will take PlainDateTime and return
+ the value of the day.
+info: |
+ 5. Return ! ISODay(temporalDateLike).
+features: [Temporal]
+---*/
+let cal = new Temporal.Calendar("iso8601");
+
+let dateTime = new Temporal.PlainDateTime(1997, 8, 23, 5, 30, 13)
+assert.sameValue(cal.day(dateTime), 23, 'cal.day(new Temporal.PlainDateTime(1997, 8, 23, 5, 30, 13)) must return 23');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/date.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/date.js
new file mode 100644
index 0000000000..e78ca47b51
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/date.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.day
+description: >
+ Temporal.Calendar.prototype.day will take PlainDate and return
+ the value of the day.
+info: |
+ 5. Return ! ISODay(temporalDateLike).
+features: [Temporal]
+---*/
+let cal = new Temporal.Calendar("iso8601");
+
+let date = new Temporal.PlainDate(2021, 7, 15);
+assert.sameValue(cal.day(date), 15, 'cal.day(new Temporal.PlainDate(2021, 7, 15)) must return 15');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..0488591f63
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/infinity-throws-rangeerror.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.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.calendar.prototype.day
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+const base = { year: 2000, month: 5, day: 2 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.day({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.day({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/length.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/length.js
new file mode 100644
index 0000000000..009a8695f2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/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.calendar.prototype.day
+description: Temporal.Calendar.prototype.day.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.Calendar.prototype.day, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/month-day.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/month-day.js
new file mode 100644
index 0000000000..b0f989774f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/month-day.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.day
+description: >
+ Temporal.Calendar.prototype.day will take PlainMonthDay and return
+ the value of the day.
+info: |
+ 4. If Type(temporalDateLike) is not Object or temporalDateLike does not have
+ an [[InitializedTemporalDate]] or [[InitializedTemporalYearMonth]] internal
+ slot, then
+ a. Set temporalDateLike to ? ToTemporalDate(temporalDateLike).
+ 5. Return ! ISODay(temporalDateLike).
+features: [Temporal]
+---*/
+let cal = new Temporal.Calendar("iso8601");
+
+let monthDay = new Temporal.PlainMonthDay(7, 15);
+assert.sameValue(cal.day(monthDay), 15, 'cal.day(new Temporal.PlainMonthDay(7, 15)) must return 15');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/name.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/name.js
new file mode 100644
index 0000000000..81ecf1d75b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/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.calendar.prototype.day
+description: Temporal.Calendar.prototype.day.name is "day".
+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.Calendar.prototype.day, "name", {
+ value: "day",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/not-a-constructor.js
new file mode 100644
index 0000000000..b754eb84c0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/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.calendar.prototype.day
+description: >
+ Temporal.Calendar.prototype.day 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.Calendar.prototype.day();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Calendar.prototype.day), false,
+ "isConstructor(Temporal.Calendar.prototype.day)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/prop-desc.js
new file mode 100644
index 0000000000..868d426d89
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/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.calendar.prototype.day
+description: The "day" property of Temporal.Calendar.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Calendar.prototype.day,
+ "function",
+ "`typeof Calendar.prototype.day` is `function`"
+);
+
+verifyProperty(Temporal.Calendar.prototype, "day", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/shell.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/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/Calendar/prototype/day/string.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/string.js
new file mode 100644
index 0000000000..9128672148
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/string.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.day
+description: >
+ Temporal.Calendar.prototype.day will take ISO8601 string and return
+ the value of the day.
+info: |
+ 4. If Type(temporalDateLike) is not Object or temporalDateLike does not have
+ an [[InitializedTemporalDate]] or [[InitializedTemporalYearMonth]] internal
+ slot, then
+ a. Set temporalDateLike to ? ToTemporalDate(temporalDateLike).
+ 5. Return ! ISODay(temporalDateLike).
+features: [Temporal]
+---*/
+let cal = new Temporal.Calendar("iso8601");
+
+assert.sameValue(cal.day("2019-03-15"), 15, 'cal.day("2019-03-15") must return 15');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/throw-range-error-ToTemporalDate.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/throw-range-error-ToTemporalDate.js
new file mode 100644
index 0000000000..1ea947ca9e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/throw-range-error-ToTemporalDate.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.day
+description: >
+ Temporal.Calendar.prototype.day throws RangeError on
+ ToTemporalDate when temporalDateLike is invalid string.
+info: |
+ 4. If Type(temporalDateLike) is not Object or temporalDateLike
+ does not have an [[InitializedTemporalDate]] or
+ [[InitializedTemporalYearMonth]] internal slot, then
+ a. Set temporalDateLike to ? ToTemporalDate(temporalDateLike).
+features: [Temporal, arrow-function]
+---*/
+let cal = new Temporal.Calendar("iso8601");
+
+assert.throws(RangeError, () => cal.day("invalid string"),
+ 'cal.day("invalid string") throws a RangeError exception');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/year-zero.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/year-zero.js
new file mode 100644
index 0000000000..f1c0e19e8c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/day/year-zero.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.calendar.prototype.day
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T00:45",
+ "-000000-10-31T00:45+01:00",
+ "-000000-10-31T00:45+00:00[UTC]",
+];
+const instance = new Temporal.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.day(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..099a7b859b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-builtin-calendar-no-array-iteration.js
@@ -0,0 +1,24 @@
+// |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.calendar.prototype.dayofweek
+description: >
+ Calling the method with a property bag argument with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.Calendar("iso8601");
+const arg = { year: 2000, month: 5, day: 2, calendar: "iso8601" };
+instance.dayOfWeek(arg);
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-calendar-datefromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..00120c596a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-calendar-datefromfields-called-with-null-prototype-fields.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.calendar.prototype.dayofweek
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.Calendar("iso8601");
+const arg = { year: 2000, month: 5, day: 2, calendar };
+instance.dayOfWeek(arg);
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..7839fb6454
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-constructor-in-calendar-fields.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.calendar.prototype.dayofweek
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.Calendar("iso8601");
+
+assert.throws(RangeError, () => instance.dayOfWeek(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-duplicate-calendar-fields.js
new file mode 100644
index 0000000000..d492982e7f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-duplicate-calendar-fields.js
@@ -0,0 +1,19 @@
+// |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.calendar.prototype.dayofweek
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['day'], ['month'], ['monthCode'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar };
+ const instance = new Temporal.Calendar("iso8601");
+
+ assert.throws(RangeError, () => instance.dayOfWeek(arg));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-leap-second.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-leap-second.js
new file mode 100644
index 0000000000..5f528093ba
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-leap-second.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.calendar.prototype.dayofweek
+description: Leap second is a valid ISO string for PlainDate
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+let arg = "2016-12-31T23:59:60";
+const result1 = instance.dayOfWeek(arg);
+assert.sameValue(
+ result1,
+ 6,
+ "leap second is a valid ISO string for PlainDate"
+);
+
+arg = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+const result2 = instance.dayOfWeek(arg);
+assert.sameValue(
+ result2,
+ 6,
+ "second: 60 is ignored in property bag for PlainDate"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-number.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-number.js
new file mode 100644
index 0000000000..6aa86300b1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/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.calendar.prototype.dayofweek
+description: A number cannot be used in place of a Temporal.PlainDate
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const numbers = [
+ 1,
+ 19761118,
+ -19761118,
+ 1234567890,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => instance.dayOfWeek(arg),
+ 'Numbers cannot be used in place of an ISO string for PlainDate'
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-propertybag-calendar-case-insensitive.js
new file mode 100644
index 0000000000..afa01ac2f3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-propertybag-calendar-case-insensitive.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.calendar.prototype.dayofweek
+description: The calendar name is case-insensitive
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const calendar = "IsO8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.dayOfWeek(arg);
+assert.sameValue(result, 4, "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-propertybag-calendar-leap-second.js
new file mode 100644
index 0000000000..014649fad0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-propertybag-calendar-leap-second.js
@@ -0,0 +1,23 @@
+// |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.calendar.prototype.dayofweek
+description: Leap second is a valid ISO string for a calendar in a property bag
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const calendar = "2016-12-31T23:59:60";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.dayOfWeek(arg);
+assert.sameValue(
+ result,
+ 4,
+ "leap second is a valid ISO string for calendar"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-propertybag-calendar-number.js
new file mode 100644
index 0000000000..8a041042d2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-propertybag-calendar-number.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.calendar.prototype.dayofweek
+description: A number as calendar in a property bag is not accepted
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const numbers = [
+ 1,
+ 19970327,
+ -19970327,
+ 1234567890,
+];
+
+for (const calendar of numbers) {
+ const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+ assert.throws(
+ TypeError,
+ () => instance.dayOfWeek(arg),
+ "Numbers cannot be used as a calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-propertybag-calendar-string.js
new file mode 100644
index 0000000000..64a59dcc51
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-propertybag-calendar-string.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.calendar.prototype.dayofweek
+description: A calendar ID is valid input for Calendar
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const calendar = "iso8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.dayOfWeek(arg);
+assert.sameValue(result, 4, `Calendar created from string "${calendar}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-propertybag-calendar-wrong-type.js
new file mode 100644
index 0000000000..ffd6082e3b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-propertybag-calendar-wrong-type.js
@@ -0,0 +1,46 @@
+// |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.calendar.prototype.dayofweek
+description: >
+ Appropriate error thrown when a calendar property from a property bag cannot
+ be converted to a calendar object or string
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.Calendar("iso8601");
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [calendar, description] of primitiveTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ typeof calendar === 'string' ? RangeError : TypeError,
+ () => instance.dayOfWeek(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+ [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields()
+];
+
+for (const [calendar, description] of typeErrorTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(TypeError, () => instance.dayOfWeek(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/Calendar/prototype/dayOfWeek/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-propertybag-calendar-year-zero.js
new file mode 100644
index 0000000000..767b87d15d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-propertybag-calendar-year-zero.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.calendar.prototype.dayofweek
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T17:45",
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+01:00",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+const instance = new Temporal.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.dayOfWeek(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-proto-in-calendar-fields.js
new file mode 100644
index 0000000000..efcd36bb13
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-proto-in-calendar-fields.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.calendar.prototype.dayofweek
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.Calendar("iso8601");
+
+assert.throws(RangeError, () => instance.dayOfWeek(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..06f5075832
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-string-calendar-annotation.js
@@ -0,0 +1,33 @@
+// |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.calendar.prototype.dayofweek
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02[u-ca=iso8601]", "without time or time zone"],
+ ["2000-05-02[UTC][u-ca=iso8601]", "with time zone and no time"],
+ ["2000-05-02T15:23[u-ca=iso8601]", "without time zone"],
+ ["2000-05-02T15:23[UTC][u-ca=iso8601]", "with time zone"],
+ ["2000-05-02T15:23[!u-ca=iso8601]", "with ! and no time zone"],
+ ["2000-05-02T15:23[UTC][!u-ca=iso8601]", "with ! and time zone"],
+ ["2000-05-02T15:23[u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+const instance = new Temporal.Calendar("iso8601");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.dayOfWeek(arg);
+
+ assert.sameValue(
+ result,
+ 2,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..68aed5c169
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-string-critical-unknown-annotation.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.calendar.prototype.dayofweek
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[!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.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.dayOfWeek(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..bad099d0d8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-string-date-with-utc-offset.js
@@ -0,0 +1,48 @@
+// |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.calendar.prototype.dayofweek
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const validStrings = [
+ "2000-05-02T00+00:00",
+ "2000-05-02T00+00:00[UTC]",
+ "2000-05-02T00+00:00[!UTC]",
+ "2000-05-02T00-02:30[America/St_Johns]",
+];
+
+for (const arg of validStrings) {
+ const result = instance.dayOfWeek(arg);
+
+ assert.sameValue(
+ result,
+ 2,
+ `"${arg}" is a valid UTC offset with time for PlainDate`
+ );
+}
+
+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.dayOfWeek(arg),
+ `"${arg}" UTC offset without time is not valid for PlainDate`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-string-invalid.js
new file mode 100644
index 0000000000..ce96c32afe
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-string-invalid.js
@@ -0,0 +1,64 @@
+// |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.calendar.prototype.dayofweek
+description: >
+ RangeError thrown if an invalid ISO string (or syntactically valid ISO string
+ that is not supported) is used as a PlainDate
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ // invalid ISO strings:
+ "",
+ "invalid iso8601",
+ "2020-01-00",
+ "2020-01-32",
+ "2020-02-30",
+ "2021-02-29",
+ "2020-00-01",
+ "2020-13-01",
+ "2020-01-01T",
+ "2020-01-01T25:00:00",
+ "2020-01-01T01:60:00",
+ "2020-01-01T01:60:61",
+ "2020-01-01junk",
+ "2020-01-01T00:00:00junk",
+ "2020-01-01T00:00:00+00:00junk",
+ "2020-01-01T00:00:00+00:00[UTC]junk",
+ "2020-01-01T00:00:00+00:00[UTC][u-ca=iso8601]junk",
+ "02020-01-01",
+ "2020-001-01",
+ "2020-01-001",
+ "2020-01-01T001",
+ "2020-01-01T01:001",
+ "2020-01-01T01:01:001",
+ // valid, but forms not supported in Temporal:
+ "2020-W01-1",
+ "2020-001",
+ "+0002020-01-01",
+ // valid, but this calendar must not exist:
+ "2020-01-01[u-ca=notexist]",
+ // may be valid in other contexts, but insufficient information for PlainDate:
+ "2020-01",
+ "+002020-01",
+ "01-01",
+ "2020-W01",
+ "P1Y",
+ "-P12Y",
+ // valid, but outside the supported range:
+ "-999999-01-01",
+ "+999999-01-01",
+];
+const instance = new Temporal.Calendar("iso8601");
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => instance.dayOfWeek(arg),
+ `"${arg}" should not be a valid ISO string for a PlainDate`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..0ac61801ab
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/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.calendar.prototype.dayofweek
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[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.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.dayOfWeek(arg),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..c1c244ac4b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-string-multiple-time-zone.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.calendar.prototype.dayofweek
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[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.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.dayOfWeek(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-string-time-separators.js
new file mode 100644
index 0000000000..e53336a8d8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-string-time-separators.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.calendar.prototype.dayofweek
+description: Time separator in string argument can vary
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02T15:23", "uppercase T"],
+ ["2000-05-02t15:23", "lowercase T"],
+ ["2000-05-02 15:23", "space between date and time"],
+];
+
+const instance = new Temporal.Calendar("iso8601");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.dayOfWeek(arg);
+
+ assert.sameValue(
+ result,
+ 2,
+ `variant time separators (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..c802250c95
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-string-time-zone-annotation.js
@@ -0,0 +1,38 @@
+// |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.calendar.prototype.dayofweek
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02[Asia/Kolkata]", "named, with no time"],
+ ["2000-05-02[!Europe/Vienna]", "named, with ! and no time"],
+ ["2000-05-02[+00:00]", "numeric, with no time"],
+ ["2000-05-02[!-02:30]", "numeric, with ! and no time"],
+ ["2000-05-02T15:23[America/Sao_Paulo]", "named, with no offset"],
+ ["2000-05-02T15:23[!Asia/Tokyo]", "named, with ! and no offset"],
+ ["2000-05-02T15:23[-02:30]", "numeric, with no offset"],
+ ["2000-05-02T15:23[!+00:00]", "numeric, with ! and no offset"],
+ ["2000-05-02T15:23+00:00[America/New_York]", "named, with offset"],
+ ["2000-05-02T15:23+00:00[!UTC]", "named, with offset and !"],
+ ["2000-05-02T15:23+00:00[+01:00]", "numeric, with offset"],
+ ["2000-05-02T15:23+00:00[!-08:00]", "numeric, with offset and !"],
+];
+
+const instance = new Temporal.Calendar("iso8601");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.dayOfWeek(arg);
+
+ assert.sameValue(
+ result,
+ 2,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..e7a079cbc0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-string-unknown-annotation.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.calendar.prototype.dayofweek
+description: Various forms of unknown annotation
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02[foo=bar]", "without time"],
+ ["2000-05-02T15:23[foo=bar]", "alone"],
+ ["2000-05-02T15:23[UTC][foo=bar]", "with time zone"],
+ ["2000-05-02T15:23[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["2000-05-02T15:23[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["2000-05-02T15:23[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+const instance = new Temporal.Calendar("iso8601");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.dayOfWeek(arg);
+
+ assert.sameValue(
+ result,
+ 2,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..3a08fea3c6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-string-with-utc-designator.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.calendar.prototype.dayofweek
+description: RangeError thrown if a string with UTC designator is used as a PlainDate
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "2019-10-01T09:00:00Z",
+ "2019-10-01T09:00:00Z[UTC]",
+];
+const instance = new Temporal.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.dayOfWeek(arg),
+ "String with UTC designator should not be valid as a PlainDate"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-wrong-type.js
new file mode 100644
index 0000000000..a1fdcb177e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/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.calendar.prototype.dayofweek
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainDate
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+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.dayOfWeek(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.PlainDate, "Temporal.PlainDate, object"],
+ [Temporal.PlainDate.prototype, "Temporal.PlainDate.prototype, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.dayOfWeek(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/Calendar/prototype/dayOfWeek/argument-zoneddatetime-convert.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-zoneddatetime-convert.js
new file mode 100644
index 0000000000..9b65728bd8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-zoneddatetime-convert.js
@@ -0,0 +1,22 @@
+// |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.calendar.prototype.dayofweek
+description: An exception from TimeZone#getOffsetNanosecondsFor() is propagated.
+features: [Temporal]
+---*/
+
+class TZ extends Temporal.TimeZone {
+ constructor() { super("UTC") }
+ getOffsetNanosecondsFor() { throw new Test262Error() }
+}
+
+const tz = new TZ();
+const arg = new Temporal.ZonedDateTime(0n, tz);
+const instance = new Temporal.Calendar("iso8601");
+
+assert.throws(Test262Error, () => instance.dayOfWeek(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-zoneddatetime-slots.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-zoneddatetime-slots.js
new file mode 100644
index 0000000000..e73205db91
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-zoneddatetime-slots.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.calendar.prototype.dayofweek
+description: Getters are not called when converting a ZonedDateTime to a PlainDate.
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.ZonedDateTime.prototype);
+const getters = ["year", "month", "monthCode", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond", "calendar"];
+
+for (const property of getters) {
+ Object.defineProperty(Temporal.ZonedDateTime.prototype, property, {
+ get() {
+ actual.push(`get ${property}`);
+ const value = prototypeDescrs[property].get.call(this);
+ return {
+ toString() {
+ actual.push(`toString ${property}`);
+ return value.toString();
+ },
+ valueOf() {
+ actual.push(`valueOf ${property}`);
+ return value;
+ },
+ };
+ },
+ });
+}
+
+const arg = new Temporal.ZonedDateTime(0n, "UTC");
+const instance = new Temporal.Calendar("iso8601");
+instance.dayOfWeek(arg);
+assert.compareArray(actual, []);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..fd111b05c3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/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.calendar.prototype.dayofweek
+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 calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => calendar.dayOfWeek(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..83f279131c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/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.calendar.prototype.dayofweek
+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 calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => calendar.dayOfWeek(datetime),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..ce4abc6ff1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/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.calendar.prototype.dayofweek
+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 calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => calendar.dayOfWeek(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..6a323edffb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/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.calendar.prototype.dayofweek
+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 calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => calendar.dayOfWeek(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/basic.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/basic.js
new file mode 100644
index 0000000000..8bfdd06ac6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/basic.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.calendar.prototype.dayofweek
+description: Basic tests for dayOfWeek().
+features: [Temporal]
+---*/
+
+const iso = Temporal.Calendar.from("iso8601");
+const res = 6;
+assert.sameValue(iso.dayOfWeek(Temporal.PlainDate.from("1994-11-05")), res, "PlainDate");
+assert.sameValue(iso.dayOfWeek(Temporal.PlainDateTime.from("1994-11-05T08:15:30")), res, "PlainDateTime");
+assert.sameValue(iso.dayOfWeek({ year: 1994, month: 11, day: 5 }), res, "property bag");
+assert.sameValue(iso.dayOfWeek("1994-11-05"), res, "string");
+assert.throws(TypeError, () => iso.dayOfWeek({ year: 2000 }), "property bag with missing properties");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/branding.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/branding.js
new file mode 100644
index 0000000000..5be07e2a66
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/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.calendar.prototype.dayofweek
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const dayOfWeek = Temporal.Calendar.prototype.dayOfWeek;
+
+assert.sameValue(typeof dayOfWeek, "function");
+
+const args = [new Temporal.PlainDate(2000, 1, 1)];
+
+assert.throws(TypeError, () => dayOfWeek.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => dayOfWeek.apply(null, args), "null");
+assert.throws(TypeError, () => dayOfWeek.apply(true, args), "true");
+assert.throws(TypeError, () => dayOfWeek.apply("", args), "empty string");
+assert.throws(TypeError, () => dayOfWeek.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => dayOfWeek.apply(1, args), "1");
+assert.throws(TypeError, () => dayOfWeek.apply({}, args), "plain object");
+assert.throws(TypeError, () => dayOfWeek.apply(Temporal.Calendar, args), "Temporal.Calendar");
+assert.throws(TypeError, () => dayOfWeek.apply(Temporal.Calendar.prototype, args), "Temporal.Calendar.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/browser.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/builtin.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/builtin.js
new file mode 100644
index 0000000000..8c4c4565c3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/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.calendar.prototype.dayofweek
+description: >
+ Tests that Temporal.Calendar.prototype.dayOfWeek
+ 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.Calendar.prototype.dayOfWeek),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Calendar.prototype.dayOfWeek),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Calendar.prototype.dayOfWeek),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Calendar.prototype.dayOfWeek.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/calendar-datefromfields-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/calendar-datefromfields-called-with-options-undefined.js
new file mode 100644
index 0000000000..a29e3f7263
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/calendar-datefromfields-called-with-options-undefined.js
@@ -0,0 +1,18 @@
+// |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.calendar.prototype.dayofweek
+description: >
+ Calendar.dateFromFields method is called with undefined as the options value
+ when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
+calendar.dayOfWeek({ year: 2000, month: 5, day: 3, calendar });
+assert.sameValue(calendar.dateFromFieldsCallCount, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/calendar-fields-iterable.js
new file mode 100644
index 0000000000..d608137f82
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/calendar-fields-iterable.js
@@ -0,0 +1,35 @@
+// |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.calendar.prototype.dayofweek
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.calendar.prototype.dayofweek step 4:
+ 4. Let _date_ be ? ToTemporalDate(_dateOrDateTime_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToListOfType(_fieldsArray_, « String »).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+calendar1.dayOfWeek({ year: 2000, month: 5, day: 2, calendar: calendar2 });
+
+assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/calendar-temporal-object.js
new file mode 100644
index 0000000000..68c013a000
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/calendar-temporal-object.js
@@ -0,0 +1,29 @@
+// |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.calendar.prototype.dayofweek
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.calendar.prototype.dayofweek step 4:
+ 4. Let _date_ be ? ToTemporalDate(_dateOrDateTime_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const calendar = new Temporal.Calendar("iso8601");
+ calendar.dayOfWeek({ year: 2000, month: 5, day: 2, calendar: temporalObject });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..3ca706358b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/infinity-throws-rangeerror.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.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.calendar.prototype.dayofweek
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+const base = { year: 2000, month: 5, day: 2 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.dayOfWeek({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.dayOfWeek({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/length.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/length.js
new file mode 100644
index 0000000000..0d2a913dcf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/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.calendar.prototype.dayofweek
+description: Temporal.Calendar.prototype.dayOfWeek.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.Calendar.prototype.dayOfWeek, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/name.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/name.js
new file mode 100644
index 0000000000..f5b9970e0d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/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.calendar.prototype.dayofweek
+description: Temporal.Calendar.prototype.dayOfWeek.name is "dayOfWeek".
+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.Calendar.prototype.dayOfWeek, "name", {
+ value: "dayOfWeek",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/not-a-constructor.js
new file mode 100644
index 0000000000..9c78acd845
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/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.calendar.prototype.dayofweek
+description: >
+ Temporal.Calendar.prototype.dayOfWeek 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.Calendar.prototype.dayOfWeek();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Calendar.prototype.dayOfWeek), false,
+ "isConstructor(Temporal.Calendar.prototype.dayOfWeek)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/plain-date-time.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/plain-date-time.js
new file mode 100644
index 0000000000..9d039cbe62
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/plain-date-time.js
@@ -0,0 +1,40 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.calendar.prototype.dayofweek
+description: >
+ Temporal.Calendar.prototype.dayOfWeek will take Temporal.PlainDateTime objects
+ and return the day of week.
+info: |
+ 5. Return 𝔽(! ToISODayOfWeek(temporalDate.[[ISOYear]], temporalDate.[[ISOMonth]], temporalDate.[[ISODay]])).
+features: [Temporal]
+---*/
+let cal = new Temporal.Calendar("iso8601");
+
+let dt = new Temporal.PlainDateTime(1997, 1, 23, 5, 30, 13);
+assert.sameValue(
+ cal.dayOfWeek(dt),
+ 4,
+ 'cal.dayOfWeek(new Temporal.PlainDateTime(1997, 1, 23, 5, 30, 13)) must return 4'
+);
+dt = new Temporal.PlainDateTime(1996, 2, 23, 5, 30, 13);
+assert.sameValue(
+ cal.dayOfWeek(dt),
+ 5,
+ 'cal.dayOfWeek(new Temporal.PlainDateTime(1996, 2, 23, 5, 30, 13)) must return 5'
+);
+dt = new Temporal.PlainDateTime(1997, 2, 23, 5, 30, 13);
+assert.sameValue(
+ cal.dayOfWeek(dt),
+ 7,
+ 'cal.dayOfWeek(new Temporal.PlainDateTime(1997, 2, 23, 5, 30, 13)) must return 7'
+);
+dt = new Temporal.PlainDateTime(1997, 6, 23, 5, 30, 13);
+assert.sameValue(
+ cal.dayOfWeek(dt),
+ 1,
+ 'cal.dayOfWeek(new Temporal.PlainDateTime(1997, 6, 23, 5, 30, 13)) must return 1'
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/plain-date.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/plain-date.js
new file mode 100644
index 0000000000..4fbf36f474
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/plain-date.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.calendar.prototype.dayofweek
+description: >
+ Temporal.Calendar.prototype.dayOfWeek will take Temporal.PlainDate objects
+ and return the day of week.
+info: |
+ 5. Return 𝔽(! ToISODayOfWeek(temporalDate.[[ISOYear]], temporalDate.[[ISOMonth]], temporalDate.[[ISODay]])).
+features: [Temporal]
+---*/
+let cal = new Temporal.Calendar("iso8601");
+
+let d = new Temporal.PlainDate(1970, 1, 1);
+assert.sameValue(4, cal.dayOfWeek(d), '4 must return the same value returned by cal.dayOfWeek(d)');
+d = new Temporal.PlainDate(2021, 2, 15);
+assert.sameValue(1, cal.dayOfWeek(d), '1 must return the same value returned by cal.dayOfWeek(d)');
+d = new Temporal.PlainDate(2021, 8, 15);
+assert.sameValue(7, cal.dayOfWeek(d), '7 must return the same value returned by cal.dayOfWeek(d)');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/prop-desc.js
new file mode 100644
index 0000000000..a0b9027a05
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/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.calendar.prototype.dayofweek
+description: The "dayOfWeek" property of Temporal.Calendar.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Calendar.prototype.dayOfWeek,
+ "function",
+ "`typeof Calendar.prototype.dayOfWeek` is `function`"
+);
+
+verifyProperty(Temporal.Calendar.prototype, "dayOfWeek", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/shell.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/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/Calendar/prototype/dayOfWeek/string.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/string.js
new file mode 100644
index 0000000000..fe93ad4d4e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/string.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.calendar.prototype.dayofweek
+description: >
+ Temporal.Calendar.prototype.dayOfWeek will take ISO8601 string
+ and return the day of week.
+info: |
+ 4. Let temporalDate be ? ToTemporalDate(temporalDateLike).
+ 5. Return 𝔽(! ToISODayOfWeek(temporalDate.[[ISOYear]], temporalDate.[[ISOMonth]], temporalDate.[[ISODay]])).
+features: [Temporal]
+---*/
+let cal = new Temporal.Calendar("iso8601");
+
+assert.sameValue(cal.dayOfWeek("2019-01-18"), 5, 'cal.dayOfWeek("2019-01-18") must return 5');
+assert.sameValue(cal.dayOfWeek("2019-03-18"), 1, 'cal.dayOfWeek("2019-03-18") must return 1');
+assert.sameValue(cal.dayOfWeek("2019-05-18"), 6, 'cal.dayOfWeek("2019-05-18") must return 6');
+assert.sameValue(cal.dayOfWeek("2019-08-18"), 7, 'cal.dayOfWeek("2019-08-18") must return 7');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/throw-range-error-ToTemporalDate.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/throw-range-error-ToTemporalDate.js
new file mode 100644
index 0000000000..6c435f930d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/throw-range-error-ToTemporalDate.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.calendar.prototype.dayOfWeek
+description: >
+ Temporal.Calendar.prototype.dayOfWeek throws RangeError on
+ ToTemporalDate when temporalDateLike is invalid string.
+info: |
+ 4. Let temporalDate be ? ToTemporalDate(temporalDateLike).
+features: [Temporal, arrow-function]
+---*/
+let cal = new Temporal.Calendar("iso8601");
+
+assert.throws(RangeError, () => cal.dayOfWeek("invalid string"),
+ 'cal.dayOfWeek("invalid string") throws a RangeError exception');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/year-zero.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/year-zero.js
new file mode 100644
index 0000000000..fadd2ebc31
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfWeek/year-zero.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.calendar.prototype.dayofweek
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T00:45",
+ "-000000-10-31T00:45+01:00",
+ "-000000-10-31T00:45+00:00[UTC]",
+];
+const instance = new Temporal.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.dayOfWeek(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..dd7cea23e4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-builtin-calendar-no-array-iteration.js
@@ -0,0 +1,24 @@
+// |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.calendar.prototype.dayofyear
+description: >
+ Calling the method with a property bag argument with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.Calendar("iso8601");
+const arg = { year: 2000, month: 5, day: 2, calendar: "iso8601" };
+instance.dayOfYear(arg);
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-calendar-datefromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..9520c825bd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-calendar-datefromfields-called-with-null-prototype-fields.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.calendar.prototype.dayofyear
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.Calendar("iso8601");
+const arg = { year: 2000, month: 5, day: 2, calendar };
+instance.dayOfYear(arg);
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..559093bf44
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-constructor-in-calendar-fields.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.calendar.prototype.dayofyear
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.Calendar("iso8601");
+
+assert.throws(RangeError, () => instance.dayOfYear(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-duplicate-calendar-fields.js
new file mode 100644
index 0000000000..c8c47a6454
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-duplicate-calendar-fields.js
@@ -0,0 +1,19 @@
+// |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.calendar.prototype.dayofyear
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['day'], ['month'], ['monthCode'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar };
+ const instance = new Temporal.Calendar("iso8601");
+
+ assert.throws(RangeError, () => instance.dayOfYear(arg));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-leap-second.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-leap-second.js
new file mode 100644
index 0000000000..d9dbd26e92
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-leap-second.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.calendar.prototype.dayofyear
+description: Leap second is a valid ISO string for PlainDate
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+let arg = "2016-12-31T23:59:60";
+const result1 = instance.dayOfYear(arg);
+assert.sameValue(
+ result1,
+ 366,
+ "leap second is a valid ISO string for PlainDate"
+);
+
+arg = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+const result2 = instance.dayOfYear(arg);
+assert.sameValue(
+ result2,
+ 366,
+ "second: 60 is ignored in property bag for PlainDate"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-number.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-number.js
new file mode 100644
index 0000000000..b2c92dc6e5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/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.calendar.prototype.dayofyear
+description: A number cannot be used in place of a Temporal.PlainDate
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const numbers = [
+ 1,
+ 19761118,
+ -19761118,
+ 1234567890,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => instance.dayOfYear(arg),
+ 'Numbers cannot be used in place of an ISO string for PlainDate'
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-propertybag-calendar-case-insensitive.js
new file mode 100644
index 0000000000..1123d096e3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-propertybag-calendar-case-insensitive.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.calendar.prototype.dayofyear
+description: The calendar name is case-insensitive
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const calendar = "IsO8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.dayOfYear(arg);
+assert.sameValue(result, 323, "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-propertybag-calendar-leap-second.js
new file mode 100644
index 0000000000..ec673db6fd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-propertybag-calendar-leap-second.js
@@ -0,0 +1,23 @@
+// |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.calendar.prototype.dayofyear
+description: Leap second is a valid ISO string for a calendar in a property bag
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const calendar = "2016-12-31T23:59:60";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.dayOfYear(arg);
+assert.sameValue(
+ result,
+ 323,
+ "leap second is a valid ISO string for calendar"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-propertybag-calendar-number.js
new file mode 100644
index 0000000000..bc418cddab
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-propertybag-calendar-number.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.calendar.prototype.dayofyear
+description: A number as calendar in a property bag is not accepted
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const numbers = [
+ 1,
+ 19970327,
+ -19970327,
+ 1234567890,
+];
+
+for (const calendar of numbers) {
+ const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+ assert.throws(
+ TypeError,
+ () => instance.dayOfYear(arg),
+ "Numbers cannot be used as a calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-propertybag-calendar-string.js
new file mode 100644
index 0000000000..91ebe03687
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-propertybag-calendar-string.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.calendar.prototype.dayofyear
+description: A calendar ID is valid input for Calendar
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const calendar = "iso8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.dayOfYear(arg);
+assert.sameValue(result, 323, `Calendar created from string "${calendar}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-propertybag-calendar-wrong-type.js
new file mode 100644
index 0000000000..7130706abb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-propertybag-calendar-wrong-type.js
@@ -0,0 +1,46 @@
+// |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.calendar.prototype.dayofyear
+description: >
+ Appropriate error thrown when a calendar property from a property bag cannot
+ be converted to a calendar object or string
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.Calendar("iso8601");
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [calendar, description] of primitiveTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ typeof calendar === 'string' ? RangeError : TypeError,
+ () => instance.dayOfYear(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+ [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields()
+];
+
+for (const [calendar, description] of typeErrorTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(TypeError, () => instance.dayOfYear(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/Calendar/prototype/dayOfYear/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-propertybag-calendar-year-zero.js
new file mode 100644
index 0000000000..7f06c31917
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-propertybag-calendar-year-zero.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.calendar.prototype.dayofyear
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T17:45",
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+01:00",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+const instance = new Temporal.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.dayOfYear(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-proto-in-calendar-fields.js
new file mode 100644
index 0000000000..829564e665
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-proto-in-calendar-fields.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.calendar.prototype.dayofyear
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.Calendar("iso8601");
+
+assert.throws(RangeError, () => instance.dayOfYear(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..09956daf3a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-string-calendar-annotation.js
@@ -0,0 +1,33 @@
+// |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.calendar.prototype.dayofyear
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02[u-ca=iso8601]", "without time or time zone"],
+ ["2000-05-02[UTC][u-ca=iso8601]", "with time zone and no time"],
+ ["2000-05-02T15:23[u-ca=iso8601]", "without time zone"],
+ ["2000-05-02T15:23[UTC][u-ca=iso8601]", "with time zone"],
+ ["2000-05-02T15:23[!u-ca=iso8601]", "with ! and no time zone"],
+ ["2000-05-02T15:23[UTC][!u-ca=iso8601]", "with ! and time zone"],
+ ["2000-05-02T15:23[u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+const instance = new Temporal.Calendar("iso8601");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.dayOfYear(arg);
+
+ assert.sameValue(
+ result,
+ 123,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..48b3724340
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-string-critical-unknown-annotation.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.calendar.prototype.dayofyear
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[!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.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.dayOfYear(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..445ee6aeff
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-string-date-with-utc-offset.js
@@ -0,0 +1,48 @@
+// |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.calendar.prototype.dayofyear
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const validStrings = [
+ "2000-05-02T00+00:00",
+ "2000-05-02T00+00:00[UTC]",
+ "2000-05-02T00+00:00[!UTC]",
+ "2000-05-02T00-02:30[America/St_Johns]",
+];
+
+for (const arg of validStrings) {
+ const result = instance.dayOfYear(arg);
+
+ assert.sameValue(
+ result,
+ 123,
+ `"${arg}" is a valid UTC offset with time for PlainDate`
+ );
+}
+
+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.dayOfYear(arg),
+ `"${arg}" UTC offset without time is not valid for PlainDate`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-string-invalid.js
new file mode 100644
index 0000000000..37167a97d6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-string-invalid.js
@@ -0,0 +1,64 @@
+// |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.calendar.prototype.dayofyear
+description: >
+ RangeError thrown if an invalid ISO string (or syntactically valid ISO string
+ that is not supported) is used as a PlainDate
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ // invalid ISO strings:
+ "",
+ "invalid iso8601",
+ "2020-01-00",
+ "2020-01-32",
+ "2020-02-30",
+ "2021-02-29",
+ "2020-00-01",
+ "2020-13-01",
+ "2020-01-01T",
+ "2020-01-01T25:00:00",
+ "2020-01-01T01:60:00",
+ "2020-01-01T01:60:61",
+ "2020-01-01junk",
+ "2020-01-01T00:00:00junk",
+ "2020-01-01T00:00:00+00:00junk",
+ "2020-01-01T00:00:00+00:00[UTC]junk",
+ "2020-01-01T00:00:00+00:00[UTC][u-ca=iso8601]junk",
+ "02020-01-01",
+ "2020-001-01",
+ "2020-01-001",
+ "2020-01-01T001",
+ "2020-01-01T01:001",
+ "2020-01-01T01:01:001",
+ // valid, but forms not supported in Temporal:
+ "2020-W01-1",
+ "2020-001",
+ "+0002020-01-01",
+ // valid, but this calendar must not exist:
+ "2020-01-01[u-ca=notexist]",
+ // may be valid in other contexts, but insufficient information for PlainDate:
+ "2020-01",
+ "+002020-01",
+ "01-01",
+ "2020-W01",
+ "P1Y",
+ "-P12Y",
+ // valid, but outside the supported range:
+ "-999999-01-01",
+ "+999999-01-01",
+];
+const instance = new Temporal.Calendar("iso8601");
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => instance.dayOfYear(arg),
+ `"${arg}" should not be a valid ISO string for a PlainDate`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..e8b2714c5c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/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.calendar.prototype.dayofyear
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[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.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.dayOfYear(arg),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..38dc81a454
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-string-multiple-time-zone.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.calendar.prototype.dayofyear
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[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.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.dayOfYear(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-string-time-separators.js
new file mode 100644
index 0000000000..6399c49752
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-string-time-separators.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.calendar.prototype.dayofyear
+description: Time separator in string argument can vary
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02T15:23", "uppercase T"],
+ ["2000-05-02t15:23", "lowercase T"],
+ ["2000-05-02 15:23", "space between date and time"],
+];
+
+const instance = new Temporal.Calendar("iso8601");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.dayOfYear(arg);
+
+ assert.sameValue(
+ result,
+ 123,
+ `variant time separators (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..e266fc22f5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-string-time-zone-annotation.js
@@ -0,0 +1,38 @@
+// |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.calendar.prototype.dayofyear
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02[Asia/Kolkata]", "named, with no time"],
+ ["2000-05-02[!Europe/Vienna]", "named, with ! and no time"],
+ ["2000-05-02[+00:00]", "numeric, with no time"],
+ ["2000-05-02[!-02:30]", "numeric, with ! and no time"],
+ ["2000-05-02T15:23[America/Sao_Paulo]", "named, with no offset"],
+ ["2000-05-02T15:23[!Asia/Tokyo]", "named, with ! and no offset"],
+ ["2000-05-02T15:23[-02:30]", "numeric, with no offset"],
+ ["2000-05-02T15:23[!+00:00]", "numeric, with ! and no offset"],
+ ["2000-05-02T15:23+00:00[America/New_York]", "named, with offset"],
+ ["2000-05-02T15:23+00:00[!UTC]", "named, with offset and !"],
+ ["2000-05-02T15:23+00:00[+01:00]", "numeric, with offset"],
+ ["2000-05-02T15:23+00:00[!-08:00]", "numeric, with offset and !"],
+];
+
+const instance = new Temporal.Calendar("iso8601");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.dayOfYear(arg);
+
+ assert.sameValue(
+ result,
+ 123,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..e7edf6df42
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-string-unknown-annotation.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.calendar.prototype.dayofyear
+description: Various forms of unknown annotation
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02[foo=bar]", "without time"],
+ ["2000-05-02T15:23[foo=bar]", "alone"],
+ ["2000-05-02T15:23[UTC][foo=bar]", "with time zone"],
+ ["2000-05-02T15:23[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["2000-05-02T15:23[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["2000-05-02T15:23[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+const instance = new Temporal.Calendar("iso8601");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.dayOfYear(arg);
+
+ assert.sameValue(
+ result,
+ 123,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..e0ee9879bc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-string-with-utc-designator.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.calendar.prototype.dayofyear
+description: RangeError thrown if a string with UTC designator is used as a PlainDate
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "2019-10-01T09:00:00Z",
+ "2019-10-01T09:00:00Z[UTC]",
+];
+const instance = new Temporal.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.dayOfYear(arg),
+ "String with UTC designator should not be valid as a PlainDate"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-wrong-type.js
new file mode 100644
index 0000000000..d607a28071
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/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.calendar.prototype.dayofyear
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainDate
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+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.dayOfYear(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.PlainDate, "Temporal.PlainDate, object"],
+ [Temporal.PlainDate.prototype, "Temporal.PlainDate.prototype, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.dayOfYear(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/Calendar/prototype/dayOfYear/argument-zoneddatetime-convert.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-zoneddatetime-convert.js
new file mode 100644
index 0000000000..fda318c39a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-zoneddatetime-convert.js
@@ -0,0 +1,22 @@
+// |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.calendar.prototype.dayofyear
+description: An exception from TimeZone#getOffsetNanosecondsFor() is propagated.
+features: [Temporal]
+---*/
+
+class TZ extends Temporal.TimeZone {
+ constructor() { super("UTC") }
+ getOffsetNanosecondsFor() { throw new Test262Error() }
+}
+
+const tz = new TZ();
+const arg = new Temporal.ZonedDateTime(0n, tz);
+const instance = new Temporal.Calendar("iso8601");
+
+assert.throws(Test262Error, () => instance.dayOfYear(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-zoneddatetime-slots.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-zoneddatetime-slots.js
new file mode 100644
index 0000000000..39bcc444b8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-zoneddatetime-slots.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.calendar.prototype.dayofyear
+description: Getters are not called when converting a ZonedDateTime to a PlainDate.
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.ZonedDateTime.prototype);
+const getters = ["year", "month", "monthCode", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond", "calendar"];
+
+for (const property of getters) {
+ Object.defineProperty(Temporal.ZonedDateTime.prototype, property, {
+ get() {
+ actual.push(`get ${property}`);
+ const value = prototypeDescrs[property].get.call(this);
+ return {
+ toString() {
+ actual.push(`toString ${property}`);
+ return value.toString();
+ },
+ valueOf() {
+ actual.push(`valueOf ${property}`);
+ return value;
+ },
+ };
+ },
+ });
+}
+
+const arg = new Temporal.ZonedDateTime(0n, "UTC");
+const instance = new Temporal.Calendar("iso8601");
+instance.dayOfYear(arg);
+assert.compareArray(actual, []);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..efbbd704cd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/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.calendar.prototype.dayofyear
+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 calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => calendar.dayOfYear(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..cca1006fd3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/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.calendar.prototype.dayofyear
+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 calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => calendar.dayOfYear(datetime),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..d9b51556fe
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/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.calendar.prototype.dayofyear
+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 calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => calendar.dayOfYear(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..87c99d26c2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/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.calendar.prototype.dayofyear
+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 calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => calendar.dayOfYear(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/basic.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/basic.js
new file mode 100644
index 0000000000..b391523d47
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/basic.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.calendar.prototype.dayofyear
+description: Basic tests for dayOfYear().
+features: [Temporal]
+---*/
+
+const iso = Temporal.Calendar.from("iso8601");
+const res = 309;
+assert.sameValue(iso.dayOfYear(Temporal.PlainDate.from("1994-11-05")), res, "PlainDate");
+assert.sameValue(iso.dayOfYear(Temporal.PlainDateTime.from("1994-11-05T08:15:30")), res, "PlainDateTime");
+assert.sameValue(iso.dayOfYear({ year: 1994, month: 11, day: 5 }), res, "property bag");
+assert.sameValue(iso.dayOfYear("1994-11-05"), res, "string");
+assert.throws(TypeError, () => iso.dayOfYear({ year: 2000 }), "property bag with missing properties");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/branding.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/branding.js
new file mode 100644
index 0000000000..e20d743ae3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/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.calendar.prototype.dayofyear
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const dayOfYear = Temporal.Calendar.prototype.dayOfYear;
+
+assert.sameValue(typeof dayOfYear, "function");
+
+const args = [new Temporal.PlainDate(2000, 1, 1)];
+
+assert.throws(TypeError, () => dayOfYear.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => dayOfYear.apply(null, args), "null");
+assert.throws(TypeError, () => dayOfYear.apply(true, args), "true");
+assert.throws(TypeError, () => dayOfYear.apply("", args), "empty string");
+assert.throws(TypeError, () => dayOfYear.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => dayOfYear.apply(1, args), "1");
+assert.throws(TypeError, () => dayOfYear.apply({}, args), "plain object");
+assert.throws(TypeError, () => dayOfYear.apply(Temporal.Calendar, args), "Temporal.Calendar");
+assert.throws(TypeError, () => dayOfYear.apply(Temporal.Calendar.prototype, args), "Temporal.Calendar.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/browser.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/builtin.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/builtin.js
new file mode 100644
index 0000000000..8c5ea8047e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/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.calendar.prototype.dayofyear
+description: >
+ Tests that Temporal.Calendar.prototype.dayOfYear
+ 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.Calendar.prototype.dayOfYear),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Calendar.prototype.dayOfYear),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Calendar.prototype.dayOfYear),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Calendar.prototype.dayOfYear.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/calendar-datefromfields-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/calendar-datefromfields-called-with-options-undefined.js
new file mode 100644
index 0000000000..465e3b9c01
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/calendar-datefromfields-called-with-options-undefined.js
@@ -0,0 +1,18 @@
+// |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.calendar.prototype.dayofyear
+description: >
+ Calendar.dateFromFields method is called with undefined as the options value
+ when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
+calendar.dayOfYear({ year: 2000, month: 5, day: 3, calendar });
+assert.sameValue(calendar.dateFromFieldsCallCount, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/calendar-fields-iterable.js
new file mode 100644
index 0000000000..1d794405c3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/calendar-fields-iterable.js
@@ -0,0 +1,35 @@
+// |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.calendar.prototype.dayofyear
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.calendar.prototype.dayofyear step 4:
+ 4. Let _date_ be ? ToTemporalDate(_dateOrDateTime_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToListOfType(_fieldsArray_, « String »).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+calendar1.dayOfYear({ year: 2000, month: 5, day: 2, calendar: calendar2 });
+
+assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/calendar-temporal-object.js
new file mode 100644
index 0000000000..614b2cd72a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/calendar-temporal-object.js
@@ -0,0 +1,29 @@
+// |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.calendar.prototype.dayofyear
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.calendar.prototype.dayofyear step 4:
+ 4. Let _date_ be ? ToTemporalDate(_dateOrDateTime_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const calendar = new Temporal.Calendar("iso8601");
+ calendar.dayOfYear({ year: 2000, month: 5, day: 2, calendar: temporalObject });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..9594bb6ded
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/infinity-throws-rangeerror.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.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.calendar.prototype.dayofyear
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+const base = { year: 2000, month: 5, day: 2 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.dayOfYear({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.dayOfYear({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/length.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/length.js
new file mode 100644
index 0000000000..005e044216
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/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.calendar.prototype.dayofyear
+description: Temporal.Calendar.prototype.dayOfYear.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.Calendar.prototype.dayOfYear, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/name.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/name.js
new file mode 100644
index 0000000000..bc9dccc138
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/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.calendar.prototype.dayofyear
+description: Temporal.Calendar.prototype.dayOfYear.name is "dayOfYear".
+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.Calendar.prototype.dayOfYear, "name", {
+ value: "dayOfYear",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/not-a-constructor.js
new file mode 100644
index 0000000000..b40bd8ee27
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/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.calendar.prototype.dayofyear
+description: >
+ Temporal.Calendar.prototype.dayOfYear 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.Calendar.prototype.dayOfYear();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Calendar.prototype.dayOfYear), false,
+ "isConstructor(Temporal.Calendar.prototype.dayOfYear)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/plain-date-time.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/plain-date-time.js
new file mode 100644
index 0000000000..58cc7d9670
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/plain-date-time.js
@@ -0,0 +1,58 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.calendar.prototype.dayofyear
+description: >
+ Temporal.Calendar.prototype.dayOfYear will take PlainDateTime object and
+ return the day of year.
+info: |
+ 4. Let temporalDate be ? ToTemporalDate(temporalDateLike).
+ 5. Return 𝔽(! ToISODayOfYear(temporalDate.[[ISOYear]], temporalDate.[[ISOMonth]], temporalDate.[[ISODay]])).
+features: [Temporal]
+---*/
+let cal = new Temporal.Calendar("iso8601");
+
+let dt = new Temporal.PlainDateTime(1997, 1, 23, 5, 30, 13);
+assert.sameValue(
+ cal.dayOfYear(dt),
+ 23,
+ 'cal.dayOfYear(new Temporal.PlainDateTime(1997, 1, 23, 5, 30, 13)) must return 23'
+);
+
+dt = new Temporal.PlainDateTime(1997, 2, 23, 5, 30, 13);
+assert.sameValue(
+ cal.dayOfYear(dt),
+ 54,
+ 'cal.dayOfYear(new Temporal.PlainDateTime(1997, 2, 23, 5, 30, 13)) must return 54'
+);
+
+dt = new Temporal.PlainDateTime(1996, 3, 23, 5, 30, 13);
+assert.sameValue(
+ cal.dayOfYear(dt),
+ 83,
+ 'cal.dayOfYear(new Temporal.PlainDateTime(1996, 3, 23, 5, 30, 13)) must return 83'
+);
+
+dt = new Temporal.PlainDateTime(1997, 3, 23, 5, 30, 13);
+assert.sameValue(
+ cal.dayOfYear(dt),
+ 82,
+ 'cal.dayOfYear(new Temporal.PlainDateTime(1997, 3, 23, 5, 30, 13)) must return 82'
+);
+
+dt = new Temporal.PlainDateTime(1997, 12, 31, 5, 30, 13);
+assert.sameValue(
+ cal.dayOfYear(dt),
+ 365,
+ 'cal.dayOfYear(new Temporal.PlainDateTime(1997, 12, 31, 5, 30, 13)) must return 365'
+);
+
+dt = new Temporal.PlainDateTime(1996, 12, 31, 5, 30, 13);
+assert.sameValue(
+ cal.dayOfYear(dt),
+ 366,
+ 'cal.dayOfYear(new Temporal.PlainDateTime(1996, 12, 31, 5, 30, 13)) must return 366'
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/plain-date.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/plain-date.js
new file mode 100644
index 0000000000..59ae28381a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/plain-date.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.calendar.prototype.dayofyear
+description: >
+ Temporal.Calendar.prototype.dayOfYear will take PlainDate object and
+ return the day of year.
+info: |
+ 5. Return 𝔽(! ToISODayOfYear(temporalDate.[[ISOYear]], temporalDate.[[ISOMonth]], temporalDate.[[ISODay]])).
+features: [Temporal]
+---*/
+let cal = new Temporal.Calendar("iso8601");
+
+let d = new Temporal.PlainDate(1970, 1, 1);
+assert.sameValue(cal.dayOfYear(d), 1, 'cal.dayOfYear(new Temporal.PlainDate(1970, 1, 1)) must return 1');
+d = new Temporal.PlainDate(2000, 1, 1);
+assert.sameValue(cal.dayOfYear(d), 1, 'cal.dayOfYear(new Temporal.PlainDate(2000, 1, 1)) must return 1');
+
+d = new Temporal.PlainDate(2021, 1, 15);
+assert.sameValue(cal.dayOfYear(d), 15, 'cal.dayOfYear(new Temporal.PlainDate(2021, 1, 15)) must return 15');
+
+d = new Temporal.PlainDate(2020, 2, 15);
+assert.sameValue(cal.dayOfYear(d), 46, 'cal.dayOfYear(new Temporal.PlainDate(2020, 2, 15)) must return 46');
+
+d = new Temporal.PlainDate(2020, 3, 15);
+assert.sameValue(cal.dayOfYear(d), 75, 'cal.dayOfYear(new Temporal.PlainDate(2020, 3, 15)) must return 75');
+
+d = new Temporal.PlainDate(2000, 3, 15);
+assert.sameValue(cal.dayOfYear(d), 75, 'cal.dayOfYear(new Temporal.PlainDate(2000, 3, 15)) must return 75');
+
+d = new Temporal.PlainDate(2001, 3, 15);
+assert.sameValue(cal.dayOfYear(d), 74, 'cal.dayOfYear(new Temporal.PlainDate(2001, 3, 15)) must return 74');
+
+d = new Temporal.PlainDate(2000, 12, 31);
+assert.sameValue(cal.dayOfYear(d), 366, 'cal.dayOfYear(new Temporal.PlainDate(2000, 12, 31)) must return 366');
+
+d = new Temporal.PlainDate(2001, 12, 31);
+assert.sameValue(cal.dayOfYear(d), 365, 'cal.dayOfYear(new Temporal.PlainDate(2001, 12, 31)) must return 365');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/prop-desc.js
new file mode 100644
index 0000000000..36da5f9f97
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/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.calendar.prototype.dayofyear
+description: The "dayOfYear" property of Temporal.Calendar.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Calendar.prototype.dayOfYear,
+ "function",
+ "`typeof Calendar.prototype.dayOfYear` is `function`"
+);
+
+verifyProperty(Temporal.Calendar.prototype, "dayOfYear", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/shell.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/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/Calendar/prototype/dayOfYear/string.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/string.js
new file mode 100644
index 0000000000..1faed72a64
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/string.js
@@ -0,0 +1,37 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.calendar.prototype.dayofyear
+description: >
+ Temporal.Calendar.prototype.dayOfYear will take ISO8601 string and
+ return the day of year.
+info: |
+ 4. Let temporalDate be ? ToTemporalDate(temporalDateLike).
+ 5. Return 𝔽(! ToISODayOfYear(temporalDate.[[ISOYear]], temporalDate.[[ISOMonth]], temporalDate.[[ISODay]])).
+features: [Temporal]
+---*/
+let cal = new Temporal.Calendar("iso8601");
+
+assert.sameValue(
+ cal.dayOfYear("2019-01-18"),
+ 18,
+ 'cal.dayOfYear("2019-01-18") must return 18'
+);
+assert.sameValue(
+ cal.dayOfYear("2020-02-18"),
+ 49,
+ 'cal.dayOfYear("2020-02-18") must return 49'
+);
+assert.sameValue(
+ cal.dayOfYear("2019-12-31"),
+ 365,
+ 'cal.dayOfYear("2019-12-31") must return 365'
+);
+assert.sameValue(
+ cal.dayOfYear("2000-12-31"),
+ 366,
+ 'cal.dayOfYear("2000-12-31") must return 366'
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/throw-range-error-ToTemporalDate.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/throw-range-error-ToTemporalDate.js
new file mode 100644
index 0000000000..52080766e5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/throw-range-error-ToTemporalDate.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.calendar.prototype.dayOfYear
+description: >
+ Temporal.Calendar.prototype.dayOfYear throws RangeError on
+ ToTemporalDate when temporalDateLike is invalid string.
+info: |
+ 4. Let temporalDate be ? ToTemporalDate(temporalDateLike).
+features: [Temporal, arrow-function]
+---*/
+let cal = new Temporal.Calendar("iso8601");
+
+assert.throws(RangeError, () => cal.dayOfYear("invalid string"),
+ 'cal.dayOfYear("invalid string") throws a RangeError exception');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/year-zero.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/year-zero.js
new file mode 100644
index 0000000000..27c08f65f3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/dayOfYear/year-zero.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.calendar.prototype.dayofyear
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T00:45",
+ "-000000-10-31T00:45+01:00",
+ "-000000-10-31T00:45+00:00[UTC]",
+];
+const instance = new Temporal.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.dayOfYear(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..319534ca4f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-builtin-calendar-no-array-iteration.js
@@ -0,0 +1,24 @@
+// |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.calendar.prototype.daysinmonth
+description: >
+ Calling the method with a property bag argument with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.Calendar("iso8601");
+const arg = { year: 2000, month: 5, day: 2, calendar: "iso8601" };
+instance.daysInMonth(arg);
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-calendar-datefromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..1198405424
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-calendar-datefromfields-called-with-null-prototype-fields.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.calendar.prototype.daysinmonth
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.Calendar("iso8601");
+const arg = { year: 2000, month: 5, day: 2, calendar };
+instance.daysInMonth(arg);
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..50db14415b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-constructor-in-calendar-fields.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.calendar.prototype.daysinmonth
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.Calendar("iso8601");
+
+assert.throws(RangeError, () => instance.daysInMonth(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-duplicate-calendar-fields.js
new file mode 100644
index 0000000000..d183b4ee69
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-duplicate-calendar-fields.js
@@ -0,0 +1,19 @@
+// |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.calendar.prototype.daysinmonth
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['day'], ['month'], ['monthCode'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar };
+ const instance = new Temporal.Calendar("iso8601");
+
+ assert.throws(RangeError, () => instance.daysInMonth(arg));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-leap-second.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-leap-second.js
new file mode 100644
index 0000000000..30e56f42b2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-leap-second.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.calendar.prototype.daysinmonth
+description: Leap second is a valid ISO string for PlainDate
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+let arg = "2016-12-31T23:59:60";
+const result1 = instance.daysInMonth(arg);
+assert.sameValue(
+ result1,
+ 31,
+ "leap second is a valid ISO string for PlainDate"
+);
+
+arg = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+const result2 = instance.daysInMonth(arg);
+assert.sameValue(
+ result2,
+ 31,
+ "second: 60 is ignored in property bag for PlainDate"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-number.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-number.js
new file mode 100644
index 0000000000..e2f40427c9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/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.calendar.prototype.daysinmonth
+description: A number cannot be used in place of a Temporal.PlainDate
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const numbers = [
+ 1,
+ 19761118,
+ -19761118,
+ 1234567890,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => instance.daysInMonth(arg),
+ 'Numbers cannot be used in place of an ISO string for PlainDate'
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-propertybag-calendar-case-insensitive.js
new file mode 100644
index 0000000000..bcda4dbf08
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-propertybag-calendar-case-insensitive.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.calendar.prototype.daysinmonth
+description: The calendar name is case-insensitive
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const calendar = "IsO8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.daysInMonth(arg);
+assert.sameValue(result, 30, "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-propertybag-calendar-leap-second.js
new file mode 100644
index 0000000000..bd6efd827b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-propertybag-calendar-leap-second.js
@@ -0,0 +1,23 @@
+// |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.calendar.prototype.daysinmonth
+description: Leap second is a valid ISO string for a calendar in a property bag
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const calendar = "2016-12-31T23:59:60";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.daysInMonth(arg);
+assert.sameValue(
+ result,
+ 30,
+ "leap second is a valid ISO string for calendar"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-propertybag-calendar-number.js
new file mode 100644
index 0000000000..4a2e0fe015
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-propertybag-calendar-number.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.calendar.prototype.daysinmonth
+description: A number as calendar in a property bag is not accepted
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const numbers = [
+ 1,
+ 19970327,
+ -19970327,
+ 1234567890,
+];
+
+for (const calendar of numbers) {
+ const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+ assert.throws(
+ TypeError,
+ () => instance.daysInMonth(arg),
+ "Numbers cannot be used as a calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-propertybag-calendar-string.js
new file mode 100644
index 0000000000..8f207d8388
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-propertybag-calendar-string.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.calendar.prototype.daysinmonth
+description: A calendar ID is valid input for Calendar
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const calendar = "iso8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.daysInMonth(arg);
+assert.sameValue(result, 30, `Calendar created from string "${calendar}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-propertybag-calendar-wrong-type.js
new file mode 100644
index 0000000000..beb74d902f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-propertybag-calendar-wrong-type.js
@@ -0,0 +1,46 @@
+// |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.calendar.prototype.daysinmonth
+description: >
+ Appropriate error thrown when a calendar property from a property bag cannot
+ be converted to a calendar object or string
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.Calendar("iso8601");
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [calendar, description] of primitiveTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ typeof calendar === 'string' ? RangeError : TypeError,
+ () => instance.daysInMonth(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+ [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields()
+];
+
+for (const [calendar, description] of typeErrorTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(TypeError, () => instance.daysInMonth(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/Calendar/prototype/daysInMonth/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-propertybag-calendar-year-zero.js
new file mode 100644
index 0000000000..8a6f651473
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-propertybag-calendar-year-zero.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.calendar.prototype.daysinmonth
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T17:45",
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+01:00",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+const instance = new Temporal.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.daysInMonth(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-proto-in-calendar-fields.js
new file mode 100644
index 0000000000..54de92486e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-proto-in-calendar-fields.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.calendar.prototype.daysinmonth
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.Calendar("iso8601");
+
+assert.throws(RangeError, () => instance.daysInMonth(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..4a0ed3ba9f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-string-calendar-annotation.js
@@ -0,0 +1,33 @@
+// |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.calendar.prototype.daysinmonth
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02[u-ca=iso8601]", "without time or time zone"],
+ ["2000-05-02[UTC][u-ca=iso8601]", "with time zone and no time"],
+ ["2000-05-02T15:23[u-ca=iso8601]", "without time zone"],
+ ["2000-05-02T15:23[UTC][u-ca=iso8601]", "with time zone"],
+ ["2000-05-02T15:23[!u-ca=iso8601]", "with ! and no time zone"],
+ ["2000-05-02T15:23[UTC][!u-ca=iso8601]", "with ! and time zone"],
+ ["2000-05-02T15:23[u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+const instance = new Temporal.Calendar("iso8601");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.daysInMonth(arg);
+
+ assert.sameValue(
+ result,
+ 31,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..fd0f58ec15
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-string-critical-unknown-annotation.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.calendar.prototype.daysinmonth
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[!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.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.daysInMonth(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..8a7a746a83
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-string-date-with-utc-offset.js
@@ -0,0 +1,48 @@
+// |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.calendar.prototype.daysinmonth
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const validStrings = [
+ "2000-05-02T00+00:00",
+ "2000-05-02T00+00:00[UTC]",
+ "2000-05-02T00+00:00[!UTC]",
+ "2000-05-02T00-02:30[America/St_Johns]",
+];
+
+for (const arg of validStrings) {
+ const result = instance.daysInMonth(arg);
+
+ assert.sameValue(
+ result,
+ 31,
+ `"${arg}" is a valid UTC offset with time for PlainDate`
+ );
+}
+
+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.daysInMonth(arg),
+ `"${arg}" UTC offset without time is not valid for PlainDate`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-string-invalid.js
new file mode 100644
index 0000000000..5d4b66354e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-string-invalid.js
@@ -0,0 +1,64 @@
+// |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.calendar.prototype.daysinmonth
+description: >
+ RangeError thrown if an invalid ISO string (or syntactically valid ISO string
+ that is not supported) is used as a PlainDate
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ // invalid ISO strings:
+ "",
+ "invalid iso8601",
+ "2020-01-00",
+ "2020-01-32",
+ "2020-02-30",
+ "2021-02-29",
+ "2020-00-01",
+ "2020-13-01",
+ "2020-01-01T",
+ "2020-01-01T25:00:00",
+ "2020-01-01T01:60:00",
+ "2020-01-01T01:60:61",
+ "2020-01-01junk",
+ "2020-01-01T00:00:00junk",
+ "2020-01-01T00:00:00+00:00junk",
+ "2020-01-01T00:00:00+00:00[UTC]junk",
+ "2020-01-01T00:00:00+00:00[UTC][u-ca=iso8601]junk",
+ "02020-01-01",
+ "2020-001-01",
+ "2020-01-001",
+ "2020-01-01T001",
+ "2020-01-01T01:001",
+ "2020-01-01T01:01:001",
+ // valid, but forms not supported in Temporal:
+ "2020-W01-1",
+ "2020-001",
+ "+0002020-01-01",
+ // valid, but this calendar must not exist:
+ "2020-01-01[u-ca=notexist]",
+ // may be valid in other contexts, but insufficient information for PlainDate:
+ "2020-01",
+ "+002020-01",
+ "01-01",
+ "2020-W01",
+ "P1Y",
+ "-P12Y",
+ // valid, but outside the supported range:
+ "-999999-01-01",
+ "+999999-01-01",
+];
+const instance = new Temporal.Calendar("iso8601");
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => instance.daysInMonth(arg),
+ `"${arg}" should not be a valid ISO string for a PlainDate`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..a6b940ebf0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/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.calendar.prototype.daysinmonth
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[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.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.daysInMonth(arg),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..5087ca2fe5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-string-multiple-time-zone.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.calendar.prototype.daysinmonth
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[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.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.daysInMonth(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-string-time-separators.js
new file mode 100644
index 0000000000..049dd56d85
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-string-time-separators.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.calendar.prototype.daysinmonth
+description: Time separator in string argument can vary
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02T15:23", "uppercase T"],
+ ["2000-05-02t15:23", "lowercase T"],
+ ["2000-05-02 15:23", "space between date and time"],
+];
+
+const instance = new Temporal.Calendar("iso8601");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.daysInMonth(arg);
+
+ assert.sameValue(
+ result,
+ 31,
+ `variant time separators (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..7432b240c0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-string-time-zone-annotation.js
@@ -0,0 +1,38 @@
+// |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.calendar.prototype.daysinmonth
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02[Asia/Kolkata]", "named, with no time"],
+ ["2000-05-02[!Europe/Vienna]", "named, with ! and no time"],
+ ["2000-05-02[+00:00]", "numeric, with no time"],
+ ["2000-05-02[!-02:30]", "numeric, with ! and no time"],
+ ["2000-05-02T15:23[America/Sao_Paulo]", "named, with no offset"],
+ ["2000-05-02T15:23[!Asia/Tokyo]", "named, with ! and no offset"],
+ ["2000-05-02T15:23[-02:30]", "numeric, with no offset"],
+ ["2000-05-02T15:23[!+00:00]", "numeric, with ! and no offset"],
+ ["2000-05-02T15:23+00:00[America/New_York]", "named, with offset"],
+ ["2000-05-02T15:23+00:00[!UTC]", "named, with offset and !"],
+ ["2000-05-02T15:23+00:00[+01:00]", "numeric, with offset"],
+ ["2000-05-02T15:23+00:00[!-08:00]", "numeric, with offset and !"],
+];
+
+const instance = new Temporal.Calendar("iso8601");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.daysInMonth(arg);
+
+ assert.sameValue(
+ result,
+ 31,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..7b9329b45f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-string-unknown-annotation.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.calendar.prototype.daysinmonth
+description: Various forms of unknown annotation
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02[foo=bar]", "without time"],
+ ["2000-05-02T15:23[foo=bar]", "alone"],
+ ["2000-05-02T15:23[UTC][foo=bar]", "with time zone"],
+ ["2000-05-02T15:23[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["2000-05-02T15:23[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["2000-05-02T15:23[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+const instance = new Temporal.Calendar("iso8601");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.daysInMonth(arg);
+
+ assert.sameValue(
+ result,
+ 31,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..2f221212ea
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-string-with-utc-designator.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.calendar.prototype.daysinmonth
+description: RangeError thrown if a string with UTC designator is used as a PlainDate
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "2019-10-01T09:00:00Z",
+ "2019-10-01T09:00:00Z[UTC]",
+];
+const instance = new Temporal.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.daysInMonth(arg),
+ "String with UTC designator should not be valid as a PlainDate"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-wrong-type.js
new file mode 100644
index 0000000000..074c5479f8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/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.calendar.prototype.daysinmonth
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainDate
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+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.daysInMonth(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.PlainDate, "Temporal.PlainDate, object"],
+ [Temporal.PlainDate.prototype, "Temporal.PlainDate.prototype, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.daysInMonth(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/Calendar/prototype/daysInMonth/argument-zoneddatetime-convert.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-zoneddatetime-convert.js
new file mode 100644
index 0000000000..4d826cd5b7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-zoneddatetime-convert.js
@@ -0,0 +1,22 @@
+// |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.calendar.prototype.daysinmonth
+description: An exception from TimeZone#getOffsetNanosecondsFor() is propagated.
+features: [Temporal]
+---*/
+
+class TZ extends Temporal.TimeZone {
+ constructor() { super("UTC") }
+ getOffsetNanosecondsFor() { throw new Test262Error() }
+}
+
+const tz = new TZ();
+const arg = new Temporal.ZonedDateTime(0n, tz);
+const instance = new Temporal.Calendar("iso8601");
+
+assert.throws(Test262Error, () => instance.daysInMonth(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-zoneddatetime-slots.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-zoneddatetime-slots.js
new file mode 100644
index 0000000000..3ca0d6e1d4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-zoneddatetime-slots.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.calendar.prototype.daysinmonth
+description: Getters are not called when converting a ZonedDateTime to a PlainDate.
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.ZonedDateTime.prototype);
+const getters = ["year", "month", "monthCode", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond", "calendar"];
+
+for (const property of getters) {
+ Object.defineProperty(Temporal.ZonedDateTime.prototype, property, {
+ get() {
+ actual.push(`get ${property}`);
+ const value = prototypeDescrs[property].get.call(this);
+ return {
+ toString() {
+ actual.push(`toString ${property}`);
+ return value.toString();
+ },
+ valueOf() {
+ actual.push(`valueOf ${property}`);
+ return value;
+ },
+ };
+ },
+ });
+}
+
+const arg = new Temporal.ZonedDateTime(0n, "UTC");
+const instance = new Temporal.Calendar("iso8601");
+instance.daysInMonth(arg);
+assert.compareArray(actual, []);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..645308ec95
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/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.calendar.prototype.daysinmonth
+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 calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => calendar.daysInMonth(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..8ed7f33ff2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/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.calendar.prototype.daysinmonth
+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 calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => calendar.daysInMonth(datetime),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..3ed66442c2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/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.calendar.prototype.daysinmonth
+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 calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => calendar.daysInMonth(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..5c18bfdbcd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/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.calendar.prototype.daysinmonth
+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 calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => calendar.daysInMonth(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/basic.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/basic.js
new file mode 100644
index 0000000000..85985cb146
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/basic.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.calendar.prototype.daysinmonth
+description: Basic tests for daysInMonth().
+features: [Temporal]
+---*/
+
+const iso = Temporal.Calendar.from("iso8601");
+const res = 30;
+assert.sameValue(iso.daysInMonth(Temporal.PlainDate.from("1994-11-05")), res, "PlainDate");
+assert.sameValue(iso.daysInMonth(Temporal.PlainDateTime.from("1994-11-05T08:15:30")), res, "PlainDateTime");
+assert.sameValue(iso.daysInMonth({ year: 1994, month: 11, day: 5 }), res, "property bag");
+assert.sameValue(iso.daysInMonth("1994-11-05"), res, "string");
+assert.throws(TypeError, () => iso.daysInMonth({ year: 2000 }), "property bag with missing properties");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/branding.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/branding.js
new file mode 100644
index 0000000000..0a08a13ba8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/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.calendar.prototype.daysinmonth
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const daysInMonth = Temporal.Calendar.prototype.daysInMonth;
+
+assert.sameValue(typeof daysInMonth, "function");
+
+const args = [new Temporal.PlainDate(2000, 1, 1)];
+
+assert.throws(TypeError, () => daysInMonth.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => daysInMonth.apply(null, args), "null");
+assert.throws(TypeError, () => daysInMonth.apply(true, args), "true");
+assert.throws(TypeError, () => daysInMonth.apply("", args), "empty string");
+assert.throws(TypeError, () => daysInMonth.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => daysInMonth.apply(1, args), "1");
+assert.throws(TypeError, () => daysInMonth.apply({}, args), "plain object");
+assert.throws(TypeError, () => daysInMonth.apply(Temporal.Calendar, args), "Temporal.Calendar");
+assert.throws(TypeError, () => daysInMonth.apply(Temporal.Calendar.prototype, args), "Temporal.Calendar.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/browser.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/builtin.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/builtin.js
new file mode 100644
index 0000000000..55e16b81a4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/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.calendar.prototype.daysinmonth
+description: >
+ Tests that Temporal.Calendar.prototype.daysInMonth
+ 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.Calendar.prototype.daysInMonth),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Calendar.prototype.daysInMonth),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Calendar.prototype.daysInMonth),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Calendar.prototype.daysInMonth.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/calendar-datefromfields-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/calendar-datefromfields-called-with-options-undefined.js
new file mode 100644
index 0000000000..30931996c1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/calendar-datefromfields-called-with-options-undefined.js
@@ -0,0 +1,18 @@
+// |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.calendar.prototype.daysinmonth
+description: >
+ Calendar.dateFromFields method is called with undefined as the options value
+ when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
+calendar.daysInMonth({ year: 2000, month: 5, day: 3, calendar });
+assert.sameValue(calendar.dateFromFieldsCallCount, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/calendar-fields-iterable.js
new file mode 100644
index 0000000000..9ea5126443
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/calendar-fields-iterable.js
@@ -0,0 +1,35 @@
+// |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.calendar.prototype.daysinmonth
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.calendar.prototype.daysinmonth step 4.a:
+ a. Set _dateOrDateTime_ to ? ToTemporalDate(_dateOrDateTime_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToListOfType(_fieldsArray_, « String »).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+calendar1.daysInMonth({ year: 2000, month: 5, day: 2, calendar: calendar2 });
+
+assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/calendar-temporal-object.js
new file mode 100644
index 0000000000..00bad9889f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/calendar-temporal-object.js
@@ -0,0 +1,29 @@
+// |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.calendar.prototype.daysinmonth
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.calendar.prototype.daysinmonth step 4.a:
+ a. Set _dateOrDateTime_ to ? ToTemporalDate(_dateOrDateTime_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const calendar = new Temporal.Calendar("iso8601");
+ calendar.daysInMonth({ year: 2000, month: 5, day: 2, calendar: temporalObject });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..eb47f7d645
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/infinity-throws-rangeerror.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.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.calendar.prototype.daysinmonth
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+const base = { year: 2000, month: 5, day: 2 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.daysInMonth({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.daysInMonth({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/length.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/length.js
new file mode 100644
index 0000000000..fe4acc1334
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/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.calendar.prototype.daysinmonth
+description: Temporal.Calendar.prototype.daysInMonth.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.Calendar.prototype.daysInMonth, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/name.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/name.js
new file mode 100644
index 0000000000..6d65a4c15a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/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.calendar.prototype.daysinmonth
+description: Temporal.Calendar.prototype.daysInMonth.name is "daysInMonth".
+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.Calendar.prototype.daysInMonth, "name", {
+ value: "daysInMonth",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/not-a-constructor.js
new file mode 100644
index 0000000000..f7edc90848
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/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.calendar.prototype.daysinmonth
+description: >
+ Temporal.Calendar.prototype.daysInMonth 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.Calendar.prototype.daysInMonth();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Calendar.prototype.daysInMonth), false,
+ "isConstructor(Temporal.Calendar.prototype.daysInMonth)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/plain-date-time.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/plain-date-time.js
new file mode 100644
index 0000000000..26e9a810fd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/plain-date-time.js
@@ -0,0 +1,116 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.calendar.prototype.daysinmonth
+description: >
+ Temporal.Calendar.prototype.daysInMonth will take Temporal.PlainDateTime object
+ and return the number of days in that month.
+info: |
+ 4. If Type(temporalDateLike) is not Object or temporalDateLike does not have an [[InitializedTemporalDate]] or [[InitializedTemporalYearMonth]] internal slots, then
+ a. Set temporalDateLike to ? ToTemporalDate(temporalDateLike).
+ 5. Return 𝔽(! ISODaysInMonth(temporalDateLike.[[ISOYear]], temporalDateLike.[[ISOMonth]])).
+features: [Temporal]
+---*/
+let cal = new Temporal.Calendar("iso8601");
+
+let dt = new Temporal.PlainDateTime(1997, 1, 23, 5, 30, 13);
+assert.sameValue(
+ cal.daysInMonth(dt),
+ 31,
+ 'cal.daysInMonth(new Temporal.PlainDateTime(1997, 1, 23, 5, 30, 13)) must return 31'
+);
+
+// leap year
+dt = new Temporal.PlainDateTime(1996, 2, 23, 5, 30, 13);
+assert.sameValue(
+ cal.daysInMonth(dt),
+ 29,
+ 'cal.daysInMonth(new Temporal.PlainDateTime(1996, 2, 23, 5, 30, 13)) must return 29'
+);
+dt = new Temporal.PlainDateTime(2000, 2, 23, 5, 30, 13);
+assert.sameValue(
+ cal.daysInMonth(dt),
+ 29,
+ 'cal.daysInMonth(new Temporal.PlainDateTime(2000, 2, 23, 5, 30, 13)) must return 29'
+);
+
+// non leap year
+dt = new Temporal.PlainDateTime(1997, 2, 23, 5, 30, 13);
+assert.sameValue(
+ cal.daysInMonth(dt),
+ 28,
+ 'cal.daysInMonth(new Temporal.PlainDateTime(1997, 2, 23, 5, 30, 13)) must return 28'
+);
+
+dt = new Temporal.PlainDateTime(1997, 3, 23, 5, 30, 13);
+assert.sameValue(
+ cal.daysInMonth(dt),
+ 31,
+ 'cal.daysInMonth(new Temporal.PlainDateTime(1997, 3, 23, 5, 30, 13)) must return 31'
+);
+
+dt = new Temporal.PlainDateTime(1997, 4, 23, 5, 30, 13);
+assert.sameValue(
+ cal.daysInMonth(dt),
+ 30,
+ 'cal.daysInMonth(new Temporal.PlainDateTime(1997, 4, 23, 5, 30, 13)) must return 30'
+);
+
+dt = new Temporal.PlainDateTime(1997, 5, 23, 5, 30, 13);
+assert.sameValue(
+ cal.daysInMonth(dt),
+ 31,
+ 'cal.daysInMonth(new Temporal.PlainDateTime(1997, 5, 23, 5, 30, 13)) must return 31'
+);
+
+dt = new Temporal.PlainDateTime(1997, 6, 23, 5, 30, 13);
+assert.sameValue(
+ cal.daysInMonth(dt),
+ 30,
+ 'cal.daysInMonth(new Temporal.PlainDateTime(1997, 6, 23, 5, 30, 13)) must return 30'
+);
+
+dt = new Temporal.PlainDateTime(1997, 7, 23, 5, 30, 13);
+assert.sameValue(
+ cal.daysInMonth(dt),
+ 31,
+ 'cal.daysInMonth(new Temporal.PlainDateTime(1997, 7, 23, 5, 30, 13)) must return 31'
+);
+
+dt = new Temporal.PlainDateTime(1997, 8, 23, 5, 30, 13);
+assert.sameValue(
+ cal.daysInMonth(dt),
+ 31,
+ 'cal.daysInMonth(new Temporal.PlainDateTime(1997, 8, 23, 5, 30, 13)) must return 31'
+);
+
+dt = new Temporal.PlainDateTime(1997, 9, 23, 5, 30, 13);
+assert.sameValue(
+ cal.daysInMonth(dt),
+ 30,
+ 'cal.daysInMonth(new Temporal.PlainDateTime(1997, 9, 23, 5, 30, 13)) must return 30'
+);
+
+dt = new Temporal.PlainDateTime(1997, 10, 23, 5, 30, 13);
+assert.sameValue(
+ cal.daysInMonth(dt),
+ 31,
+ 'cal.daysInMonth(new Temporal.PlainDateTime(1997, 10, 23, 5, 30, 13)) must return 31'
+);
+
+dt = new Temporal.PlainDateTime(1997, 11, 23, 5, 30, 13);
+assert.sameValue(
+ cal.daysInMonth(dt),
+ 30,
+ 'cal.daysInMonth(new Temporal.PlainDateTime(1997, 11, 23, 5, 30, 13)) must return 30'
+);
+
+dt = new Temporal.PlainDateTime(1997, 12, 23, 5, 30, 13);
+assert.sameValue(
+ cal.daysInMonth(dt),
+ 31,
+ 'cal.daysInMonth(new Temporal.PlainDateTime(1997, 12, 23, 5, 30, 13)) must return 31'
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/plain-date.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/plain-date.js
new file mode 100644
index 0000000000..edb6f55093
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/plain-date.js
@@ -0,0 +1,58 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.calendar.prototype.daysinmonth
+description: >
+ Temporal.Calendar.prototype.daysInMonth will take Temporal.PlainDate object
+ and return the number of days in that month.
+info: |
+ 5. Return 𝔽(! ISODaysInMonth(temporalDateLike.[[ISOYear]], temporalDateLike.[[ISOMonth]])).
+features: [Temporal]
+---*/
+let cal = new Temporal.Calendar("iso8601");
+
+let d = new Temporal.PlainDate(2021, 1, 15);
+assert.sameValue(cal.daysInMonth(d), 31, 'cal.daysInMonth(new Temporal.PlainDate(2021, 1, 15)) must return 31');
+
+// non-leap year
+d = new Temporal.PlainDate(2021, 2, 15);
+assert.sameValue(cal.daysInMonth(d), 28, 'cal.daysInMonth(new Temporal.PlainDate(2021, 2, 15)) must return 28');
+
+// leap year
+d = new Temporal.PlainDate(2020, 2, 15);
+assert.sameValue(cal.daysInMonth(d), 29, 'cal.daysInMonth(new Temporal.PlainDate(2020, 2, 15)) must return 29');
+d = new Temporal.PlainDate(2000, 2, 15);
+assert.sameValue(cal.daysInMonth(d), 29, 'cal.daysInMonth(new Temporal.PlainDate(2000, 2, 15)) must return 29');
+
+d = new Temporal.PlainDate(2021, 3, 15);
+assert.sameValue(cal.daysInMonth(d), 31, 'cal.daysInMonth(new Temporal.PlainDate(2021, 3, 15)) must return 31');
+
+d = new Temporal.PlainDate(2021, 4, 15);
+assert.sameValue(cal.daysInMonth(d), 30, 'cal.daysInMonth(new Temporal.PlainDate(2021, 4, 15)) must return 30');
+
+d = new Temporal.PlainDate(2021, 5, 15);
+assert.sameValue(cal.daysInMonth(d), 31, 'cal.daysInMonth(new Temporal.PlainDate(2021, 5, 15)) must return 31');
+
+d = new Temporal.PlainDate(2021, 6, 15);
+assert.sameValue(cal.daysInMonth(d), 30, 'cal.daysInMonth(new Temporal.PlainDate(2021, 6, 15)) must return 30');
+
+d = new Temporal.PlainDate(2021, 7, 15);
+assert.sameValue(cal.daysInMonth(d), 31, 'cal.daysInMonth(new Temporal.PlainDate(2021, 7, 15)) must return 31');
+
+d = new Temporal.PlainDate(2021, 8, 15);
+assert.sameValue(cal.daysInMonth(d), 31, 'cal.daysInMonth(new Temporal.PlainDate(2021, 8, 15)) must return 31');
+
+d = new Temporal.PlainDate(2021, 9, 15);
+assert.sameValue(cal.daysInMonth(d), 30, 'cal.daysInMonth(new Temporal.PlainDate(2021, 9, 15)) must return 30');
+
+d = new Temporal.PlainDate(2021, 10, 15);
+assert.sameValue(cal.daysInMonth(d), 31, 'cal.daysInMonth(new Temporal.PlainDate(2021, 10, 15)) must return 31');
+
+d = new Temporal.PlainDate(2021, 11, 15);
+assert.sameValue(cal.daysInMonth(d), 30, 'cal.daysInMonth(new Temporal.PlainDate(2021, 11, 15)) must return 30');
+
+d = new Temporal.PlainDate(2021, 12, 15);
+assert.sameValue(cal.daysInMonth(d), 31, 'cal.daysInMonth(new Temporal.PlainDate(2021, 12, 15)) must return 31');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/prop-desc.js
new file mode 100644
index 0000000000..d2f45ed5ca
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/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.calendar.prototype.daysinmonth
+description: The "daysInMonth" property of Temporal.Calendar.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Calendar.prototype.daysInMonth,
+ "function",
+ "`typeof Calendar.prototype.daysInMonth` is `function`"
+);
+
+verifyProperty(Temporal.Calendar.prototype, "daysInMonth", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/shell.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/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/Calendar/prototype/daysInMonth/string.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/string.js
new file mode 100644
index 0000000000..0f236a1622
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/string.js
@@ -0,0 +1,35 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.calendar.prototype.daysinmonth
+description: >
+ Temporal.Calendar.prototype.daysInMonth will take ISO8601 string
+ and return the number of days in that month.
+info: |
+ 4. If Type(temporalDateLike) is not Object or temporalDateLike does not have
+ an [[InitializedTemporalDate]] or [[InitializedTemporalYearMonth]] internal
+ slots, then
+ a. Set temporalDateLike to ? ToTemporalDate(temporalDateLike).
+ 5. Return 𝔽(! ISODaysInMonth(temporalDateLike.[[ISOYear]], temporalDateLike.[[ISOMonth]])).
+features: [Temporal]
+---*/
+let cal = new Temporal.Calendar("iso8601");
+
+assert.sameValue(cal.daysInMonth("2019-01-18"), 31, 'cal.daysInMonth("2019-01-18") must return 31');
+// leap year
+assert.sameValue(cal.daysInMonth("2020-02-18"), 29, 'cal.daysInMonth("2020-02-18") must return 29');
+// non leap
+assert.sameValue(cal.daysInMonth("2019-02-18"), 28, 'cal.daysInMonth("2019-02-18") must return 28');
+assert.sameValue(cal.daysInMonth("2019-03-18"), 31, 'cal.daysInMonth("2019-03-18") must return 31');
+assert.sameValue(cal.daysInMonth("2019-04-18"), 30, 'cal.daysInMonth("2019-04-18") must return 30');
+assert.sameValue(cal.daysInMonth("2019-05-18"), 31, 'cal.daysInMonth("2019-05-18") must return 31');
+assert.sameValue(cal.daysInMonth("2019-06-18"), 30, 'cal.daysInMonth("2019-06-18") must return 30');
+assert.sameValue(cal.daysInMonth("2019-07-18"), 31, 'cal.daysInMonth("2019-07-18") must return 31');
+assert.sameValue(cal.daysInMonth("2019-08-18"), 31, 'cal.daysInMonth("2019-08-18") must return 31');
+assert.sameValue(cal.daysInMonth("2019-09-18"), 30, 'cal.daysInMonth("2019-09-18") must return 30');
+assert.sameValue(cal.daysInMonth("2019-10-18"), 31, 'cal.daysInMonth("2019-10-18") must return 31');
+assert.sameValue(cal.daysInMonth("2019-11-18"), 30, 'cal.daysInMonth("2019-11-18") must return 30');
+assert.sameValue(cal.daysInMonth("2019-12-18"), 31, 'cal.daysInMonth("2019-12-18") must return 31');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/throw-range-error-ToTemporalDate.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/throw-range-error-ToTemporalDate.js
new file mode 100644
index 0000000000..6571d37f2d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/throw-range-error-ToTemporalDate.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.calendar.prototype.daysInMonth
+description: >
+ Temporal.Calendar.prototype.daysInMonth throws RangeError on
+ ToTemporalDate when temporalDateLike is invalid string.
+info: |
+ 4. If Type(temporalDateLike) is not Object or temporalDateLike does not have
+ an [[InitializedTemporalDate]] or [[InitializedTemporalYearMonth]] internal
+ slots, then
+ a. Set temporalDateLike to ? ToTemporalDate(temporalDateLike).
+features: [Temporal, arrow-function]
+---*/
+let cal = new Temporal.Calendar("iso8601");
+
+assert.throws(RangeError, () => cal.daysInMonth("invalid string"),
+ 'cal.daysInMonth("invalid string") throws a RangeError exception');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/year-zero.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/year-zero.js
new file mode 100644
index 0000000000..7974098376
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInMonth/year-zero.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.calendar.prototype.daysinmonth
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T00:45",
+ "-000000-10-31T00:45+01:00",
+ "-000000-10-31T00:45+00:00[UTC]",
+];
+const instance = new Temporal.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.daysInMonth(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..110aed2bed
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-builtin-calendar-no-array-iteration.js
@@ -0,0 +1,24 @@
+// |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.calendar.prototype.daysinweek
+description: >
+ Calling the method with a property bag argument with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.Calendar("iso8601");
+const arg = { year: 2000, month: 5, day: 2, calendar: "iso8601" };
+instance.daysInWeek(arg);
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-calendar-datefromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..3db254da68
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-calendar-datefromfields-called-with-null-prototype-fields.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.calendar.prototype.daysinweek
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.Calendar("iso8601");
+const arg = { year: 2000, month: 5, day: 2, calendar };
+instance.daysInWeek(arg);
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..8952ae023f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-constructor-in-calendar-fields.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.calendar.prototype.daysinweek
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.Calendar("iso8601");
+
+assert.throws(RangeError, () => instance.daysInWeek(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-duplicate-calendar-fields.js
new file mode 100644
index 0000000000..c36efdc0a9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-duplicate-calendar-fields.js
@@ -0,0 +1,19 @@
+// |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.calendar.prototype.daysinweek
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['day'], ['month'], ['monthCode'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar };
+ const instance = new Temporal.Calendar("iso8601");
+
+ assert.throws(RangeError, () => instance.daysInWeek(arg));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-leap-second.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-leap-second.js
new file mode 100644
index 0000000000..aa15290d2f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-leap-second.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.calendar.prototype.daysinweek
+description: Leap second is a valid ISO string for PlainDate
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+let arg = "2016-12-31T23:59:60";
+const result1 = instance.daysInWeek(arg);
+assert.sameValue(
+ result1,
+ 7,
+ "leap second is a valid ISO string for PlainDate"
+);
+
+arg = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+const result2 = instance.daysInWeek(arg);
+assert.sameValue(
+ result2,
+ 7,
+ "second: 60 is ignored in property bag for PlainDate"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-number.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-number.js
new file mode 100644
index 0000000000..a279f5c8c2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/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.calendar.prototype.daysinweek
+description: A number cannot be used in place of a Temporal.PlainDate
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const numbers = [
+ 1,
+ 19761118,
+ -19761118,
+ 1234567890,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => instance.daysInWeek(arg),
+ 'Numbers cannot be used in place of an ISO string for PlainDate'
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-propertybag-calendar-case-insensitive.js
new file mode 100644
index 0000000000..08a615e309
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-propertybag-calendar-case-insensitive.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.calendar.prototype.daysinweek
+description: The calendar name is case-insensitive
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const calendar = "IsO8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.daysInWeek(arg);
+assert.sameValue(result, 7, "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-propertybag-calendar-leap-second.js
new file mode 100644
index 0000000000..0a2b0806f9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-propertybag-calendar-leap-second.js
@@ -0,0 +1,23 @@
+// |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.calendar.prototype.daysinweek
+description: Leap second is a valid ISO string for a calendar in a property bag
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const calendar = "2016-12-31T23:59:60";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.daysInWeek(arg);
+assert.sameValue(
+ result,
+ 7,
+ "leap second is a valid ISO string for calendar"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-propertybag-calendar-number.js
new file mode 100644
index 0000000000..f4f77d8c2e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-propertybag-calendar-number.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.calendar.prototype.daysinweek
+description: A number as calendar in a property bag is not accepted
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const numbers = [
+ 1,
+ 19970327,
+ -19970327,
+ 1234567890,
+];
+
+for (const calendar of numbers) {
+ const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+ assert.throws(
+ TypeError,
+ () => instance.daysInWeek(arg),
+ "Numbers cannot be used as a calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-propertybag-calendar-string.js
new file mode 100644
index 0000000000..556f29d81f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-propertybag-calendar-string.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.calendar.prototype.daysinweek
+description: A calendar ID is valid input for Calendar
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const calendar = "iso8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.daysInWeek(arg);
+assert.sameValue(result, 7, `Calendar created from string "${calendar}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-propertybag-calendar-wrong-type.js
new file mode 100644
index 0000000000..11394edbb6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-propertybag-calendar-wrong-type.js
@@ -0,0 +1,46 @@
+// |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.calendar.prototype.daysinweek
+description: >
+ Appropriate error thrown when a calendar property from a property bag cannot
+ be converted to a calendar object or string
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.Calendar("iso8601");
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [calendar, description] of primitiveTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ typeof calendar === 'string' ? RangeError : TypeError,
+ () => instance.daysInWeek(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+ [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields()
+];
+
+for (const [calendar, description] of typeErrorTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(TypeError, () => instance.daysInWeek(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/Calendar/prototype/daysInWeek/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-propertybag-calendar-year-zero.js
new file mode 100644
index 0000000000..7d8395c074
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-propertybag-calendar-year-zero.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.calendar.prototype.daysinweek
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T17:45",
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+01:00",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+const instance = new Temporal.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.daysInWeek(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-proto-in-calendar-fields.js
new file mode 100644
index 0000000000..cda32b22e9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-proto-in-calendar-fields.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.calendar.prototype.daysinweek
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.Calendar("iso8601");
+
+assert.throws(RangeError, () => instance.daysInWeek(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..25f5900218
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-string-calendar-annotation.js
@@ -0,0 +1,33 @@
+// |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.calendar.prototype.daysinweek
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02[u-ca=iso8601]", "without time or time zone"],
+ ["2000-05-02[UTC][u-ca=iso8601]", "with time zone and no time"],
+ ["2000-05-02T15:23[u-ca=iso8601]", "without time zone"],
+ ["2000-05-02T15:23[UTC][u-ca=iso8601]", "with time zone"],
+ ["2000-05-02T15:23[!u-ca=iso8601]", "with ! and no time zone"],
+ ["2000-05-02T15:23[UTC][!u-ca=iso8601]", "with ! and time zone"],
+ ["2000-05-02T15:23[u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+const instance = new Temporal.Calendar("iso8601");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.daysInWeek(arg);
+
+ assert.sameValue(
+ result,
+ 7,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..54755595a0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-string-critical-unknown-annotation.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.calendar.prototype.daysinweek
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[!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.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.daysInWeek(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..74ece3cf77
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-string-date-with-utc-offset.js
@@ -0,0 +1,48 @@
+// |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.calendar.prototype.daysinweek
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const validStrings = [
+ "2000-05-02T00+00:00",
+ "2000-05-02T00+00:00[UTC]",
+ "2000-05-02T00+00:00[!UTC]",
+ "2000-05-02T00-02:30[America/St_Johns]",
+];
+
+for (const arg of validStrings) {
+ const result = instance.daysInWeek(arg);
+
+ assert.sameValue(
+ result,
+ 7,
+ `"${arg}" is a valid UTC offset with time for PlainDate`
+ );
+}
+
+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.daysInWeek(arg),
+ `"${arg}" UTC offset without time is not valid for PlainDate`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-string-invalid.js
new file mode 100644
index 0000000000..c748c767a7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-string-invalid.js
@@ -0,0 +1,64 @@
+// |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.calendar.prototype.daysinweek
+description: >
+ RangeError thrown if an invalid ISO string (or syntactically valid ISO string
+ that is not supported) is used as a PlainDate
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ // invalid ISO strings:
+ "",
+ "invalid iso8601",
+ "2020-01-00",
+ "2020-01-32",
+ "2020-02-30",
+ "2021-02-29",
+ "2020-00-01",
+ "2020-13-01",
+ "2020-01-01T",
+ "2020-01-01T25:00:00",
+ "2020-01-01T01:60:00",
+ "2020-01-01T01:60:61",
+ "2020-01-01junk",
+ "2020-01-01T00:00:00junk",
+ "2020-01-01T00:00:00+00:00junk",
+ "2020-01-01T00:00:00+00:00[UTC]junk",
+ "2020-01-01T00:00:00+00:00[UTC][u-ca=iso8601]junk",
+ "02020-01-01",
+ "2020-001-01",
+ "2020-01-001",
+ "2020-01-01T001",
+ "2020-01-01T01:001",
+ "2020-01-01T01:01:001",
+ // valid, but forms not supported in Temporal:
+ "2020-W01-1",
+ "2020-001",
+ "+0002020-01-01",
+ // valid, but this calendar must not exist:
+ "2020-01-01[u-ca=notexist]",
+ // may be valid in other contexts, but insufficient information for PlainDate:
+ "2020-01",
+ "+002020-01",
+ "01-01",
+ "2020-W01",
+ "P1Y",
+ "-P12Y",
+ // valid, but outside the supported range:
+ "-999999-01-01",
+ "+999999-01-01",
+];
+const instance = new Temporal.Calendar("iso8601");
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => instance.daysInWeek(arg),
+ `"${arg}" should not be a valid ISO string for a PlainDate`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..57ba7d4c4a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/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.calendar.prototype.daysinweek
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[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.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.daysInWeek(arg),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..fc99e1a45a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-string-multiple-time-zone.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.calendar.prototype.daysinweek
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[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.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.daysInWeek(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-string-time-separators.js
new file mode 100644
index 0000000000..1059a26053
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-string-time-separators.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.calendar.prototype.daysinweek
+description: Time separator in string argument can vary
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02T15:23", "uppercase T"],
+ ["2000-05-02t15:23", "lowercase T"],
+ ["2000-05-02 15:23", "space between date and time"],
+];
+
+const instance = new Temporal.Calendar("iso8601");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.daysInWeek(arg);
+
+ assert.sameValue(
+ result,
+ 7,
+ `variant time separators (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..9716308c3b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-string-time-zone-annotation.js
@@ -0,0 +1,38 @@
+// |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.calendar.prototype.daysinweek
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02[Asia/Kolkata]", "named, with no time"],
+ ["2000-05-02[!Europe/Vienna]", "named, with ! and no time"],
+ ["2000-05-02[+00:00]", "numeric, with no time"],
+ ["2000-05-02[!-02:30]", "numeric, with ! and no time"],
+ ["2000-05-02T15:23[America/Sao_Paulo]", "named, with no offset"],
+ ["2000-05-02T15:23[!Asia/Tokyo]", "named, with ! and no offset"],
+ ["2000-05-02T15:23[-02:30]", "numeric, with no offset"],
+ ["2000-05-02T15:23[!+00:00]", "numeric, with ! and no offset"],
+ ["2000-05-02T15:23+00:00[America/New_York]", "named, with offset"],
+ ["2000-05-02T15:23+00:00[!UTC]", "named, with offset and !"],
+ ["2000-05-02T15:23+00:00[+01:00]", "numeric, with offset"],
+ ["2000-05-02T15:23+00:00[!-08:00]", "numeric, with offset and !"],
+];
+
+const instance = new Temporal.Calendar("iso8601");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.daysInWeek(arg);
+
+ assert.sameValue(
+ result,
+ 7,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..d5ce3102a5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-string-unknown-annotation.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.calendar.prototype.daysinweek
+description: Various forms of unknown annotation
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02[foo=bar]", "without time"],
+ ["2000-05-02T15:23[foo=bar]", "alone"],
+ ["2000-05-02T15:23[UTC][foo=bar]", "with time zone"],
+ ["2000-05-02T15:23[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["2000-05-02T15:23[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["2000-05-02T15:23[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+const instance = new Temporal.Calendar("iso8601");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.daysInWeek(arg);
+
+ assert.sameValue(
+ result,
+ 7,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..5ba18b0ea1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-string-with-utc-designator.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.calendar.prototype.daysinweek
+description: RangeError thrown if a string with UTC designator is used as a PlainDate
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "2019-10-01T09:00:00Z",
+ "2019-10-01T09:00:00Z[UTC]",
+];
+const instance = new Temporal.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.daysInWeek(arg),
+ "String with UTC designator should not be valid as a PlainDate"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-wrong-type.js
new file mode 100644
index 0000000000..449bb99be3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/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.calendar.prototype.daysinweek
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainDate
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+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.daysInWeek(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.PlainDate, "Temporal.PlainDate, object"],
+ [Temporal.PlainDate.prototype, "Temporal.PlainDate.prototype, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.daysInWeek(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/Calendar/prototype/daysInWeek/argument-zoneddatetime-convert.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-zoneddatetime-convert.js
new file mode 100644
index 0000000000..941c35db81
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-zoneddatetime-convert.js
@@ -0,0 +1,22 @@
+// |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.calendar.prototype.daysinweek
+description: An exception from TimeZone#getOffsetNanosecondsFor() is propagated.
+features: [Temporal]
+---*/
+
+class TZ extends Temporal.TimeZone {
+ constructor() { super("UTC") }
+ getOffsetNanosecondsFor() { throw new Test262Error() }
+}
+
+const tz = new TZ();
+const arg = new Temporal.ZonedDateTime(0n, tz);
+const instance = new Temporal.Calendar("iso8601");
+
+assert.throws(Test262Error, () => instance.daysInWeek(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-zoneddatetime-slots.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-zoneddatetime-slots.js
new file mode 100644
index 0000000000..60a3547c9a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-zoneddatetime-slots.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.calendar.prototype.daysinweek
+description: Getters are not called when converting a ZonedDateTime to a PlainDate.
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.ZonedDateTime.prototype);
+const getters = ["year", "month", "monthCode", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond", "calendar"];
+
+for (const property of getters) {
+ Object.defineProperty(Temporal.ZonedDateTime.prototype, property, {
+ get() {
+ actual.push(`get ${property}`);
+ const value = prototypeDescrs[property].get.call(this);
+ return {
+ toString() {
+ actual.push(`toString ${property}`);
+ return value.toString();
+ },
+ valueOf() {
+ actual.push(`valueOf ${property}`);
+ return value;
+ },
+ };
+ },
+ });
+}
+
+const arg = new Temporal.ZonedDateTime(0n, "UTC");
+const instance = new Temporal.Calendar("iso8601");
+instance.daysInWeek(arg);
+assert.compareArray(actual, []);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..6145e1b1f3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/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.calendar.prototype.daysinweek
+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 calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => calendar.daysInWeek(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..10c6de0f88
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/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.calendar.prototype.daysinweek
+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 calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => calendar.daysInWeek(datetime),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..a2700dd325
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/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.calendar.prototype.daysinweek
+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 calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => calendar.daysInWeek(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..575f44a56f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/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.calendar.prototype.daysinweek
+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 calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => calendar.daysInWeek(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/basic.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/basic.js
new file mode 100644
index 0000000000..bc08c6bcd8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/basic.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.calendar.prototype.daysinweek
+description: Basic tests for daysInWeek().
+features: [Temporal]
+---*/
+
+const iso = Temporal.Calendar.from("iso8601");
+const res = 7;
+assert.sameValue(iso.daysInWeek(Temporal.PlainDate.from("1994-11-05")), res, "PlainDate");
+assert.sameValue(iso.daysInWeek(Temporal.PlainDateTime.from("1994-11-05T08:15:30")), res, "PlainDateTime");
+assert.sameValue(iso.daysInWeek({ year: 1994, month: 11, day: 5 }), res, "property bag");
+assert.sameValue(iso.daysInWeek("1994-11-05"), res, "string");
+assert.throws(TypeError, () => iso.daysInWeek({ year: 2000 }), "property bag with missing properties");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/branding.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/branding.js
new file mode 100644
index 0000000000..6772299fda
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/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.calendar.prototype.daysinweek
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const daysInWeek = Temporal.Calendar.prototype.daysInWeek;
+
+assert.sameValue(typeof daysInWeek, "function");
+
+const args = [new Temporal.PlainDate(2000, 1, 1)];
+
+assert.throws(TypeError, () => daysInWeek.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => daysInWeek.apply(null, args), "null");
+assert.throws(TypeError, () => daysInWeek.apply(true, args), "true");
+assert.throws(TypeError, () => daysInWeek.apply("", args), "empty string");
+assert.throws(TypeError, () => daysInWeek.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => daysInWeek.apply(1, args), "1");
+assert.throws(TypeError, () => daysInWeek.apply({}, args), "plain object");
+assert.throws(TypeError, () => daysInWeek.apply(Temporal.Calendar, args), "Temporal.Calendar");
+assert.throws(TypeError, () => daysInWeek.apply(Temporal.Calendar.prototype, args), "Temporal.Calendar.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/browser.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/builtin.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/builtin.js
new file mode 100644
index 0000000000..d0cdf2e2ac
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/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.calendar.prototype.daysinweek
+description: >
+ Tests that Temporal.Calendar.prototype.daysInWeek
+ 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.Calendar.prototype.daysInWeek),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Calendar.prototype.daysInWeek),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Calendar.prototype.daysInWeek),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Calendar.prototype.daysInWeek.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/calendar-datefromfields-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/calendar-datefromfields-called-with-options-undefined.js
new file mode 100644
index 0000000000..1c31202345
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/calendar-datefromfields-called-with-options-undefined.js
@@ -0,0 +1,18 @@
+// |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.calendar.prototype.daysinweek
+description: >
+ Calendar.dateFromFields method is called with undefined as the options value
+ when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
+calendar.daysInWeek({ year: 2000, month: 5, day: 3, calendar });
+assert.sameValue(calendar.dateFromFieldsCallCount, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/calendar-fields-iterable.js
new file mode 100644
index 0000000000..5fe4eb09bd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/calendar-fields-iterable.js
@@ -0,0 +1,35 @@
+// |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.calendar.prototype.daysinweek
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.calendar.prototype.daysinweek step 4:
+ 4. Perform ? ToTemporalDate(_dateOrDateTime_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToListOfType(_fieldsArray_, « String »).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+calendar1.daysInWeek({ year: 2000, month: 5, day: 2, calendar: calendar2 });
+
+assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/calendar-temporal-object.js
new file mode 100644
index 0000000000..98d7a71412
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/calendar-temporal-object.js
@@ -0,0 +1,29 @@
+// |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.calendar.prototype.daysinweek
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.calendar.prototype.daysinweek step 4:
+ 4. Perform ? ToTemporalDate(_dateOrDateTime_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const calendar = new Temporal.Calendar("iso8601");
+ calendar.daysInWeek({ year: 2000, month: 5, day: 2, calendar: temporalObject });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/date-time.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/date-time.js
new file mode 100644
index 0000000000..7ab14c3ca8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/date-time.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.calendar.prototype.daysinweeks
+description: Temporal.Calendar.prototype.daysInWeek will take PlainDateTime and return 7.
+info: |
+ 4. Perform ? ToTemporalDate(temporalDateLike).
+ 5. Return 7𝔽.
+features: [Temporal]
+---*/
+let cal = new Temporal.Calendar("iso8601");
+
+let dt = new Temporal.PlainDateTime(1997, 8, 23, 5, 30, 13);
+assert.sameValue(
+ cal.daysInWeek(dt),
+ 7,
+ 'cal.daysInWeek(new Temporal.PlainDateTime(1997, 8, 23, 5, 30, 13)) must return 7'
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/date.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/date.js
new file mode 100644
index 0000000000..62f2ff263a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/date.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.calendar.prototype.daysinweeks
+description: Temporal.Calendar.prototype.daysInWeek will take PlainDate and return 7.
+info: |
+ 5. Return 7𝔽.
+features: [Temporal]
+---*/
+let cal = new Temporal.Calendar("iso8601");
+
+let d = new Temporal.PlainDate(2021, 7, 15);
+assert.sameValue(cal.daysInWeek(d), 7, 'cal.daysInWeek(new Temporal.PlainDate(2021, 7, 15)) must return 7');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..44cecb2e90
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/infinity-throws-rangeerror.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.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.calendar.prototype.daysinweek
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+const base = { year: 2000, month: 5, day: 2 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.daysInWeek({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.daysInWeek({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/length.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/length.js
new file mode 100644
index 0000000000..b29774e9ff
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/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.calendar.prototype.daysinweek
+description: Temporal.Calendar.prototype.daysInWeek.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.Calendar.prototype.daysInWeek, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/name.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/name.js
new file mode 100644
index 0000000000..59cfe96553
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/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.calendar.prototype.daysinweek
+description: Temporal.Calendar.prototype.daysInWeek.name is "daysInWeek".
+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.Calendar.prototype.daysInWeek, "name", {
+ value: "daysInWeek",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/not-a-constructor.js
new file mode 100644
index 0000000000..3445a89301
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/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.calendar.prototype.daysinweek
+description: >
+ Temporal.Calendar.prototype.daysInWeek 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.Calendar.prototype.daysInWeek();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Calendar.prototype.daysInWeek), false,
+ "isConstructor(Temporal.Calendar.prototype.daysInWeek)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/prop-desc.js
new file mode 100644
index 0000000000..27bc5aca22
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/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.calendar.prototype.daysinweek
+description: The "daysInWeek" property of Temporal.Calendar.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Calendar.prototype.daysInWeek,
+ "function",
+ "`typeof Calendar.prototype.daysInWeek` is `function`"
+);
+
+verifyProperty(Temporal.Calendar.prototype, "daysInWeek", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/shell.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/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/Calendar/prototype/daysInWeek/string.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/string.js
new file mode 100644
index 0000000000..b7ca1084a6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/string.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.calendar.prototype.daysinweeks
+description: >
+ Temporal.Calendar.prototype.daysInWeek will take valid ISO8601 string
+ and return 7.
+info: |
+ 4. Perform ? ToTemporalDate(temporalDateLike).
+ 5. Return 7𝔽.
+features: [Temporal]
+---*/
+let cal = new Temporal.Calendar("iso8601");
+assert.sameValue(cal.daysInWeek("2019-03-18"), 7, 'cal.daysInWeek("2019-03-18") must return 7');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/throw-range-error-ToTemporalDate.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/throw-range-error-ToTemporalDate.js
new file mode 100644
index 0000000000..7bd6692848
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/throw-range-error-ToTemporalDate.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.calendar.prototype.daysInWeek
+description: >
+ Temporal.Calendar.prototype.daysInWeek throws RangeError on
+ ToTemporalDate when temporalDateLike is invalid string.
+info: |
+ 4. Let temporalDate be ? ToTemporalDate(temporalDateLike).
+features: [Temporal, arrow-function]
+---*/
+let cal = new Temporal.Calendar("iso8601");
+
+assert.throws(RangeError, () => cal.daysInWeek("invalid string"),
+ 'cal.daysInWeek("invalid string") throws a RangeError exception');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/year-zero.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/year-zero.js
new file mode 100644
index 0000000000..bdd058fe7a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInWeek/year-zero.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.calendar.prototype.daysinweek
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T00:45",
+ "-000000-10-31T00:45+01:00",
+ "-000000-10-31T00:45+00:00[UTC]",
+];
+const instance = new Temporal.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.daysInWeek(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..a1a9d56b40
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-builtin-calendar-no-array-iteration.js
@@ -0,0 +1,24 @@
+// |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.calendar.prototype.daysinyear
+description: >
+ Calling the method with a property bag argument with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.Calendar("iso8601");
+const arg = { year: 2000, month: 5, day: 2, calendar: "iso8601" };
+instance.daysInYear(arg);
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-calendar-datefromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..b7d705dff3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-calendar-datefromfields-called-with-null-prototype-fields.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.calendar.prototype.daysinyear
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.Calendar("iso8601");
+const arg = { year: 2000, month: 5, day: 2, calendar };
+instance.daysInYear(arg);
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..f47b077c13
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-constructor-in-calendar-fields.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.calendar.prototype.daysinyear
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.Calendar("iso8601");
+
+assert.throws(RangeError, () => instance.daysInYear(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-duplicate-calendar-fields.js
new file mode 100644
index 0000000000..8600467296
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-duplicate-calendar-fields.js
@@ -0,0 +1,19 @@
+// |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.calendar.prototype.daysinyear
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['day'], ['month'], ['monthCode'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar };
+ const instance = new Temporal.Calendar("iso8601");
+
+ assert.throws(RangeError, () => instance.daysInYear(arg));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-leap-second.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-leap-second.js
new file mode 100644
index 0000000000..7ea0a919f0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-leap-second.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.calendar.prototype.daysinyear
+description: Leap second is a valid ISO string for PlainDate
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+let arg = "2016-12-31T23:59:60";
+const result1 = instance.daysInYear(arg);
+assert.sameValue(
+ result1,
+ 366,
+ "leap second is a valid ISO string for PlainDate"
+);
+
+arg = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+const result2 = instance.daysInYear(arg);
+assert.sameValue(
+ result2,
+ 366,
+ "second: 60 is ignored in property bag for PlainDate"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-number.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-number.js
new file mode 100644
index 0000000000..a4192c3a91
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/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.calendar.prototype.daysinyear
+description: A number cannot be used in place of a Temporal.PlainDate
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const numbers = [
+ 1,
+ 19761118,
+ -19761118,
+ 1234567890,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => instance.daysInYear(arg),
+ 'Numbers cannot be used in place of an ISO string for PlainDate'
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-propertybag-calendar-case-insensitive.js
new file mode 100644
index 0000000000..762b02f555
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-propertybag-calendar-case-insensitive.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.calendar.prototype.daysinyear
+description: The calendar name is case-insensitive
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const calendar = "IsO8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.daysInYear(arg);
+assert.sameValue(result, 366, "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-propertybag-calendar-leap-second.js
new file mode 100644
index 0000000000..a491924c6e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-propertybag-calendar-leap-second.js
@@ -0,0 +1,23 @@
+// |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.calendar.prototype.daysinyear
+description: Leap second is a valid ISO string for a calendar in a property bag
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const calendar = "2016-12-31T23:59:60";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.daysInYear(arg);
+assert.sameValue(
+ result,
+ 366,
+ "leap second is a valid ISO string for calendar"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-propertybag-calendar-number.js
new file mode 100644
index 0000000000..99a8086ff6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-propertybag-calendar-number.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.calendar.prototype.daysinyear
+description: A number as calendar in a property bag is not accepted
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const numbers = [
+ 1,
+ 19970327,
+ -19970327,
+ 1234567890,
+];
+
+for (const calendar of numbers) {
+ const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+ assert.throws(
+ TypeError,
+ () => instance.daysInYear(arg),
+ "Numbers cannot be used as a calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-propertybag-calendar-string.js
new file mode 100644
index 0000000000..c1c2e5a742
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-propertybag-calendar-string.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.calendar.prototype.daysinyear
+description: A calendar ID is valid input for Calendar
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const calendar = "iso8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.daysInYear(arg);
+assert.sameValue(result, 366, `Calendar created from string "${calendar}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-propertybag-calendar-wrong-type.js
new file mode 100644
index 0000000000..b259e17cb9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-propertybag-calendar-wrong-type.js
@@ -0,0 +1,46 @@
+// |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.calendar.prototype.daysinyear
+description: >
+ Appropriate error thrown when a calendar property from a property bag cannot
+ be converted to a calendar object or string
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.Calendar("iso8601");
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [calendar, description] of primitiveTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ typeof calendar === 'string' ? RangeError : TypeError,
+ () => instance.daysInYear(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+ [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields()
+];
+
+for (const [calendar, description] of typeErrorTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(TypeError, () => instance.daysInYear(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/Calendar/prototype/daysInYear/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-propertybag-calendar-year-zero.js
new file mode 100644
index 0000000000..18d81a4c17
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-propertybag-calendar-year-zero.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.calendar.prototype.daysinyear
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T17:45",
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+01:00",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+const instance = new Temporal.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.daysInYear(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-proto-in-calendar-fields.js
new file mode 100644
index 0000000000..19af1a4da4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-proto-in-calendar-fields.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.calendar.prototype.daysinyear
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.Calendar("iso8601");
+
+assert.throws(RangeError, () => instance.daysInYear(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..bd1c76b3d1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-string-calendar-annotation.js
@@ -0,0 +1,33 @@
+// |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.calendar.prototype.daysinyear
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02[u-ca=iso8601]", "without time or time zone"],
+ ["2000-05-02[UTC][u-ca=iso8601]", "with time zone and no time"],
+ ["2000-05-02T15:23[u-ca=iso8601]", "without time zone"],
+ ["2000-05-02T15:23[UTC][u-ca=iso8601]", "with time zone"],
+ ["2000-05-02T15:23[!u-ca=iso8601]", "with ! and no time zone"],
+ ["2000-05-02T15:23[UTC][!u-ca=iso8601]", "with ! and time zone"],
+ ["2000-05-02T15:23[u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+const instance = new Temporal.Calendar("iso8601");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.daysInYear(arg);
+
+ assert.sameValue(
+ result,
+ 366,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..4a4ce08fcf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-string-critical-unknown-annotation.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.calendar.prototype.daysinyear
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[!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.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.daysInYear(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..b5cf28d12d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-string-date-with-utc-offset.js
@@ -0,0 +1,48 @@
+// |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.calendar.prototype.daysinyear
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const validStrings = [
+ "2000-05-02T00+00:00",
+ "2000-05-02T00+00:00[UTC]",
+ "2000-05-02T00+00:00[!UTC]",
+ "2000-05-02T00-02:30[America/St_Johns]",
+];
+
+for (const arg of validStrings) {
+ const result = instance.daysInYear(arg);
+
+ assert.sameValue(
+ result,
+ 366,
+ `"${arg}" is a valid UTC offset with time for PlainDate`
+ );
+}
+
+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.daysInYear(arg),
+ `"${arg}" UTC offset without time is not valid for PlainDate`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-string-invalid.js
new file mode 100644
index 0000000000..e26385f3ba
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-string-invalid.js
@@ -0,0 +1,64 @@
+// |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.calendar.prototype.daysinyear
+description: >
+ RangeError thrown if an invalid ISO string (or syntactically valid ISO string
+ that is not supported) is used as a PlainDate
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ // invalid ISO strings:
+ "",
+ "invalid iso8601",
+ "2020-01-00",
+ "2020-01-32",
+ "2020-02-30",
+ "2021-02-29",
+ "2020-00-01",
+ "2020-13-01",
+ "2020-01-01T",
+ "2020-01-01T25:00:00",
+ "2020-01-01T01:60:00",
+ "2020-01-01T01:60:61",
+ "2020-01-01junk",
+ "2020-01-01T00:00:00junk",
+ "2020-01-01T00:00:00+00:00junk",
+ "2020-01-01T00:00:00+00:00[UTC]junk",
+ "2020-01-01T00:00:00+00:00[UTC][u-ca=iso8601]junk",
+ "02020-01-01",
+ "2020-001-01",
+ "2020-01-001",
+ "2020-01-01T001",
+ "2020-01-01T01:001",
+ "2020-01-01T01:01:001",
+ // valid, but forms not supported in Temporal:
+ "2020-W01-1",
+ "2020-001",
+ "+0002020-01-01",
+ // valid, but this calendar must not exist:
+ "2020-01-01[u-ca=notexist]",
+ // may be valid in other contexts, but insufficient information for PlainDate:
+ "2020-01",
+ "+002020-01",
+ "01-01",
+ "2020-W01",
+ "P1Y",
+ "-P12Y",
+ // valid, but outside the supported range:
+ "-999999-01-01",
+ "+999999-01-01",
+];
+const instance = new Temporal.Calendar("iso8601");
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => instance.daysInYear(arg),
+ `"${arg}" should not be a valid ISO string for a PlainDate`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..5b8160db1f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/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.calendar.prototype.daysinyear
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[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.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.daysInYear(arg),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..37f4be64b6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-string-multiple-time-zone.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.calendar.prototype.daysinyear
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[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.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.daysInYear(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-string-time-separators.js
new file mode 100644
index 0000000000..d5b474c4ae
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-string-time-separators.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.calendar.prototype.daysinyear
+description: Time separator in string argument can vary
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02T15:23", "uppercase T"],
+ ["2000-05-02t15:23", "lowercase T"],
+ ["2000-05-02 15:23", "space between date and time"],
+];
+
+const instance = new Temporal.Calendar("iso8601");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.daysInYear(arg);
+
+ assert.sameValue(
+ result,
+ 366,
+ `variant time separators (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..be37b75348
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-string-time-zone-annotation.js
@@ -0,0 +1,38 @@
+// |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.calendar.prototype.daysinyear
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02[Asia/Kolkata]", "named, with no time"],
+ ["2000-05-02[!Europe/Vienna]", "named, with ! and no time"],
+ ["2000-05-02[+00:00]", "numeric, with no time"],
+ ["2000-05-02[!-02:30]", "numeric, with ! and no time"],
+ ["2000-05-02T15:23[America/Sao_Paulo]", "named, with no offset"],
+ ["2000-05-02T15:23[!Asia/Tokyo]", "named, with ! and no offset"],
+ ["2000-05-02T15:23[-02:30]", "numeric, with no offset"],
+ ["2000-05-02T15:23[!+00:00]", "numeric, with ! and no offset"],
+ ["2000-05-02T15:23+00:00[America/New_York]", "named, with offset"],
+ ["2000-05-02T15:23+00:00[!UTC]", "named, with offset and !"],
+ ["2000-05-02T15:23+00:00[+01:00]", "numeric, with offset"],
+ ["2000-05-02T15:23+00:00[!-08:00]", "numeric, with offset and !"],
+];
+
+const instance = new Temporal.Calendar("iso8601");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.daysInYear(arg);
+
+ assert.sameValue(
+ result,
+ 366,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..56f5429ea1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-string-unknown-annotation.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.calendar.prototype.daysinyear
+description: Various forms of unknown annotation
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02[foo=bar]", "without time"],
+ ["2000-05-02T15:23[foo=bar]", "alone"],
+ ["2000-05-02T15:23[UTC][foo=bar]", "with time zone"],
+ ["2000-05-02T15:23[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["2000-05-02T15:23[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["2000-05-02T15:23[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+const instance = new Temporal.Calendar("iso8601");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.daysInYear(arg);
+
+ assert.sameValue(
+ result,
+ 366,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..0c09cafed0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-string-with-utc-designator.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.calendar.prototype.daysinyear
+description: RangeError thrown if a string with UTC designator is used as a PlainDate
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "2019-10-01T09:00:00Z",
+ "2019-10-01T09:00:00Z[UTC]",
+];
+const instance = new Temporal.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.daysInYear(arg),
+ "String with UTC designator should not be valid as a PlainDate"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-wrong-type.js
new file mode 100644
index 0000000000..d74e334fb7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/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.calendar.prototype.daysinyear
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainDate
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+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.daysInYear(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.PlainDate, "Temporal.PlainDate, object"],
+ [Temporal.PlainDate.prototype, "Temporal.PlainDate.prototype, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.daysInYear(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/Calendar/prototype/daysInYear/argument-zoneddatetime-convert.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-zoneddatetime-convert.js
new file mode 100644
index 0000000000..3788acb0b0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-zoneddatetime-convert.js
@@ -0,0 +1,22 @@
+// |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.calendar.prototype.daysinyear
+description: An exception from TimeZone#getOffsetNanosecondsFor() is propagated.
+features: [Temporal]
+---*/
+
+class TZ extends Temporal.TimeZone {
+ constructor() { super("UTC") }
+ getOffsetNanosecondsFor() { throw new Test262Error() }
+}
+
+const tz = new TZ();
+const arg = new Temporal.ZonedDateTime(0n, tz);
+const instance = new Temporal.Calendar("iso8601");
+
+assert.throws(Test262Error, () => instance.daysInYear(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-zoneddatetime-slots.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-zoneddatetime-slots.js
new file mode 100644
index 0000000000..806c8a0da8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-zoneddatetime-slots.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.calendar.prototype.daysinyear
+description: Getters are not called when converting a ZonedDateTime to a PlainDate.
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.ZonedDateTime.prototype);
+const getters = ["year", "month", "monthCode", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond", "calendar"];
+
+for (const property of getters) {
+ Object.defineProperty(Temporal.ZonedDateTime.prototype, property, {
+ get() {
+ actual.push(`get ${property}`);
+ const value = prototypeDescrs[property].get.call(this);
+ return {
+ toString() {
+ actual.push(`toString ${property}`);
+ return value.toString();
+ },
+ valueOf() {
+ actual.push(`valueOf ${property}`);
+ return value;
+ },
+ };
+ },
+ });
+}
+
+const arg = new Temporal.ZonedDateTime(0n, "UTC");
+const instance = new Temporal.Calendar("iso8601");
+instance.daysInYear(arg);
+assert.compareArray(actual, []);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..27c987cc47
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/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.calendar.prototype.daysinyear
+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 calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => calendar.daysInYear(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..ba08e54bc6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/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.calendar.prototype.daysinyear
+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 calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => calendar.daysInYear(datetime),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..f393a863b9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/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.calendar.prototype.daysinyear
+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 calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => calendar.daysInYear(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..70ce4d3e49
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/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.calendar.prototype.daysinyear
+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 calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => calendar.daysInYear(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/basic.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/basic.js
new file mode 100644
index 0000000000..75c610132f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/basic.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.calendar.prototype.daysinyear
+description: Basic tests for daysInYear().
+features: [Temporal]
+---*/
+
+const iso = Temporal.Calendar.from("iso8601");
+const res = 365;
+assert.sameValue(iso.daysInYear(Temporal.PlainDate.from("1994-11-05")), res, "PlainDate");
+assert.sameValue(iso.daysInYear(Temporal.PlainDateTime.from("1994-11-05T08:15:30")), res, "PlainDateTime");
+assert.sameValue(iso.daysInYear(Temporal.PlainYearMonth.from("1994-11")), res, "PlainYearMonth");
+assert.sameValue(iso.daysInYear({ year: 1994, month: 11, day: 5 }), res, "property bag");
+assert.sameValue(iso.daysInYear("1994-11-05"), res, "string");
+assert.throws(TypeError, () => iso.daysInYear({ year: 2000 }), "property bag with missing properties");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/branding.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/branding.js
new file mode 100644
index 0000000000..803d4ac8c6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/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.calendar.prototype.daysinyear
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const daysInYear = Temporal.Calendar.prototype.daysInYear;
+
+assert.sameValue(typeof daysInYear, "function");
+
+const args = [new Temporal.PlainDate(2000, 1, 1)];
+
+assert.throws(TypeError, () => daysInYear.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => daysInYear.apply(null, args), "null");
+assert.throws(TypeError, () => daysInYear.apply(true, args), "true");
+assert.throws(TypeError, () => daysInYear.apply("", args), "empty string");
+assert.throws(TypeError, () => daysInYear.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => daysInYear.apply(1, args), "1");
+assert.throws(TypeError, () => daysInYear.apply({}, args), "plain object");
+assert.throws(TypeError, () => daysInYear.apply(Temporal.Calendar, args), "Temporal.Calendar");
+assert.throws(TypeError, () => daysInYear.apply(Temporal.Calendar.prototype, args), "Temporal.Calendar.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/browser.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/builtin.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/builtin.js
new file mode 100644
index 0000000000..32cbf094c4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/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.calendar.prototype.daysinyear
+description: >
+ Tests that Temporal.Calendar.prototype.daysInYear
+ 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.Calendar.prototype.daysInYear),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Calendar.prototype.daysInYear),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Calendar.prototype.daysInYear),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Calendar.prototype.daysInYear.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/calendar-datefromfields-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/calendar-datefromfields-called-with-options-undefined.js
new file mode 100644
index 0000000000..7d7c2c34ec
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/calendar-datefromfields-called-with-options-undefined.js
@@ -0,0 +1,18 @@
+// |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.calendar.prototype.daysinyear
+description: >
+ Calendar.dateFromFields method is called with undefined as the options value
+ when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
+calendar.daysInYear({ year: 2000, month: 5, day: 3, calendar });
+assert.sameValue(calendar.dateFromFieldsCallCount, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/calendar-fields-iterable.js
new file mode 100644
index 0000000000..8c09cdeb78
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/calendar-fields-iterable.js
@@ -0,0 +1,37 @@
+// |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.calendar.prototype.daysinyear
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.calendar.prototype.daysinyear step 4:
+ 4. Let _year_ be ? ISOYear(_dateOrDateTime_).
+ sec-temporal-isoyear step 1.a:
+ a. Set _dateOrDateTime_ to ? ToTemporalDate(_dateOrDateTime_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToListOfType(_fieldsArray_, « String »).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+calendar1.daysInYear({ year: 2000, month: 5, day: 2, calendar: calendar2 });
+
+assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/calendar-temporal-object.js
new file mode 100644
index 0000000000..47db820adf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/calendar-temporal-object.js
@@ -0,0 +1,31 @@
+// |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.calendar.prototype.daysinyear
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.calendar.prototype.daysinyear step 4:
+ 4. Let _year_ be ? ISOYear(_dateOrDateTime_).
+ sec-temporal-isoyear step 1.a:
+ a. Set _dateOrDateTime_ to ? ToTemporalDate(_dateOrDateTime_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const calendar = new Temporal.Calendar("iso8601");
+ calendar.daysInYear({ year: 2000, month: 5, day: 2, calendar: temporalObject });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..5c3b5b8bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/infinity-throws-rangeerror.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.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.calendar.prototype.daysinyear
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+const base = { year: 2000, month: 5, day: 2 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.daysInYear({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.daysInYear({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/length.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/length.js
new file mode 100644
index 0000000000..abf641692b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/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.calendar.prototype.daysinyear
+description: Temporal.Calendar.prototype.daysInYear.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.Calendar.prototype.daysInYear, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/name.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/name.js
new file mode 100644
index 0000000000..62a1d4ca2c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/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.calendar.prototype.daysinyear
+description: Temporal.Calendar.prototype.daysInYear.name is "daysInYear".
+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.Calendar.prototype.daysInYear, "name", {
+ value: "daysInYear",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/not-a-constructor.js
new file mode 100644
index 0000000000..a601f0132a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/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.calendar.prototype.daysinyear
+description: >
+ Temporal.Calendar.prototype.daysInYear 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.Calendar.prototype.daysInYear();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Calendar.prototype.daysInYear), false,
+ "isConstructor(Temporal.Calendar.prototype.daysInYear)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/plain-date-time.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/plain-date-time.js
new file mode 100644
index 0000000000..8782edd51b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/plain-date-time.js
@@ -0,0 +1,94 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.calendar.prototype.daysinyear
+description: >
+ Temporal.Calendar.prototype.daysInYear will take PlainDateTime and return
+ the number of days in a year.
+info: |
+ 4. If Type(temporalDateLike) is not Object or temporalDateLike does not have an [[InitializedTemporalDate]] or [[InitializedTemporalYearMonth]] internal slot, then
+ a. Set temporalDateLike to ? ToTemporalDate(temporalDateLike).
+ 5. Return 𝔽(! ISODaysInYear(temporalDateLike.[[ISOYear]])).
+features: [Temporal]
+---*/
+let cal = new Temporal.Calendar("iso8601");
+
+let dt = new Temporal.PlainDateTime(1995, 8, 23, 5, 30, 13);
+assert.sameValue(
+ cal.daysInYear(dt),
+ 365,
+ 'cal.daysInYear(new Temporal.PlainDateTime(1995, 8, 23, 5, 30, 13)) must return 365'
+);
+
+dt = new Temporal.PlainDateTime(1996, 8, 23, 5, 30, 13);
+assert.sameValue(
+ cal.daysInYear(dt),
+ 366,
+ 'cal.daysInYear(new Temporal.PlainDateTime(1996, 8, 23, 5, 30, 13)) must return 366'
+);
+
+dt = new Temporal.PlainDateTime(1997, 8, 23, 5, 30, 13);
+assert.sameValue(
+ cal.daysInYear(dt),
+ 365,
+ 'cal.daysInYear(new Temporal.PlainDateTime(1997, 8, 23, 5, 30, 13)) must return 365'
+);
+
+dt = new Temporal.PlainDateTime(1998, 8, 23, 5, 30, 13);
+assert.sameValue(
+ cal.daysInYear(dt),
+ 365,
+ 'cal.daysInYear(new Temporal.PlainDateTime(1998, 8, 23, 5, 30, 13)) must return 365'
+);
+
+dt = new Temporal.PlainDateTime(1999, 8, 23, 5, 30, 13);
+assert.sameValue(
+ cal.daysInYear(dt),
+ 365,
+ 'cal.daysInYear(new Temporal.PlainDateTime(1999, 8, 23, 5, 30, 13)) must return 365'
+);
+
+dt = new Temporal.PlainDateTime(2000, 8, 23, 5, 30, 13);
+assert.sameValue(
+ cal.daysInYear(dt),
+ 366,
+ 'cal.daysInYear(new Temporal.PlainDateTime(2000, 8, 23, 5, 30, 13)) must return 366'
+);
+
+dt = new Temporal.PlainDateTime(2001, 8, 23, 5, 30, 13);
+assert.sameValue(
+ cal.daysInYear(dt),
+ 365,
+ 'cal.daysInYear(new Temporal.PlainDateTime(2001, 8, 23, 5, 30, 13)) must return 365'
+);
+
+dt = new Temporal.PlainDateTime(2002, 8, 23, 5, 30, 13);
+assert.sameValue(
+ cal.daysInYear(dt),
+ 365,
+ 'cal.daysInYear(new Temporal.PlainDateTime(2002, 8, 23, 5, 30, 13)) must return 365'
+);
+
+dt = new Temporal.PlainDateTime(2003, 8, 23, 5, 30, 13);
+assert.sameValue(
+ cal.daysInYear(dt),
+ 365,
+ 'cal.daysInYear(new Temporal.PlainDateTime(2003, 8, 23, 5, 30, 13)) must return 365'
+);
+
+dt = new Temporal.PlainDateTime(2004, 8, 23, 5, 30, 13);
+assert.sameValue(
+ cal.daysInYear(dt),
+ 366,
+ 'cal.daysInYear(new Temporal.PlainDateTime(2004, 8, 23, 5, 30, 13)) must return 366'
+);
+
+dt = new Temporal.PlainDateTime(2005, 8, 23, 5, 30, 13);
+assert.sameValue(
+ cal.daysInYear(dt),
+ 365,
+ 'cal.daysInYear(new Temporal.PlainDateTime(2005, 8, 23, 5, 30, 13)) must return 365'
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/plain-date.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/plain-date.js
new file mode 100644
index 0000000000..54b3383c1e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/plain-date.js
@@ -0,0 +1,48 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.calendar.prototype.daysinyear
+description: >
+ Temporal.Calendar.prototype.daysInYear will take PlainDate and return
+ the number of days in a year.
+info: |
+ 5. Return 𝔽(! ISODaysInYear(temporalDateLike.[[ISOYear]])).
+features: [Temporal]
+---*/
+let cal = new Temporal.Calendar("iso8601");
+
+let d = new Temporal.PlainDate(1995, 7, 15);
+assert.sameValue(cal.daysInYear(d), 365, 'cal.daysInYear(new Temporal.PlainDate(1995, 7, 15)) must return 365');
+
+d = new Temporal.PlainDate(1996, 7, 15);
+assert.sameValue(cal.daysInYear(d), 366, 'cal.daysInYear(new Temporal.PlainDate(1996, 7, 15)) must return 366');
+
+d = new Temporal.PlainDate(1997, 7, 15);
+assert.sameValue(cal.daysInYear(d), 365, 'cal.daysInYear(new Temporal.PlainDate(1997, 7, 15)) must return 365');
+
+d = new Temporal.PlainDate(1998, 7, 15);
+assert.sameValue(cal.daysInYear(d), 365, 'cal.daysInYear(new Temporal.PlainDate(1998, 7, 15)) must return 365');
+
+d = new Temporal.PlainDate(1999, 7, 15);
+assert.sameValue(cal.daysInYear(d), 365, 'cal.daysInYear(new Temporal.PlainDate(1999, 7, 15)) must return 365');
+
+d = new Temporal.PlainDate(2000, 7, 15);
+assert.sameValue(cal.daysInYear(d), 366, 'cal.daysInYear(new Temporal.PlainDate(2000, 7, 15)) must return 366');
+
+d = new Temporal.PlainDate(2001, 7, 15);
+assert.sameValue(cal.daysInYear(d), 365, 'cal.daysInYear(new Temporal.PlainDate(2001, 7, 15)) must return 365');
+
+d = new Temporal.PlainDate(2002, 7, 15);
+assert.sameValue(cal.daysInYear(d), 365, 'cal.daysInYear(new Temporal.PlainDate(2002, 7, 15)) must return 365');
+
+d = new Temporal.PlainDate(2003, 7, 15);
+assert.sameValue(cal.daysInYear(d), 365, 'cal.daysInYear(new Temporal.PlainDate(2003, 7, 15)) must return 365');
+
+d = new Temporal.PlainDate(2004, 7, 15);
+assert.sameValue(cal.daysInYear(d), 366, 'cal.daysInYear(new Temporal.PlainDate(2004, 7, 15)) must return 366');
+
+d = new Temporal.PlainDate(2005, 7, 15);
+assert.sameValue(cal.daysInYear(d), 365, 'cal.daysInYear(new Temporal.PlainDate(2005, 7, 15)) must return 365');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/prop-desc.js
new file mode 100644
index 0000000000..77e93a567c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/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.calendar.prototype.daysinyear
+description: The "daysInYear" property of Temporal.Calendar.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Calendar.prototype.daysInYear,
+ "function",
+ "`typeof Calendar.prototype.daysInYear` is `function`"
+);
+
+verifyProperty(Temporal.Calendar.prototype, "daysInYear", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/shell.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/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/Calendar/prototype/daysInYear/string.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/string.js
new file mode 100644
index 0000000000..f1697e8fc9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/string.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.calendar.prototype.daysinyear
+description: >
+ Temporal.Calendar.prototype.daysInYear will take PlainDate and return
+ the number of days in a year.
+info: |
+ 4. If Type(temporalDateLike) is not Object or temporalDateLike does not have an [[InitializedTemporalDate]] or [[InitializedTemporalYearMonth]] internal slot, then
+ a. Set temporalDateLike to ? ToTemporalDate(temporalDateLike).
+ 5. Return 𝔽(! ISODaysInYear(temporalDateLike.[[ISOYear]])).
+features: [Temporal]
+---*/
+let cal = new Temporal.Calendar("iso8601");
+
+assert.sameValue(cal.daysInYear("2019-03-18"), 365, 'cal.daysInYear("2019-03-18") must return 365');
+assert.sameValue(cal.daysInYear("2020-03-18"), 366, 'cal.daysInYear("2020-03-18") must return 366');
+assert.sameValue(cal.daysInYear("2021-03-18"), 365, 'cal.daysInYear("2021-03-18") must return 365');
+assert.sameValue(cal.daysInYear("2022-03-18"), 365, 'cal.daysInYear("2022-03-18") must return 365');
+assert.sameValue(cal.daysInYear("2023-03-18"), 365, 'cal.daysInYear("2023-03-18") must return 365');
+assert.sameValue(cal.daysInYear("2024-03-18"), 366, 'cal.daysInYear("2024-03-18") must return 366');
+assert.sameValue(cal.daysInYear("2025-03-18"), 365, 'cal.daysInYear("2025-03-18") must return 365');
+assert.sameValue(cal.daysInYear("2026-03-18"), 365, 'cal.daysInYear("2026-03-18") must return 365');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/throw-range-error-ToTemporalDate.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/throw-range-error-ToTemporalDate.js
new file mode 100644
index 0000000000..40fff73941
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/throw-range-error-ToTemporalDate.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.calendar.prototype.daysInYear
+description: >
+ Temporal.Calendar.prototype.daysInYear throws RangeError on
+ ToTemporalDate when temporalDateLike is invalid string.
+info: |
+ 4. If Type(temporalDateLike) is not Object or temporalDateLike does
+ not have an [[InitializedTemporalDate]] or [[InitializedTemporalYearMonth]]
+ internal slot, then
+ a. Set temporalDateLike to ? ToTemporalDate(temporalDateLike).
+features: [Temporal, arrow-function]
+---*/
+let cal = new Temporal.Calendar("iso8601");
+
+assert.throws(RangeError, () => cal.daysInYear("invalid string"),
+ 'cal.daysInYear("invalid string") throws a RangeError exception');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/year-zero.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/year-zero.js
new file mode 100644
index 0000000000..8fd8c110a9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/daysInYear/year-zero.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.calendar.prototype.daysinyear
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T00:45",
+ "-000000-10-31T00:45+01:00",
+ "-000000-10-31T00:45+00:00[UTC]",
+];
+const instance = new Temporal.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.daysInYear(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/fields/argument-iterable-not-array.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/fields/argument-iterable-not-array.js
new file mode 100644
index 0000000000..ead494c99e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/fields/argument-iterable-not-array.js
@@ -0,0 +1,30 @@
+// |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.calendar.prototype.fields
+description: A non-Array iterable passed as the argument is exhausted
+info: |
+ sec-temporal.calendar.prototype.fields step 4:
+ 4. Let _fieldNames_ be ? IterableToList(_fields_).
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const fieldNames = ["day", "month", "monthCode", "year"];
+const iterable = {
+ iteratorExhausted: false,
+ *[Symbol.iterator]() {
+ yield* fieldNames;
+ this.iteratorExhausted = true;
+ },
+};
+
+const calendar = new Temporal.Calendar("iso8601");
+const result = calendar.fields(iterable);
+
+assert.compareArray(result, fieldNames);
+assert(iterable.iteratorExhausted);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/fields/argument-throws-duplicate-keys.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/fields/argument-throws-duplicate-keys.js
new file mode 100644
index 0000000000..be868d3bf7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/fields/argument-throws-duplicate-keys.js
@@ -0,0 +1,31 @@
+// |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.calendar.prototype.fields
+description: Duplicate fields are not allowed in the argument to Calendar.prototype.fields
+info: |
+ sec-temporal.calendar.prototype.fields step 7.b.iii:
+ iii. If _fieldNames_ contains _nextValue_, then
+ 1. Let _completion_ be ThrowCompletion(a newly created *RangeError* object).
+ 2. Return ? IteratorClose(_iteratorRecord_, _completion_).
+features: [Temporal]
+---*/
+
+const calendar = new Temporal.Calendar("iso8601");
+assert.throws(RangeError, () => calendar.fields(["day", "month", "day"]));
+assert.throws(RangeError, () => calendar.fields(["year", "month", "monthCode", "day", "year"]));
+
+const manyFields = {
+ count: 0
+};
+
+manyFields[Symbol.iterator] = function*() {
+ while (this.count++ < 100) yield "year";
+};
+
+assert.throws(RangeError, () => calendar.fields(manyFields), "Rejected duplicate values");
+assert.sameValue(manyFields.count, 2, "Rejected first duplicate value");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/fields/argument-throws-invalid-keys.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/fields/argument-throws-invalid-keys.js
new file mode 100644
index 0000000000..81bc2e5d64
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/fields/argument-throws-invalid-keys.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.calendar.prototype.fields
+description: >
+ Calendar.prototype.fields rejects input field names that are not singular
+ names of Temporal date units
+info: |
+ sec-temporal.calendar.prototype.fields step 7.b.ii:
+ 7. Repeat, while next is not false,
+ a. Set next to ? IteratorStep(iteratorRecord).
+ b. If next is not false, then
+ i. Let nextValue be ? IteratorValue(next).
+ ii. If Type(nextValue) is not String, then
+ 1. Let completion be ThrowCompletion(a newly created TypeError object).
+ 2. Return ? IteratorClose(iteratorRecord, completion).
+ sec-temporal.calendar.prototype.fields step 7.b.iv:
+ iv. If _nextValue_ is not one of *"year"*, *"month"*, *"monthCode"*, or *"day"*, then
+ 1. Let _completion_ be ThrowCompletion(a newly created *RangeError* object).
+ 2. Return ? IteratorClose(_iteratorRecord_, _completion_).
+
+features: [Temporal]
+---*/
+
+const calendar = new Temporal.Calendar("iso8601");
+assert.throws(TypeError, () => calendar.fields([1]));
+assert.throws(TypeError, () => calendar.fields([1n]));
+assert.throws(TypeError, () => calendar.fields([Symbol('foo')]));
+assert.throws(TypeError, () => calendar.fields([true]));
+assert.throws(TypeError, () => calendar.fields([null]));
+assert.throws(TypeError, () => calendar.fields([{}]));
+assert.throws(TypeError, () => calendar.fields([undefined]));
+assert.throws(TypeError, () => calendar.fields(["day", 1]));
+assert.throws(RangeError, () => calendar.fields(["hour"]));
+assert.throws(RangeError, () => calendar.fields(["minute"]));
+assert.throws(RangeError, () => calendar.fields(["second"]));
+assert.throws(RangeError, () => calendar.fields(["millisecond"]));
+assert.throws(RangeError, () => calendar.fields(["microsecond"]));
+assert.throws(RangeError, () => calendar.fields(["nanosecond"]));
+assert.throws(RangeError, () => calendar.fields(["month", "days"]));
+assert.throws(RangeError, () => calendar.fields(["days"]));
+assert.throws(RangeError, () => calendar.fields(["people"]));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/fields/branding.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/fields/branding.js
new file mode 100644
index 0000000000..fbfb89f314
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/fields/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.calendar.prototype.fields
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const fields = Temporal.Calendar.prototype.fields;
+
+assert.sameValue(typeof fields, "function");
+
+const args = [[]];
+
+assert.throws(TypeError, () => fields.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => fields.apply(null, args), "null");
+assert.throws(TypeError, () => fields.apply(true, args), "true");
+assert.throws(TypeError, () => fields.apply("", args), "empty string");
+assert.throws(TypeError, () => fields.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => fields.apply(1, args), "1");
+assert.throws(TypeError, () => fields.apply({}, args), "plain object");
+assert.throws(TypeError, () => fields.apply(Temporal.Calendar, args), "Temporal.Calendar");
+assert.throws(TypeError, () => fields.apply(Temporal.Calendar.prototype, args), "Temporal.Calendar.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/fields/browser.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/fields/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/fields/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/fields/builtin.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/fields/builtin.js
new file mode 100644
index 0000000000..5c8387cd05
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/fields/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.calendar.prototype.fields
+description: >
+ Tests that Temporal.Calendar.prototype.fields
+ 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.Calendar.prototype.fields),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Calendar.prototype.fields),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Calendar.prototype.fields),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Calendar.prototype.fields.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/fields/length.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/fields/length.js
new file mode 100644
index 0000000000..312c745db3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/fields/length.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.calendar.prototype.fields
+description: Temporal.Calendar.prototype.fields.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.Calendar.prototype.fields, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/fields/long-input.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/fields/long-input.js
new file mode 100644
index 0000000000..88410f3d05
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/fields/long-input.js
@@ -0,0 +1,50 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.fields
+description: >
+ Temporal.Calendar.prototype.fields will throw when its input iterable yields an
+ invalid field.
+info: |
+ ## 12.4.21 Temporal.Calendar.prototype.fields ( fields )
+ 1. Let calendar be the this value.
+ 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
+ 4. Let iteratorRecord be ? Getiterator(fields, sync).
+ 5. Let fieldNames be a new empty List.
+ 6. Let next be true.
+ 7. Repeat, while next is not false,
+ a. Set next to ? IteratorStep(iteratorRecord).
+ b. If next is not false, then
+ i. Let nextValue be ? IteratorValue(next).
+ iv. If nextValue is not one of "year", "month", "monthCode", or "day", then
+ 1. Let completion be ThrowCompletion(a newly created RangeError object).
+ 2. Return ? IteratorClose(iteratorRecord, completion).
+features: [Symbol, Symbol.iterator, Temporal, computed-property-names, generators]
+---*/
+let cal = new Temporal.Calendar("iso8601")
+let i = 0;
+const fields = {
+ *[Symbol.iterator]() {
+ // The first three are valid values
+ yield "year";
+ i++;
+ yield "month";
+ i++;
+ yield "monthCode";
+ i++;
+ // The fourth one is wrong and should throw after the next line.
+ yield "garbage";
+ // The following three lines should not be reached if the implemention
+ // correctly check the previous line.
+ i++;
+ yield "day";
+ i++;
+ }
+}
+assert.throws(RangeError, () => cal.fields(fields), "Garbage content");
+// stop after the third one.
+assert.sameValue(i, 3);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/fields/name.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/fields/name.js
new file mode 100644
index 0000000000..16a1efe247
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/fields/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.calendar.prototype.fields
+description: Temporal.Calendar.prototype.fields.name is "fields".
+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.Calendar.prototype.fields, "name", {
+ value: "fields",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/fields/non-string-element-throws.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/fields/non-string-element-throws.js
new file mode 100644
index 0000000000..c7627ff747
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/fields/non-string-element-throws.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.calendar.prototype.fields
+description: TypeError thrown if the input iterable yields a non-String value
+info: |
+ sec-temporal.calendar.prototype.fields step 5:
+ 5. For each element _fieldName_ of _fieldNames_, do
+ a. If Type(_fieldName_) is not String, throw a *TypeError* exception.
+features: [Temporal]
+---*/
+
+const calendar = new Temporal.Calendar("iso8601");
+[true, 3, 3n, {}, () => {}, Symbol(), undefined, null].forEach((element) => {
+ assert.throws(TypeError, () => calendar.fields([element]), "bad input to calendar.fields()");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/fields/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/fields/not-a-constructor.js
new file mode 100644
index 0000000000..da8317c185
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/fields/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.calendar.prototype.fields
+description: >
+ Temporal.Calendar.prototype.fields 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.Calendar.prototype.fields();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Calendar.prototype.fields), false,
+ "isConstructor(Temporal.Calendar.prototype.fields)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/fields/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/fields/prop-desc.js
new file mode 100644
index 0000000000..4461f5bd3c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/fields/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.calendar.prototype.fields
+description: The "fields" property of Temporal.Calendar.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Calendar.prototype.fields,
+ "function",
+ "`typeof Calendar.prototype.fields` is `function`"
+);
+
+verifyProperty(Temporal.Calendar.prototype, "fields", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/fields/repeated-throw.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/fields/repeated-throw.js
new file mode 100644
index 0000000000..58bb67dc6c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/fields/repeated-throw.js
@@ -0,0 +1,58 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.fields
+description: >
+ Temporal.Calendar.prototype.fields will throw if its input iterable yields
+ the same value twice.
+info: |
+ ## 12.4.21 Temporal.Calendar.prototype.fields ( fields )
+ 1. Let calendar be the this value.
+ 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
+ 4. Let iteratorRecord be ? Getiterator(fields, sync).
+ 5. Let fieldNames be a new empty List.
+ 6. Let next be true.
+ 7. Repeat, while next is not false,
+ a. Set next to ? IteratorStep(iteratorRecord).
+ b. If next is not false, then
+ i. Let nextValue be ? IteratorValue(next).
+ iii. If fieldNames contains nextValue, then
+ 1. Let completion be ThrowCompletion(a newly created RangeError object).
+ 2. Return ? IteratorClose(iteratorRecord, completion).
+features: [Symbol, Symbol.iterator, Temporal, computed-property-names, generators]
+---*/
+let cal = new Temporal.Calendar("iso8601")
+let i = 0;
+const fields = {
+ *[Symbol.iterator]() {
+ yield "month";
+ i++;
+ yield "year";
+ i++;
+ yield "year";
+ i++;
+ }
+}
+assert.throws(
+ RangeError, () => cal.fields(fields), "repeated valid value should throw");
+assert.sameValue(i, 2, "Should stop at 2");
+
+// Test all valid values will throw when repeated
+[ "day", "monthCode", "month", "year" ].forEach((f) => {
+ i = 0;
+ const fields2 = {
+ *[Symbol.iterator]() {
+ yield f;
+ i++;
+ yield f;
+ i++;
+ }
+ }
+ assert.throws(
+ RangeError, () => cal.fields(fields2), "repeated valid value should throw");
+ assert.sameValue(i, 1, "Should stop at 1");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/fields/reverse.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/fields/reverse.js
new file mode 100644
index 0000000000..88a1721cfd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/fields/reverse.js
@@ -0,0 +1,39 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.fields
+description: >
+ Temporal.Calendar.prototype.fields will return the iterable in array if all
+ input are valid regardless of it's order.
+info: |
+ ## 12.4.21 Temporal.Calendar.prototype.fields ( fields )
+ 1. Let calendar be the this value.
+ 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
+ 4. Let iteratorRecord be ? Getiterator(fields, sync).
+ 5. Let fieldNames be a new empty List.
+ 6. Let next be true.
+ 7. Repeat, while next is not false,
+ a. Set next to ? IteratorStep(iteratorRecord).
+ b. If next is not false, then
+ i. Let nextValue be ? IteratorValue(next).
+ iv. If nextValue is not one of "year", "month", "monthCode", or "day", then
+ 1. Let completion be ThrowCompletion(a newly created RangeError object).
+ 2. Return ? IteratorClose(iteratorRecord, completion).
+features: [Symbol, Symbol.iterator, Temporal, computed-property-names, generators]
+includes: [compareArray.js]
+---*/
+let cal = new Temporal.Calendar("iso8601")
+const fields = {
+ *[Symbol.iterator]() {
+ yield "day";
+ yield "monthCode";
+ yield "month";
+ yield "year";
+ }
+}
+assert.compareArray(cal.fields(fields), Array.from(fields),
+ 'valid fields should be supported even if they are in reversed order of the spec');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/fields/shell.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/fields/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/fields/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/Calendar/prototype/id/branding.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/id/branding.js
new file mode 100644
index 0000000000..447fe2b6c1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/id/branding.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-get-temporal.calendar.prototype.id
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "id");
+const id = descriptor.get;
+
+assert.sameValue(typeof id, "function");
+
+assert.throws(TypeError, () => id.call(undefined), "undefined");
+assert.throws(TypeError, () => id.call(null), "null");
+assert.throws(TypeError, () => id.call(true), "true");
+assert.throws(TypeError, () => id.call(""), "empty string");
+assert.throws(TypeError, () => id.call(Symbol()), "symbol");
+assert.throws(TypeError, () => id.call(1), "1");
+assert.throws(TypeError, () => id.call({}), "plain object");
+assert.throws(TypeError, () => id.call(Temporal.Calendar), "Temporal.Calendar");
+assert.throws(TypeError, () => id.call(Temporal.Calendar.prototype), "Temporal.Calendar.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/id/browser.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/id/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/id/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/id/custom-calendar.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/id/custom-calendar.js
new file mode 100644
index 0000000000..25e5499b48
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/id/custom-calendar.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-get-temporal.calendar.prototype.id
+description: Getter does not call toString(), returns the ID from internal slot
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const expected = [];
+
+const calendar = new Temporal.Calendar("iso8601");
+TemporalHelpers.observeProperty(actual, calendar, Symbol.toPrimitive, undefined);
+TemporalHelpers.observeProperty(actual, calendar, "toString", function () {
+ actual.push("call calendar.toString");
+ return "calendar ID";
+});
+
+const result = calendar.id;
+assert.compareArray(actual, expected);
+assert.sameValue(result, "iso8601");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/id/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/id/prop-desc.js
new file mode 100644
index 0000000000..13d41bea22
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/id/prop-desc.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-get-temporal.calendar.prototype.id
+description: The "id" property of Temporal.Calendar.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "id");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/id/shell.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/id/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/id/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..8fa53a0782
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-builtin-calendar-no-array-iteration.js
@@ -0,0 +1,24 @@
+// |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.calendar.prototype.inleapyear
+description: >
+ Calling the method with a property bag argument with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.Calendar("iso8601");
+const arg = { year: 2000, month: 5, day: 2, calendar: "iso8601" };
+instance.inLeapYear(arg);
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-calendar-datefromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..e265505050
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-calendar-datefromfields-called-with-null-prototype-fields.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.calendar.prototype.inleapyear
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.Calendar("iso8601");
+const arg = { year: 2000, month: 5, day: 2, calendar };
+instance.inLeapYear(arg);
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..767c8ba1b2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-constructor-in-calendar-fields.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.calendar.prototype.inleapyear
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.Calendar("iso8601");
+
+assert.throws(RangeError, () => instance.inLeapYear(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-duplicate-calendar-fields.js
new file mode 100644
index 0000000000..30586d5b8e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-duplicate-calendar-fields.js
@@ -0,0 +1,19 @@
+// |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.calendar.prototype.inleapyear
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['day'], ['month'], ['monthCode'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar };
+ const instance = new Temporal.Calendar("iso8601");
+
+ assert.throws(RangeError, () => instance.inLeapYear(arg));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-leap-second.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-leap-second.js
new file mode 100644
index 0000000000..294783583a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-leap-second.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.calendar.prototype.inleapyear
+description: Leap second is a valid ISO string for PlainDate
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+let arg = "2016-12-31T23:59:60";
+const result1 = instance.inLeapYear(arg);
+assert.sameValue(
+ result1,
+ true,
+ "leap second is a valid ISO string for PlainDate"
+);
+
+arg = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+const result2 = instance.inLeapYear(arg);
+assert.sameValue(
+ result2,
+ true,
+ "second: 60 is ignored in property bag for PlainDate"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-number.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-number.js
new file mode 100644
index 0000000000..0ee5fc83da
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/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.calendar.prototype.inleapyear
+description: A number cannot be used in place of a Temporal.PlainDate
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const numbers = [
+ 1,
+ 19761118,
+ -19761118,
+ 1234567890,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => instance.inLeapYear(arg),
+ 'Numbers cannot be used in place of an ISO string for PlainDate'
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-propertybag-calendar-case-insensitive.js
new file mode 100644
index 0000000000..5b4bf6c04f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-propertybag-calendar-case-insensitive.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.calendar.prototype.inleapyear
+description: The calendar name is case-insensitive
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const calendar = "IsO8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.inLeapYear(arg);
+assert.sameValue(result, true, "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-propertybag-calendar-leap-second.js
new file mode 100644
index 0000000000..d48face463
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-propertybag-calendar-leap-second.js
@@ -0,0 +1,23 @@
+// |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.calendar.prototype.inleapyear
+description: Leap second is a valid ISO string for a calendar in a property bag
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const calendar = "2016-12-31T23:59:60";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.inLeapYear(arg);
+assert.sameValue(
+ result,
+ true,
+ "leap second is a valid ISO string for calendar"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-propertybag-calendar-number.js
new file mode 100644
index 0000000000..32faae6439
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-propertybag-calendar-number.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.calendar.prototype.inleapyear
+description: A number as calendar in a property bag is not accepted
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const numbers = [
+ 1,
+ 19970327,
+ -19970327,
+ 1234567890,
+];
+
+for (const calendar of numbers) {
+ const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+ assert.throws(
+ TypeError,
+ () => instance.inLeapYear(arg),
+ "Numbers cannot be used as a calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-propertybag-calendar-string.js
new file mode 100644
index 0000000000..5d3a595827
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-propertybag-calendar-string.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.calendar.prototype.inleapyear
+description: A calendar ID is valid input for Calendar
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const calendar = "iso8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.inLeapYear(arg);
+assert.sameValue(result, true, `Calendar created from string "${calendar}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-propertybag-calendar-wrong-type.js
new file mode 100644
index 0000000000..fa0d34bb03
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-propertybag-calendar-wrong-type.js
@@ -0,0 +1,46 @@
+// |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.calendar.prototype.inleapyear
+description: >
+ Appropriate error thrown when a calendar property from a property bag cannot
+ be converted to a calendar object or string
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.Calendar("iso8601");
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [calendar, description] of primitiveTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ typeof calendar === 'string' ? RangeError : TypeError,
+ () => instance.inLeapYear(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+ [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields()
+];
+
+for (const [calendar, description] of typeErrorTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(TypeError, () => instance.inLeapYear(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/Calendar/prototype/inLeapYear/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-propertybag-calendar-year-zero.js
new file mode 100644
index 0000000000..0835539959
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-propertybag-calendar-year-zero.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.calendar.prototype.inleapyear
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T17:45",
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+01:00",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+const instance = new Temporal.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.inLeapYear(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-proto-in-calendar-fields.js
new file mode 100644
index 0000000000..c4b97957a5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-proto-in-calendar-fields.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.calendar.prototype.inleapyear
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.Calendar("iso8601");
+
+assert.throws(RangeError, () => instance.inLeapYear(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..bf2019e648
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-string-calendar-annotation.js
@@ -0,0 +1,33 @@
+// |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.calendar.prototype.inleapyear
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02[u-ca=iso8601]", "without time or time zone"],
+ ["2000-05-02[UTC][u-ca=iso8601]", "with time zone and no time"],
+ ["2000-05-02T15:23[u-ca=iso8601]", "without time zone"],
+ ["2000-05-02T15:23[UTC][u-ca=iso8601]", "with time zone"],
+ ["2000-05-02T15:23[!u-ca=iso8601]", "with ! and no time zone"],
+ ["2000-05-02T15:23[UTC][!u-ca=iso8601]", "with ! and time zone"],
+ ["2000-05-02T15:23[u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+const instance = new Temporal.Calendar("iso8601");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.inLeapYear(arg);
+
+ assert.sameValue(
+ result,
+ true,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..d7a4e32644
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-string-critical-unknown-annotation.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.calendar.prototype.inleapyear
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[!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.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.inLeapYear(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..4d68cef862
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-string-date-with-utc-offset.js
@@ -0,0 +1,48 @@
+// |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.calendar.prototype.inleapyear
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const validStrings = [
+ "2000-05-02T00+00:00",
+ "2000-05-02T00+00:00[UTC]",
+ "2000-05-02T00+00:00[!UTC]",
+ "2000-05-02T00-02:30[America/St_Johns]",
+];
+
+for (const arg of validStrings) {
+ const result = instance.inLeapYear(arg);
+
+ assert.sameValue(
+ result,
+ true,
+ `"${arg}" is a valid UTC offset with time for PlainDate`
+ );
+}
+
+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.inLeapYear(arg),
+ `"${arg}" UTC offset without time is not valid for PlainDate`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-string-invalid.js
new file mode 100644
index 0000000000..51f36ee0cb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-string-invalid.js
@@ -0,0 +1,64 @@
+// |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.calendar.prototype.inleapyear
+description: >
+ RangeError thrown if an invalid ISO string (or syntactically valid ISO string
+ that is not supported) is used as a PlainDate
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ // invalid ISO strings:
+ "",
+ "invalid iso8601",
+ "2020-01-00",
+ "2020-01-32",
+ "2020-02-30",
+ "2021-02-29",
+ "2020-00-01",
+ "2020-13-01",
+ "2020-01-01T",
+ "2020-01-01T25:00:00",
+ "2020-01-01T01:60:00",
+ "2020-01-01T01:60:61",
+ "2020-01-01junk",
+ "2020-01-01T00:00:00junk",
+ "2020-01-01T00:00:00+00:00junk",
+ "2020-01-01T00:00:00+00:00[UTC]junk",
+ "2020-01-01T00:00:00+00:00[UTC][u-ca=iso8601]junk",
+ "02020-01-01",
+ "2020-001-01",
+ "2020-01-001",
+ "2020-01-01T001",
+ "2020-01-01T01:001",
+ "2020-01-01T01:01:001",
+ // valid, but forms not supported in Temporal:
+ "2020-W01-1",
+ "2020-001",
+ "+0002020-01-01",
+ // valid, but this calendar must not exist:
+ "2020-01-01[u-ca=notexist]",
+ // may be valid in other contexts, but insufficient information for PlainDate:
+ "2020-01",
+ "+002020-01",
+ "01-01",
+ "2020-W01",
+ "P1Y",
+ "-P12Y",
+ // valid, but outside the supported range:
+ "-999999-01-01",
+ "+999999-01-01",
+];
+const instance = new Temporal.Calendar("iso8601");
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => instance.inLeapYear(arg),
+ `"${arg}" should not be a valid ISO string for a PlainDate`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..bb3468f3de
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/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.calendar.prototype.inleapyear
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[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.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.inLeapYear(arg),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..ae1f291355
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-string-multiple-time-zone.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.calendar.prototype.inleapyear
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[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.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.inLeapYear(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-string-time-separators.js
new file mode 100644
index 0000000000..be8ab2a244
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-string-time-separators.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.calendar.prototype.inleapyear
+description: Time separator in string argument can vary
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02T15:23", "uppercase T"],
+ ["2000-05-02t15:23", "lowercase T"],
+ ["2000-05-02 15:23", "space between date and time"],
+];
+
+const instance = new Temporal.Calendar("iso8601");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.inLeapYear(arg);
+
+ assert.sameValue(
+ result,
+ true,
+ `variant time separators (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..0c410681b1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-string-time-zone-annotation.js
@@ -0,0 +1,38 @@
+// |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.calendar.prototype.inleapyear
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02[Asia/Kolkata]", "named, with no time"],
+ ["2000-05-02[!Europe/Vienna]", "named, with ! and no time"],
+ ["2000-05-02[+00:00]", "numeric, with no time"],
+ ["2000-05-02[!-02:30]", "numeric, with ! and no time"],
+ ["2000-05-02T15:23[America/Sao_Paulo]", "named, with no offset"],
+ ["2000-05-02T15:23[!Asia/Tokyo]", "named, with ! and no offset"],
+ ["2000-05-02T15:23[-02:30]", "numeric, with no offset"],
+ ["2000-05-02T15:23[!+00:00]", "numeric, with ! and no offset"],
+ ["2000-05-02T15:23+00:00[America/New_York]", "named, with offset"],
+ ["2000-05-02T15:23+00:00[!UTC]", "named, with offset and !"],
+ ["2000-05-02T15:23+00:00[+01:00]", "numeric, with offset"],
+ ["2000-05-02T15:23+00:00[!-08:00]", "numeric, with offset and !"],
+];
+
+const instance = new Temporal.Calendar("iso8601");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.inLeapYear(arg);
+
+ assert.sameValue(
+ result,
+ true,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..6e73f60c4f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-string-unknown-annotation.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.calendar.prototype.inleapyear
+description: Various forms of unknown annotation
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02[foo=bar]", "without time"],
+ ["2000-05-02T15:23[foo=bar]", "alone"],
+ ["2000-05-02T15:23[UTC][foo=bar]", "with time zone"],
+ ["2000-05-02T15:23[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["2000-05-02T15:23[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["2000-05-02T15:23[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+const instance = new Temporal.Calendar("iso8601");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.inLeapYear(arg);
+
+ assert.sameValue(
+ result,
+ true,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..6a1b8e81ee
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-string-with-utc-designator.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.calendar.prototype.inleapyear
+description: RangeError thrown if a string with UTC designator is used as a PlainDate
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "2019-10-01T09:00:00Z",
+ "2019-10-01T09:00:00Z[UTC]",
+];
+const instance = new Temporal.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.inLeapYear(arg),
+ "String with UTC designator should not be valid as a PlainDate"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-string.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-string.js
new file mode 100644
index 0000000000..386d77fc36
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-string.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.inleapyear
+description: An ISO 8601 date string should be converted as input
+info: |
+ 4. If Type(temporalDateLike) is not Object or temporalDateLike does not have an [[InitializedTemporalDate]] or [[InitializedTemporalYearMonth]] internal slot, then
+ a. Set temporalDateLike to ? ToTemporalDate(temporalDateLike).
+ 5. Return ! IsISOLeapYear(temporalDateLike.[[ISOYear]]).
+features: [Temporal]
+---*/
+
+const cal = new Temporal.Calendar("iso8601");
+
+assert.sameValue(cal.inLeapYear("2019-03-18"), false);
+assert.sameValue(cal.inLeapYear("2020-03-18"), true);
+
+assert.sameValue(cal.inLeapYear("+002023-03-18"), false);
+assert.sameValue(cal.inLeapYear("+002024-03-18"), true);
+
+assert.sameValue(cal.inLeapYear("2019-03-18T13:00:00+00:00[UTC]"), false);
+assert.sameValue(cal.inLeapYear("2020-12-31T23:59:59+00:00[UTC]"), true);
+
+assert.sameValue(cal.inLeapYear("+002023-03-18T13:00:00+00:00[UTC]"), false);
+assert.sameValue(cal.inLeapYear("+002024-03-18T13:00:00+00:00[UTC]"), true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-wrong-type.js
new file mode 100644
index 0000000000..62c7f9da78
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/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.calendar.prototype.inleapyear
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainDate
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+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.inLeapYear(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.PlainDate, "Temporal.PlainDate, object"],
+ [Temporal.PlainDate.prototype, "Temporal.PlainDate.prototype, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.inLeapYear(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/Calendar/prototype/inLeapYear/argument-zoneddatetime-convert.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-zoneddatetime-convert.js
new file mode 100644
index 0000000000..0eaaa5648a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-zoneddatetime-convert.js
@@ -0,0 +1,22 @@
+// |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.calendar.prototype.inleapyear
+description: An exception from TimeZone#getOffsetNanosecondsFor() is propagated.
+features: [Temporal]
+---*/
+
+class TZ extends Temporal.TimeZone {
+ constructor() { super("UTC") }
+ getOffsetNanosecondsFor() { throw new Test262Error() }
+}
+
+const tz = new TZ();
+const arg = new Temporal.ZonedDateTime(0n, tz);
+const instance = new Temporal.Calendar("iso8601");
+
+assert.throws(Test262Error, () => instance.inLeapYear(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-zoneddatetime-slots.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-zoneddatetime-slots.js
new file mode 100644
index 0000000000..deb0f55d75
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-zoneddatetime-slots.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.calendar.prototype.inleapyear
+description: Getters are not called when converting a ZonedDateTime to a PlainDate.
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.ZonedDateTime.prototype);
+const getters = ["year", "month", "monthCode", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond", "calendar"];
+
+for (const property of getters) {
+ Object.defineProperty(Temporal.ZonedDateTime.prototype, property, {
+ get() {
+ actual.push(`get ${property}`);
+ const value = prototypeDescrs[property].get.call(this);
+ return {
+ toString() {
+ actual.push(`toString ${property}`);
+ return value.toString();
+ },
+ valueOf() {
+ actual.push(`valueOf ${property}`);
+ return value;
+ },
+ };
+ },
+ });
+}
+
+const arg = new Temporal.ZonedDateTime(0n, "UTC");
+const instance = new Temporal.Calendar("iso8601");
+instance.inLeapYear(arg);
+assert.compareArray(actual, []);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..855ce0b0a5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/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.calendar.prototype.inleapyear
+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 calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => calendar.inLeapYear(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..2133bdff72
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/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.calendar.prototype.inleapyear
+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 calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => calendar.inLeapYear(datetime),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..073dfc3f0f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/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.calendar.prototype.inleapyear
+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 calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => calendar.inLeapYear(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..8156a9a9d2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/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.calendar.prototype.inleapyear
+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 calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => calendar.inLeapYear(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/basic.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/basic.js
new file mode 100644
index 0000000000..0629f27bb4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/basic.js
@@ -0,0 +1,29 @@
+// |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.calendar.prototype.inleapyear
+description: Basic tests for inLeapYear().
+features: [Temporal]
+---*/
+
+const iso = Temporal.Calendar.from("iso8601");
+let res = false;
+
+assert.sameValue(iso.inLeapYear(new Temporal.PlainDate(1994, 11, 5)), res, "PlainDate");
+assert.sameValue(iso.inLeapYear(new Temporal.PlainDateTime(1994, 11, 5, 8, 15, 30)), res, "PlainDateTime");
+assert.sameValue(iso.inLeapYear(new Temporal.PlainYearMonth(1994, 11)), res, "PlainYearMonth");
+assert.sameValue(iso.inLeapYear({ year: 1994, month: 11, day: 5 }), res, "property bag");
+assert.sameValue(iso.inLeapYear("1994-11-05"), res, "string");
+
+res = true;
+assert.sameValue(iso.inLeapYear(new Temporal.PlainDate(1996, 7, 15)), res, "PlainDate in leap year");
+assert.sameValue(iso.inLeapYear(new Temporal.PlainDateTime(1996, 7, 15, 5, 30, 13)), res, "PlainDateTime in leap year");
+assert.sameValue(iso.inLeapYear(new Temporal.PlainYearMonth(1996, 7)), res, "PlainYearMonth in leap year");
+assert.sameValue(iso.inLeapYear({ year: 1996, month: 7, day: 15 }), res, "property bag in leap year");
+assert.sameValue(iso.inLeapYear("1996-07-15"), res, "string in leap year");
+
+assert.throws(TypeError, () => iso.inLeapYear({ year: 2000 }), "property bag with missing properties");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/branding.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/branding.js
new file mode 100644
index 0000000000..a093d181e8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/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.calendar.prototype.inleapyear
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const inLeapYear = Temporal.Calendar.prototype.inLeapYear;
+
+assert.sameValue(typeof inLeapYear, "function");
+
+const args = [new Temporal.PlainDate(2021, 3, 4)];
+
+assert.throws(TypeError, () => inLeapYear.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => inLeapYear.apply(null, args), "null");
+assert.throws(TypeError, () => inLeapYear.apply(true, args), "true");
+assert.throws(TypeError, () => inLeapYear.apply("", args), "empty string");
+assert.throws(TypeError, () => inLeapYear.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => inLeapYear.apply(1, args), "1");
+assert.throws(TypeError, () => inLeapYear.apply({}, args), "plain object");
+assert.throws(TypeError, () => inLeapYear.apply(Temporal.Calendar, args), "Temporal.Calendar");
+assert.throws(TypeError, () => inLeapYear.apply(Temporal.Calendar.prototype, args), "Temporal.Calendar.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/browser.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/builtin.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/builtin.js
new file mode 100644
index 0000000000..7af66d6a7b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/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.calendar.prototype.inleapyear
+description: >
+ Tests that Temporal.Calendar.prototype.inLeapYear
+ 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.Calendar.prototype.inLeapYear),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Calendar.prototype.inLeapYear),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Calendar.prototype.inLeapYear),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Calendar.prototype.inLeapYear.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/calendar-datefromfields-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/calendar-datefromfields-called-with-options-undefined.js
new file mode 100644
index 0000000000..d5296d2717
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/calendar-datefromfields-called-with-options-undefined.js
@@ -0,0 +1,18 @@
+// |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.calendar.prototype.inleapyear
+description: >
+ Calendar.dateFromFields method is called with undefined as the options value
+ when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
+calendar.inLeapYear({ year: 2000, month: 5, day: 3, calendar });
+assert.sameValue(calendar.dateFromFieldsCallCount, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/calendar-fields-iterable.js
new file mode 100644
index 0000000000..d04651f126
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/calendar-fields-iterable.js
@@ -0,0 +1,37 @@
+// |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.calendar.prototype.inleapyear
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.calendar.prototype.inleapyear step 4:
+ 4. Let _year_ be ? ISOYear(_dateOrDateTime_).
+ sec-temporal-isoyear step 1.a:
+ a. Set _dateOrDateTime_ to ? ToTemporalDate(_dateOrDateTime_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToListOfType(_fieldsArray_, « String »).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+calendar1.inLeapYear({ year: 2000, month: 5, day: 2, calendar: calendar2 });
+
+assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/calendar-temporal-object.js
new file mode 100644
index 0000000000..af40ed70f8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/calendar-temporal-object.js
@@ -0,0 +1,29 @@
+// |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.calendar.prototype.inleapyear
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.calendar.prototype.inleapyear step 4:
+ 4. Let _year_ be ? ISOYear(_dateOrDateTime_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const calendar = new Temporal.Calendar("iso8601");
+ calendar.inLeapYear({ year: 2000, month: 5, day: 2, calendar: temporalObject });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..3800fd60d0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/infinity-throws-rangeerror.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.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.calendar.prototype.inleapyear
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+const base = { year: 2000, month: 5, day: 2 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.inLeapYear({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.inLeapYear({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/length.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/length.js
new file mode 100644
index 0000000000..c14994d608
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/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.calendar.prototype.inleapyear
+description: Temporal.Calendar.prototype.inLeapYear.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.Calendar.prototype.inLeapYear, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/name.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/name.js
new file mode 100644
index 0000000000..0243320bd6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/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.calendar.prototype.inleapyear
+description: Temporal.Calendar.prototype.inLeapYear.name is "inLeapYear".
+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.Calendar.prototype.inLeapYear, "name", {
+ value: "inLeapYear",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/not-a-constructor.js
new file mode 100644
index 0000000000..5e1ce16eef
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/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.calendar.prototype.inleapyear
+description: >
+ Temporal.Calendar.prototype.inLeapYear 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.Calendar.prototype.inLeapYear();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Calendar.prototype.inLeapYear), false,
+ "isConstructor(Temporal.Calendar.prototype.inLeapYear)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/prop-desc.js
new file mode 100644
index 0000000000..43017e7fb9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/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.calendar.prototype.inleapyear
+description: The "inLeapYear" property of Temporal.Calendar.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Calendar.prototype.inLeapYear,
+ "function",
+ "`typeof Calendar.prototype.inLeapYear` is `function`"
+);
+
+verifyProperty(Temporal.Calendar.prototype, "inLeapYear", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/shell.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/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/Calendar/prototype/inLeapYear/year-zero.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/year-zero.js
new file mode 100644
index 0000000000..26f32ed16a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/inLeapYear/year-zero.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.calendar.prototype.inleapyear
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T00:45",
+ "-000000-10-31T00:45+01:00",
+ "-000000-10-31T00:45+00:00[UTC]",
+];
+const instance = new Temporal.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.inLeapYear(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/mergeFields/arguments-empty-object.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/mergeFields/arguments-empty-object.js
new file mode 100644
index 0000000000..a5d437c195
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/mergeFields/arguments-empty-object.js
@@ -0,0 +1,35 @@
+// |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.calendar.prototype.mergefields
+description: Either argument being an empty object should result in a copy of the other object
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const calendar = new Temporal.Calendar("iso8601");
+
+let calls = 0;
+const yearObserver = {
+ get year() {
+ calls++;
+ return 2021;
+ }
+};
+
+const result1 = calendar.mergeFields(yearObserver, {});
+assert.sameValue(calls, 1, "property copied");
+assert.compareArray(Object.keys(result1), ["year"]);
+assert.sameValue(result1.year, 2021);
+assert.sameValue(calls, 1, "result has a data property");
+
+calls = 0;
+const result2 = calendar.mergeFields({}, yearObserver);
+assert.sameValue(calls, 1, "property copied");
+assert.compareArray(Object.keys(result2), ["year"]);
+assert.sameValue(result2.year, 2021);
+assert.sameValue(calls, 1, "result has a data property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/mergeFields/arguments-not-object.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/mergeFields/arguments-not-object.js
new file mode 100644
index 0000000000..3355b631d9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/mergeFields/arguments-not-object.js
@@ -0,0 +1,43 @@
+// |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.calendar.prototype.mergefields
+description: Non-object arguments are converted with ToObject and merge their [[OwnPropertyKeys]] onto a new object
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const calendar = new Temporal.Calendar("iso8601");
+
+assert.throws(TypeError, () => calendar.mergeFields(undefined, {}));
+assert.throws(TypeError, () => calendar.mergeFields({}, undefined));
+
+assert.throws(TypeError, () => calendar.mergeFields(null, {}));
+assert.throws(TypeError, () => calendar.mergeFields({}, null));
+
+const boolResult = calendar.mergeFields(true, false);
+assert.compareArray(Object.keys(boolResult), [], "Boolean objects have no own property keys");
+assert.sameValue(Object.getPrototypeOf(boolResult), null, "null-prototype object returned");
+
+const numResult = calendar.mergeFields(3, 4);
+assert.compareArray(Object.keys(numResult), [], "Number objects have no own property keys");
+assert.sameValue(Object.getPrototypeOf(boolResult), null, "null-prototype object returned");
+
+const strResult = calendar.mergeFields("abc", "de");
+assert.compareArray(Object.keys(strResult), ["0", "1", "2"], "String objects have integer indices as own property keys");
+assert.sameValue(strResult["0"], "d");
+assert.sameValue(strResult["1"], "e");
+assert.sameValue(strResult["2"], "c");
+assert.sameValue(Object.getPrototypeOf(boolResult), null, "null-prototype object returned");
+
+const symResult = calendar.mergeFields(Symbol("foo"), Symbol("bar"));
+assert.compareArray(Object.keys(symResult), [], "Symbol objects have no own property keys");
+assert.sameValue(Object.getPrototypeOf(symResult), null, "null-prototype object returned");
+
+const bigintResult = calendar.mergeFields(3n, 4n);
+assert.compareArray(Object.keys(bigintResult), [], "BigInt objects have no own property keys");
+assert.sameValue(Object.getPrototypeOf(bigintResult), null, "null-prototype object returned");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/mergeFields/basic.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/mergeFields/basic.js
new file mode 100644
index 0000000000..2a9d6abacd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/mergeFields/basic.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// 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.calendar.prototype.mergefields
+description: >
+ Temporal.Calendar.prototype.mergeFields will merge own data properties on its
+ arguments
+info: |
+ 1. Let calendar be the this value.
+ 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
+ 3. Assert: calendar.[[Identifier]] is "iso8601".
+ 4. Set fields to ? ToObject(fields).
+ 5. Set additionalFields to ? ToObject(additionalFields).
+ 6. Return ? DefaultMergeFields(fields, additionalFields).
+features: [Temporal]
+includes: [deepEqual.js]
+---*/
+
+const cal = new Temporal.Calendar("iso8601");
+
+assert.deepEqual(
+ cal.mergeFields({ a: 1, b: 2 }, { c: 3, d: 4 }),
+ { a: 1, b: 2, c: 3, d: 4 },
+ "properties are merged"
+);
+
+assert.deepEqual(
+ cal.mergeFields({ a: 1, b: 2 }, { b: 3, c: 4 }),
+ { a: 1, b: 3, c: 4 },
+ "property in additionalFields should overwrite one in fields"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/mergeFields/branding.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/mergeFields/branding.js
new file mode 100644
index 0000000000..9fc1451238
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/mergeFields/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.calendar.prototype.mergefields
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const mergeFields = Temporal.Calendar.prototype.mergeFields;
+
+assert.sameValue(typeof mergeFields, "function");
+
+const args = [{}, {}];
+
+assert.throws(TypeError, () => mergeFields.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => mergeFields.apply(null, args), "null");
+assert.throws(TypeError, () => mergeFields.apply(true, args), "true");
+assert.throws(TypeError, () => mergeFields.apply("", args), "empty string");
+assert.throws(TypeError, () => mergeFields.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => mergeFields.apply(1, args), "1");
+assert.throws(TypeError, () => mergeFields.apply({}, args), "plain object");
+assert.throws(TypeError, () => mergeFields.apply(Temporal.Calendar, args), "Temporal.Calendar");
+assert.throws(TypeError, () => mergeFields.apply(Temporal.Calendar.prototype, args), "Temporal.Calendar.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/mergeFields/browser.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/mergeFields/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/mergeFields/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/mergeFields/builtin.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/mergeFields/builtin.js
new file mode 100644
index 0000000000..6f7cee257c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/mergeFields/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.calendar.prototype.mergefields
+description: >
+ Tests that Temporal.Calendar.prototype.mergeFields
+ 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.Calendar.prototype.mergeFields),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Calendar.prototype.mergeFields),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Calendar.prototype.mergeFields),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Calendar.prototype.mergeFields.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/mergeFields/iso8601-calendar-month-monthCode.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/mergeFields/iso8601-calendar-month-monthCode.js
new file mode 100644
index 0000000000..d223e63931
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/mergeFields/iso8601-calendar-month-monthCode.js
@@ -0,0 +1,102 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.mergefields
+description: >
+ The default mergeFields algorithm from the ISO 8601 calendar should correctly
+ merge the month and monthCode properties
+info: |
+ 1. Let calendar be the this value.
+ 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
+ 3. Assert: calendar.[[Identifier]] is "iso8601".
+ 4. Set fields to ? ToObject(fields).
+ 5. Set additionalFields to ? ToObject(additionalFields).
+ 6. Return ? DefaultMergeFields(fields, additionalFields).
+features: [Temporal]
+includes: [deepEqual.js]
+---*/
+
+const cal = new Temporal.Calendar("iso8601");
+
+assert.deepEqual(
+ cal.mergeFields({ a: 1, b: 2, month: 7 }, { b: 3, c: 4 }),
+ { a: 1, b: 3, c: 4, month: 7 },
+ "month is copied from fields"
+);
+assert.deepEqual(
+ cal.mergeFields({ a: 1, b: 2, monthCode: "M08" }, { b: 3, c: 4 }),
+ { a: 1, b: 3, c: 4, monthCode: "M08" },
+ "monthCode is copied from fields"
+);
+assert.deepEqual(
+ cal.mergeFields({ a: 1, b: 2, month: 7, monthCode: "M08" }, { b: 3, c: 4 }),
+ { a: 1, b: 3, c: 4, month: 7, monthCode: "M08" },
+ "both month and monthCode are copied from fields, no validation is performed"
+);
+
+assert.deepEqual(
+ cal.mergeFields({ a: 1, b: 2 }, { b: 3, c: 4, month: 5 }),
+ { a: 1, b: 3, c: 4, month: 5 },
+ "month is copied from additionalFields"
+);
+assert.deepEqual(
+ cal.mergeFields({ a: 1, b: 2 }, { b: 3, c: 4, monthCode: "M06" }),
+ { a: 1, b: 3, c: 4, monthCode: "M06" },
+ "monthCode is copied from additionalFields"
+);
+assert.deepEqual(
+ cal.mergeFields({ a: 1, b: 2 }, { b: 3, c: 4, month: 5, monthCode: "M06" }),
+ { a: 1, b: 3, c: 4, month: 5, monthCode: "M06" },
+ "both month and monthCode are copied from additionalFields, no validation is performed"
+);
+
+assert.deepEqual(
+ cal.mergeFields({ a: 1, b: 2, month: 7 }, { b: 3, c: 4, month: 5 }),
+ { a: 1, b: 3, c: 4, month: 5 },
+ "month from additionalFields overrides month from fields"
+);
+assert.deepEqual(
+ cal.mergeFields({ a: 1, b: 2, monthCode: "M07" }, { b: 3, c: 4, monthCode: "M05" }),
+ { a: 1, b: 3, c: 4, monthCode: "M05" },
+ "monthCode from additionalFields overrides monthCode from fields"
+);
+assert.deepEqual(
+ cal.mergeFields({ a: 1, b: 2, monthCode: "M07" }, { b: 3, c: 4, month: 6 }),
+ { a: 1, b: 3, c: 4, month: 6 },
+ "month's presence on additionalFields blocks monthCode from fields"
+);
+assert.deepEqual(
+ cal.mergeFields({ a: 1, b: 2, month: 7 }, { b: 3, c: 4, monthCode: "M06" }),
+ { a: 1, b: 3, c: 4, monthCode: "M06"},
+ "monthCode's presence on additionalFields blocks month from fields"
+);
+assert.deepEqual(
+ cal.mergeFields({ a: 1, b: 2, month: 7, monthCode: "M08" },{ b: 3, c: 4, month: 5 }),
+ { a: 1, b: 3, c: 4, month: 5 },
+ "month's presence on additionalFields blocks both month and monthCode from fields"
+);
+assert.deepEqual(
+ cal.mergeFields({ a: 1, b: 2, month: 7, monthCode: "M08" }, { b: 3, c: 4, monthCode: "M06" }),
+ { a: 1, b: 3, c: 4, monthCode: "M06" },
+ "monthCode's presence on additionalFields blocks both month and monthCode from fields"
+);
+
+assert.deepEqual(
+ cal.mergeFields({ a: 1, b: 2, month: 7 }, { b: 3, c: 4, month: 5, monthCode: "M06" }),
+ { a: 1, b: 3, c: 4, month: 5, monthCode: "M06" },
+ "both month and monthCode are copied from additionalFields even when fields has month"
+);
+assert.deepEqual(
+ cal.mergeFields({ a: 1, b: 2, monthCode: "M07" }, { b: 3, c: 4, month: 5, monthCode: "M06" }),
+ { a: 1, b: 3, c: 4, month: 5, monthCode: "M06" },
+ "both month and monthCode are copied from additionalFields even when fields has monthCode"
+);
+assert.deepEqual(
+ cal.mergeFields({ a: 1, b: 2, month: 7, monthCode: "M08" }, { b: 3, c: 4, month: 5, monthCode: "M06" }),
+ { a: 1, b: 3, c: 4, month: 5, monthCode: "M06" },
+ "both month and monthCode are copied from additionalFields even when fields has both month and monthCode"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/mergeFields/length.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/mergeFields/length.js
new file mode 100644
index 0000000000..fa2778be25
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/mergeFields/length.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.calendar.prototype.mergefields
+description: Temporal.Calendar.prototype.mergeFields.length is 2
+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.Calendar.prototype.mergeFields, "length", {
+ value: 2,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/mergeFields/name.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/mergeFields/name.js
new file mode 100644
index 0000000000..23c4ea861c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/mergeFields/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.calendar.prototype.mergefields
+description: Temporal.Calendar.prototype.mergeFields.name is "mergeFields".
+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.Calendar.prototype.mergeFields, "name", {
+ value: "mergeFields",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/mergeFields/non-string-properties.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/mergeFields/non-string-properties.js
new file mode 100644
index 0000000000..b78f828d4f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/mergeFields/non-string-properties.js
@@ -0,0 +1,58 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// 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.calendar.prototype.mergefields
+description: Both string and symbol keys from the arguments are merged
+info: |
+ 1. Let calendar be the this value.
+ 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
+ 3. Assert: calendar.[[Identifier]] is "iso8601".
+ 4. Set fields to ? ToObject(fields).
+ 5. Set additionalFields to ? ToObject(additionalFields).
+ 6. Return ? DefaultMergeFields(fields, additionalFields).
+features: [Temporal]
+---*/
+
+function assertEntriesEqual(actual, expectedEntries, message) {
+ const names = Object.getOwnPropertyNames(actual);
+ const symbols = Object.getOwnPropertySymbols(actual);
+ const actualKeys = names.concat(symbols);
+ assert.sameValue(
+ actualKeys.length,
+ expectedEntries.length,
+ `${message}: expected object to have ${expectedEntries.length} properties, not ${actualKeys.length}:`
+ );
+ for (var index = 0; index < actualKeys.length; index++) {
+ const actualKey = actualKeys[index];
+ const expectedKey = expectedEntries[index][0];
+ const expectedValue = expectedEntries[index][1];
+ assert.sameValue(actualKey, expectedKey, `${message}: key ${index}:`);
+ assert.sameValue(actual[actualKey], expectedValue, `${message}: value ${index}:`);
+ }
+}
+
+const cal = new Temporal.Calendar("iso8601");
+
+assertEntriesEqual(
+ cal.mergeFields({ 1: 2 }, { 3: 4 }),
+ [["1", 2], ["3", 4]],
+ "number keys are actually string keys and are merged as such"
+);
+assertEntriesEqual(
+ cal.mergeFields({ 1n: 2 }, { 2n: 4 }),
+ [["1", 2], ["2", 4]],
+ "bigint keys are actually string keys and are merged as such"
+);
+
+const foo = Symbol("foo");
+const bar = Symbol("bar");
+assertEntriesEqual(
+ cal.mergeFields({ [foo]: 1 }, { [bar]: 2 }),
+ [[foo, 1], [bar, 2]],
+ "symbol keys are also merged"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/mergeFields/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/mergeFields/not-a-constructor.js
new file mode 100644
index 0000000000..c53ddcbf32
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/mergeFields/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.calendar.prototype.mergefields
+description: >
+ Temporal.Calendar.prototype.mergeFields 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.Calendar.prototype.mergeFields();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Calendar.prototype.mergeFields), false,
+ "isConstructor(Temporal.Calendar.prototype.mergeFields)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/mergeFields/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/mergeFields/order-of-operations.js
new file mode 100644
index 0000000000..38cd82d5ea
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/mergeFields/order-of-operations.js
@@ -0,0 +1,50 @@
+// |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.calendar.prototype.mergefields
+description: Properties on objects passed to mergeFields() are accessed in the correct order
+features: [Temporal]
+includes: [compareArray.js, temporalHelpers.js]
+---*/
+
+const expected = [
+ // CopyDataProperties on fields
+ "ownKeys fields",
+ "getOwnPropertyDescriptor fields.year",
+ "get fields.year",
+ "getOwnPropertyDescriptor fields.month",
+ "get fields.month",
+ "getOwnPropertyDescriptor fields.day",
+ "get fields.day",
+ "getOwnPropertyDescriptor fields.extra",
+ "get fields.extra",
+ // CopyDataProperties on additionalFields
+ "ownKeys additionalFields",
+ "getOwnPropertyDescriptor additionalFields[3]",
+ "get additionalFields[3]",
+ "getOwnPropertyDescriptor additionalFields.monthCode",
+ "get additionalFields.monthCode",
+ "getOwnPropertyDescriptor additionalFields[Symbol('extra')]",
+ "get additionalFields[Symbol('extra')]",
+];
+const actual = [];
+
+const fields = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2022,
+ month: 10,
+ day: 17,
+ extra: "extra property",
+}, "fields");
+const additionalFields = TemporalHelpers.propertyBagObserver(actual, {
+ [Symbol("extra")]: "extra symbol property",
+ monthCode: "M10",
+ 3: "extra array index property",
+}, "additionalFields");
+
+const instance = new Temporal.Calendar("iso8601");
+instance.mergeFields(fields, additionalFields);
+assert.compareArray(actual, expected, "order of observable operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/mergeFields/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/mergeFields/prop-desc.js
new file mode 100644
index 0000000000..4c4f0e8d56
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/mergeFields/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.calendar.prototype.mergefields
+description: The "mergeFields" property of Temporal.Calendar.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Calendar.prototype.mergeFields,
+ "function",
+ "`typeof Calendar.prototype.mergeFields` is `function`"
+);
+
+verifyProperty(Temporal.Calendar.prototype, "mergeFields", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/mergeFields/shell.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/mergeFields/shell.js
new file mode 100644
index 0000000000..346758ebd5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/mergeFields/shell.js
@@ -0,0 +1,353 @@
+// GENERATED, DO NOT EDIT
+// file: deepEqual.js
+// Copyright 2019 Ron Buckton. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+description: >
+ Compare two values structurally
+defines: [assert.deepEqual]
+---*/
+
+assert.deepEqual = function(actual, expected, message) {
+ var format = assert.deepEqual.format;
+ assert(
+ assert.deepEqual._compare(actual, expected),
+ `Expected ${format(actual)} to be structurally equal to ${format(expected)}. ${(message || '')}`
+ );
+};
+
+assert.deepEqual.format = function(value, seen) {
+ switch (typeof value) {
+ case 'string':
+ return typeof JSON !== "undefined" ? JSON.stringify(value) : `"${value}"`;
+ case 'number':
+ case 'boolean':
+ case 'symbol':
+ case 'bigint':
+ return value.toString();
+ case 'undefined':
+ return 'undefined';
+ case 'function':
+ return `[Function${value.name ? `: ${value.name}` : ''}]`;
+ case 'object':
+ if (value === null) return 'null';
+ if (value instanceof Date) return `Date "${value.toISOString()}"`;
+ if (value instanceof RegExp) return value.toString();
+ if (!seen) {
+ seen = {
+ counter: 0,
+ map: new Map()
+ };
+ }
+
+ let usage = seen.map.get(value);
+ if (usage) {
+ usage.used = true;
+ return `[Ref: #${usage.id}]`;
+ }
+
+ usage = { id: ++seen.counter, used: false };
+ seen.map.set(value, usage);
+
+ if (typeof Set !== "undefined" && value instanceof Set) {
+ return `Set {${Array.from(value).map(value => assert.deepEqual.format(value, seen)).join(', ')}}${usage.used ? ` as #${usage.id}` : ''}`;
+ }
+ if (typeof Map !== "undefined" && value instanceof Map) {
+ return `Map {${Array.from(value).map(pair => `${assert.deepEqual.format(pair[0], seen)} => ${assert.deepEqual.format(pair[1], seen)}}`).join(', ')}}${usage.used ? ` as #${usage.id}` : ''}`;
+ }
+ if (Array.isArray ? Array.isArray(value) : value instanceof Array) {
+ return `[${value.map(value => assert.deepEqual.format(value, seen)).join(', ')}]${usage.used ? ` as #${usage.id}` : ''}`;
+ }
+ let tag = Symbol.toStringTag in value ? value[Symbol.toStringTag] : 'Object';
+ if (tag === 'Object' && Object.getPrototypeOf(value) === null) {
+ tag = '[Object: null prototype]';
+ }
+ return `${tag ? `${tag} ` : ''}{ ${Object.keys(value).map(key => `${key.toString()}: ${assert.deepEqual.format(value[key], seen)}`).join(', ')} }${usage.used ? ` as #${usage.id}` : ''}`;
+ default:
+ return typeof value;
+ }
+};
+
+assert.deepEqual._compare = (function () {
+ var EQUAL = 1;
+ var NOT_EQUAL = -1;
+ var UNKNOWN = 0;
+
+ function deepEqual(a, b) {
+ return compareEquality(a, b) === EQUAL;
+ }
+
+ function compareEquality(a, b, cache) {
+ return compareIf(a, b, isOptional, compareOptionality)
+ || compareIf(a, b, isPrimitiveEquatable, comparePrimitiveEquality)
+ || compareIf(a, b, isObjectEquatable, compareObjectEquality, cache)
+ || NOT_EQUAL;
+ }
+
+ function compareIf(a, b, test, compare, cache) {
+ return !test(a)
+ ? !test(b) ? UNKNOWN : NOT_EQUAL
+ : !test(b) ? NOT_EQUAL : cacheComparison(a, b, compare, cache);
+ }
+
+ function tryCompareStrictEquality(a, b) {
+ return a === b ? EQUAL : UNKNOWN;
+ }
+
+ function tryCompareTypeOfEquality(a, b) {
+ return typeof a !== typeof b ? NOT_EQUAL : UNKNOWN;
+ }
+
+ function tryCompareToStringTagEquality(a, b) {
+ var aTag = Symbol.toStringTag in a ? a[Symbol.toStringTag] : undefined;
+ var bTag = Symbol.toStringTag in b ? b[Symbol.toStringTag] : undefined;
+ return aTag !== bTag ? NOT_EQUAL : UNKNOWN;
+ }
+
+ function isOptional(value) {
+ return value === undefined
+ || value === null;
+ }
+
+ function compareOptionality(a, b) {
+ return tryCompareStrictEquality(a, b)
+ || NOT_EQUAL;
+ }
+
+ function isPrimitiveEquatable(value) {
+ switch (typeof value) {
+ case 'string':
+ case 'number':
+ case 'bigint':
+ case 'boolean':
+ case 'symbol':
+ return true;
+ default:
+ return isBoxed(value);
+ }
+ }
+
+ function comparePrimitiveEquality(a, b) {
+ if (isBoxed(a)) a = a.valueOf();
+ if (isBoxed(b)) b = b.valueOf();
+ return tryCompareStrictEquality(a, b)
+ || tryCompareTypeOfEquality(a, b)
+ || compareIf(a, b, isNaNEquatable, compareNaNEquality)
+ || NOT_EQUAL;
+ }
+
+ function isNaNEquatable(value) {
+ return typeof value === 'number';
+ }
+
+ function compareNaNEquality(a, b) {
+ return isNaN(a) && isNaN(b) ? EQUAL : NOT_EQUAL;
+ }
+
+ function isObjectEquatable(value) {
+ return typeof value === 'object';
+ }
+
+ function compareObjectEquality(a, b, cache) {
+ if (!cache) cache = new Map();
+ return getCache(cache, a, b)
+ || setCache(cache, a, b, EQUAL) // consider equal for now
+ || cacheComparison(a, b, tryCompareStrictEquality, cache)
+ || cacheComparison(a, b, tryCompareToStringTagEquality, cache)
+ || compareIf(a, b, isValueOfEquatable, compareValueOfEquality)
+ || compareIf(a, b, isToStringEquatable, compareToStringEquality)
+ || compareIf(a, b, isArrayLikeEquatable, compareArrayLikeEquality, cache)
+ || compareIf(a, b, isStructurallyEquatable, compareStructuralEquality, cache)
+ || compareIf(a, b, isIterableEquatable, compareIterableEquality, cache)
+ || cacheComparison(a, b, fail, cache);
+ }
+
+ function isBoxed(value) {
+ return value instanceof String
+ || value instanceof Number
+ || value instanceof Boolean
+ || typeof Symbol === 'function' && value instanceof Symbol
+ || typeof BigInt === 'function' && value instanceof BigInt;
+ }
+
+ function isValueOfEquatable(value) {
+ return value instanceof Date;
+ }
+
+ function compareValueOfEquality(a, b) {
+ return compareIf(a.valueOf(), b.valueOf(), isPrimitiveEquatable, comparePrimitiveEquality)
+ || NOT_EQUAL;
+ }
+
+ function isToStringEquatable(value) {
+ return value instanceof RegExp;
+ }
+
+ function compareToStringEquality(a, b) {
+ return compareIf(a.toString(), b.toString(), isPrimitiveEquatable, comparePrimitiveEquality)
+ || NOT_EQUAL;
+ }
+
+ function isArrayLikeEquatable(value) {
+ return (Array.isArray ? Array.isArray(value) : value instanceof Array)
+ || (typeof Uint8Array === 'function' && value instanceof Uint8Array)
+ || (typeof Uint8ClampedArray === 'function' && value instanceof Uint8ClampedArray)
+ || (typeof Uint16Array === 'function' && value instanceof Uint16Array)
+ || (typeof Uint32Array === 'function' && value instanceof Uint32Array)
+ || (typeof Int8Array === 'function' && value instanceof Int8Array)
+ || (typeof Int16Array === 'function' && value instanceof Int16Array)
+ || (typeof Int32Array === 'function' && value instanceof Int32Array)
+ || (typeof Float32Array === 'function' && value instanceof Float32Array)
+ || (typeof Float64Array === 'function' && value instanceof Float64Array)
+ || (typeof BigUint64Array === 'function' && value instanceof BigUint64Array)
+ || (typeof BigInt64Array === 'function' && value instanceof BigInt64Array);
+ }
+
+ function compareArrayLikeEquality(a, b, cache) {
+ if (a.length !== b.length) return NOT_EQUAL;
+ for (var i = 0; i < a.length; i++) {
+ if (compareEquality(a[i], b[i], cache) === NOT_EQUAL) {
+ return NOT_EQUAL;
+ }
+ }
+ return EQUAL;
+ }
+
+ function isStructurallyEquatable(value) {
+ return !(typeof Promise === 'function' && value instanceof Promise // only comparable by reference
+ || typeof WeakMap === 'function' && value instanceof WeakMap // only comparable by reference
+ || typeof WeakSet === 'function' && value instanceof WeakSet // only comparable by reference
+ || typeof Map === 'function' && value instanceof Map // comparable via @@iterator
+ || typeof Set === 'function' && value instanceof Set); // comparable via @@iterator
+ }
+
+ function compareStructuralEquality(a, b, cache) {
+ var aKeys = [];
+ for (var key in a) aKeys.push(key);
+
+ var bKeys = [];
+ for (var key in b) bKeys.push(key);
+
+ if (aKeys.length !== bKeys.length) {
+ return NOT_EQUAL;
+ }
+
+ aKeys.sort();
+ bKeys.sort();
+
+ for (var i = 0; i < aKeys.length; i++) {
+ var aKey = aKeys[i];
+ var bKey = bKeys[i];
+ if (compareEquality(aKey, bKey, cache) === NOT_EQUAL) {
+ return NOT_EQUAL;
+ }
+ if (compareEquality(a[aKey], b[bKey], cache) === NOT_EQUAL) {
+ return NOT_EQUAL;
+ }
+ }
+
+ return compareIf(a, b, isIterableEquatable, compareIterableEquality, cache)
+ || EQUAL;
+ }
+
+ function isIterableEquatable(value) {
+ return typeof Symbol === 'function'
+ && typeof value[Symbol.iterator] === 'function';
+ }
+
+ function compareIteratorEquality(a, b, cache) {
+ if (typeof Map === 'function' && a instanceof Map && b instanceof Map ||
+ typeof Set === 'function' && a instanceof Set && b instanceof Set) {
+ if (a.size !== b.size) return NOT_EQUAL; // exit early if we detect a difference in size
+ }
+
+ var ar, br;
+ while (true) {
+ ar = a.next();
+ br = b.next();
+ if (ar.done) {
+ if (br.done) return EQUAL;
+ if (b.return) b.return();
+ return NOT_EQUAL;
+ }
+ if (br.done) {
+ if (a.return) a.return();
+ return NOT_EQUAL;
+ }
+ if (compareEquality(ar.value, br.value, cache) === NOT_EQUAL) {
+ if (a.return) a.return();
+ if (b.return) b.return();
+ return NOT_EQUAL;
+ }
+ }
+ }
+
+ function compareIterableEquality(a, b, cache) {
+ return compareIteratorEquality(a[Symbol.iterator](), b[Symbol.iterator](), cache);
+ }
+
+ function cacheComparison(a, b, compare, cache) {
+ var result = compare(a, b, cache);
+ if (cache && (result === EQUAL || result === NOT_EQUAL)) {
+ setCache(cache, a, b, /** @type {EQUAL | NOT_EQUAL} */(result));
+ }
+ return result;
+ }
+
+ function fail() {
+ return NOT_EQUAL;
+ }
+
+ function setCache(cache, left, right, result) {
+ var otherCache;
+
+ otherCache = cache.get(left);
+ if (!otherCache) cache.set(left, otherCache = new Map());
+ otherCache.set(right, result);
+
+ otherCache = cache.get(right);
+ if (!otherCache) cache.set(right, otherCache = new Map());
+ otherCache.set(left, result);
+ }
+
+ function getCache(cache, left, right) {
+ var otherCache;
+ var result;
+
+ otherCache = cache.get(left);
+ result = otherCache && otherCache.get(right);
+ if (result) return result;
+
+ otherCache = cache.get(right);
+ result = otherCache && otherCache.get(left);
+ if (result) return result;
+
+ return UNKNOWN;
+ }
+
+ return deepEqual;
+})();
+
+// 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/Calendar/prototype/month/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..f3ad260dbc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-builtin-calendar-no-array-iteration.js
@@ -0,0 +1,24 @@
+// |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.calendar.prototype.month
+description: >
+ Calling the method with a property bag argument with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.Calendar("iso8601");
+const arg = { year: 2000, month: 5, day: 2, calendar: "iso8601" };
+instance.month(arg);
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-calendar-datefromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..0c45a60cc0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-calendar-datefromfields-called-with-null-prototype-fields.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.calendar.prototype.month
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.Calendar("iso8601");
+const arg = { year: 2000, month: 5, day: 2, calendar };
+instance.month(arg);
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..b42eb7eedb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-constructor-in-calendar-fields.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.calendar.prototype.month
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.Calendar("iso8601");
+
+assert.throws(RangeError, () => instance.month(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-duplicate-calendar-fields.js
new file mode 100644
index 0000000000..6c9458de2b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-duplicate-calendar-fields.js
@@ -0,0 +1,19 @@
+// |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.calendar.prototype.month
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['day'], ['month'], ['monthCode'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar };
+ const instance = new Temporal.Calendar("iso8601");
+
+ assert.throws(RangeError, () => instance.month(arg));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-leap-second.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-leap-second.js
new file mode 100644
index 0000000000..faaa158e03
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-leap-second.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.calendar.prototype.month
+description: Leap second is a valid ISO string for PlainDate
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+let arg = "2016-12-31T23:59:60";
+const result1 = instance.month(arg);
+assert.sameValue(
+ result1,
+ 12,
+ "leap second is a valid ISO string for PlainDate"
+);
+
+arg = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+const result2 = instance.month(arg);
+assert.sameValue(
+ result2,
+ 12,
+ "second: 60 is ignored in property bag for PlainDate"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-number.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-number.js
new file mode 100644
index 0000000000..e9ad09de4b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/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.calendar.prototype.month
+description: A number cannot be used in place of a Temporal.PlainDate
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const numbers = [
+ 1,
+ 19761118,
+ -19761118,
+ 1234567890,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => instance.month(arg),
+ 'Numbers cannot be used in place of an ISO string for PlainDate'
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-propertybag-calendar-case-insensitive.js
new file mode 100644
index 0000000000..0bd26a63b8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-propertybag-calendar-case-insensitive.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.calendar.prototype.month
+description: The calendar name is case-insensitive
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const calendar = "IsO8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.month(arg);
+assert.sameValue(result, 11, "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-propertybag-calendar-leap-second.js
new file mode 100644
index 0000000000..7af34e9cb0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-propertybag-calendar-leap-second.js
@@ -0,0 +1,23 @@
+// |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.calendar.prototype.month
+description: Leap second is a valid ISO string for a calendar in a property bag
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const calendar = "2016-12-31T23:59:60";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.month(arg);
+assert.sameValue(
+ result,
+ 11,
+ "leap second is a valid ISO string for calendar"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-propertybag-calendar-number.js
new file mode 100644
index 0000000000..dd8ce27a1b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-propertybag-calendar-number.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.calendar.prototype.month
+description: A number as calendar in a property bag is not accepted
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const numbers = [
+ 1,
+ 19970327,
+ -19970327,
+ 1234567890,
+];
+
+for (const calendar of numbers) {
+ const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+ assert.throws(
+ TypeError,
+ () => instance.month(arg),
+ "Numbers cannot be used as a calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-propertybag-calendar-string.js
new file mode 100644
index 0000000000..b7d15b225b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-propertybag-calendar-string.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.calendar.prototype.month
+description: A calendar ID is valid input for Calendar
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const calendar = "iso8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.month(arg);
+assert.sameValue(result, 11, `Calendar created from string "${calendar}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-propertybag-calendar-wrong-type.js
new file mode 100644
index 0000000000..173852f0b7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-propertybag-calendar-wrong-type.js
@@ -0,0 +1,46 @@
+// |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.calendar.prototype.month
+description: >
+ Appropriate error thrown when a calendar property from a property bag cannot
+ be converted to a calendar object or string
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.Calendar("iso8601");
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [calendar, description] of primitiveTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ typeof calendar === 'string' ? RangeError : TypeError,
+ () => instance.month(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+ [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields()
+];
+
+for (const [calendar, description] of typeErrorTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(TypeError, () => instance.month(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/Calendar/prototype/month/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-propertybag-calendar-year-zero.js
new file mode 100644
index 0000000000..81bd484995
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-propertybag-calendar-year-zero.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.calendar.prototype.month
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T17:45",
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+01:00",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+const instance = new Temporal.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.month(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-proto-in-calendar-fields.js
new file mode 100644
index 0000000000..f81d9391a4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-proto-in-calendar-fields.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.calendar.prototype.month
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.Calendar("iso8601");
+
+assert.throws(RangeError, () => instance.month(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..ac8502eab0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-string-calendar-annotation.js
@@ -0,0 +1,33 @@
+// |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.calendar.prototype.month
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02[u-ca=iso8601]", "without time or time zone"],
+ ["2000-05-02[UTC][u-ca=iso8601]", "with time zone and no time"],
+ ["2000-05-02T15:23[u-ca=iso8601]", "without time zone"],
+ ["2000-05-02T15:23[UTC][u-ca=iso8601]", "with time zone"],
+ ["2000-05-02T15:23[!u-ca=iso8601]", "with ! and no time zone"],
+ ["2000-05-02T15:23[UTC][!u-ca=iso8601]", "with ! and time zone"],
+ ["2000-05-02T15:23[u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+const instance = new Temporal.Calendar("iso8601");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.month(arg);
+
+ assert.sameValue(
+ result,
+ 5,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..fbd525323b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-string-critical-unknown-annotation.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.calendar.prototype.month
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[!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.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.month(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..460fbfdc95
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-string-date-with-utc-offset.js
@@ -0,0 +1,48 @@
+// |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.calendar.prototype.month
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const validStrings = [
+ "2000-05-02T00+00:00",
+ "2000-05-02T00+00:00[UTC]",
+ "2000-05-02T00+00:00[!UTC]",
+ "2000-05-02T00-02:30[America/St_Johns]",
+];
+
+for (const arg of validStrings) {
+ const result = instance.month(arg);
+
+ assert.sameValue(
+ result,
+ 5,
+ `"${arg}" is a valid UTC offset with time for PlainDate`
+ );
+}
+
+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.month(arg),
+ `"${arg}" UTC offset without time is not valid for PlainDate`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-string-invalid.js
new file mode 100644
index 0000000000..8ef222a838
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-string-invalid.js
@@ -0,0 +1,64 @@
+// |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.calendar.prototype.month
+description: >
+ RangeError thrown if an invalid ISO string (or syntactically valid ISO string
+ that is not supported) is used as a PlainDate
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ // invalid ISO strings:
+ "",
+ "invalid iso8601",
+ "2020-01-00",
+ "2020-01-32",
+ "2020-02-30",
+ "2021-02-29",
+ "2020-00-01",
+ "2020-13-01",
+ "2020-01-01T",
+ "2020-01-01T25:00:00",
+ "2020-01-01T01:60:00",
+ "2020-01-01T01:60:61",
+ "2020-01-01junk",
+ "2020-01-01T00:00:00junk",
+ "2020-01-01T00:00:00+00:00junk",
+ "2020-01-01T00:00:00+00:00[UTC]junk",
+ "2020-01-01T00:00:00+00:00[UTC][u-ca=iso8601]junk",
+ "02020-01-01",
+ "2020-001-01",
+ "2020-01-001",
+ "2020-01-01T001",
+ "2020-01-01T01:001",
+ "2020-01-01T01:01:001",
+ // valid, but forms not supported in Temporal:
+ "2020-W01-1",
+ "2020-001",
+ "+0002020-01-01",
+ // valid, but this calendar must not exist:
+ "2020-01-01[u-ca=notexist]",
+ // may be valid in other contexts, but insufficient information for PlainDate:
+ "2020-01",
+ "+002020-01",
+ "01-01",
+ "2020-W01",
+ "P1Y",
+ "-P12Y",
+ // valid, but outside the supported range:
+ "-999999-01-01",
+ "+999999-01-01",
+];
+const instance = new Temporal.Calendar("iso8601");
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => instance.month(arg),
+ `"${arg}" should not be a valid ISO string for a PlainDate`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..2318d545b5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/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.calendar.prototype.month
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[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.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.month(arg),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..abfed3580c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-string-multiple-time-zone.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.calendar.prototype.month
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[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.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.month(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-string-time-separators.js
new file mode 100644
index 0000000000..6166133d3d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-string-time-separators.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.calendar.prototype.month
+description: Time separator in string argument can vary
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02T15:23", "uppercase T"],
+ ["2000-05-02t15:23", "lowercase T"],
+ ["2000-05-02 15:23", "space between date and time"],
+];
+
+const instance = new Temporal.Calendar("iso8601");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.month(arg);
+
+ assert.sameValue(
+ result,
+ 5,
+ `variant time separators (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..d5d9b46388
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-string-time-zone-annotation.js
@@ -0,0 +1,38 @@
+// |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.calendar.prototype.month
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02[Asia/Kolkata]", "named, with no time"],
+ ["2000-05-02[!Europe/Vienna]", "named, with ! and no time"],
+ ["2000-05-02[+00:00]", "numeric, with no time"],
+ ["2000-05-02[!-02:30]", "numeric, with ! and no time"],
+ ["2000-05-02T15:23[America/Sao_Paulo]", "named, with no offset"],
+ ["2000-05-02T15:23[!Asia/Tokyo]", "named, with ! and no offset"],
+ ["2000-05-02T15:23[-02:30]", "numeric, with no offset"],
+ ["2000-05-02T15:23[!+00:00]", "numeric, with ! and no offset"],
+ ["2000-05-02T15:23+00:00[America/New_York]", "named, with offset"],
+ ["2000-05-02T15:23+00:00[!UTC]", "named, with offset and !"],
+ ["2000-05-02T15:23+00:00[+01:00]", "numeric, with offset"],
+ ["2000-05-02T15:23+00:00[!-08:00]", "numeric, with offset and !"],
+];
+
+const instance = new Temporal.Calendar("iso8601");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.month(arg);
+
+ assert.sameValue(
+ result,
+ 5,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..b0eb8ca2e2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-string-unknown-annotation.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.calendar.prototype.month
+description: Various forms of unknown annotation
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02[foo=bar]", "without time"],
+ ["2000-05-02T15:23[foo=bar]", "alone"],
+ ["2000-05-02T15:23[UTC][foo=bar]", "with time zone"],
+ ["2000-05-02T15:23[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["2000-05-02T15:23[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["2000-05-02T15:23[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+const instance = new Temporal.Calendar("iso8601");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.month(arg);
+
+ assert.sameValue(
+ result,
+ 5,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..6a85186669
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-string-with-utc-designator.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.calendar.prototype.month
+description: RangeError thrown if a string with UTC designator is used as a PlainDate
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "2019-10-01T09:00:00Z",
+ "2019-10-01T09:00:00Z[UTC]",
+];
+const instance = new Temporal.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.month(arg),
+ "String with UTC designator should not be valid as a PlainDate"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-wrong-type.js
new file mode 100644
index 0000000000..2edf45bb54
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/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.calendar.prototype.month
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainDate
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+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.month(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.PlainDate, "Temporal.PlainDate, object"],
+ [Temporal.PlainDate.prototype, "Temporal.PlainDate.prototype, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.month(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/Calendar/prototype/month/argument-zoneddatetime-convert.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-zoneddatetime-convert.js
new file mode 100644
index 0000000000..efa7901fbd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-zoneddatetime-convert.js
@@ -0,0 +1,22 @@
+// |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.calendar.prototype.month
+description: An exception from TimeZone#getOffsetNanosecondsFor() is propagated.
+features: [Temporal]
+---*/
+
+class TZ extends Temporal.TimeZone {
+ constructor() { super("UTC") }
+ getOffsetNanosecondsFor() { throw new Test262Error() }
+}
+
+const tz = new TZ();
+const arg = new Temporal.ZonedDateTime(0n, tz);
+const instance = new Temporal.Calendar("iso8601");
+
+assert.throws(Test262Error, () => instance.month(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-zoneddatetime-slots.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-zoneddatetime-slots.js
new file mode 100644
index 0000000000..083973936e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-zoneddatetime-slots.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.calendar.prototype.month
+description: Getters are not called when converting a ZonedDateTime to a PlainDate.
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.ZonedDateTime.prototype);
+const getters = ["year", "month", "monthCode", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond", "calendar"];
+
+for (const property of getters) {
+ Object.defineProperty(Temporal.ZonedDateTime.prototype, property, {
+ get() {
+ actual.push(`get ${property}`);
+ const value = prototypeDescrs[property].get.call(this);
+ return {
+ toString() {
+ actual.push(`toString ${property}`);
+ return value.toString();
+ },
+ valueOf() {
+ actual.push(`valueOf ${property}`);
+ return value;
+ },
+ };
+ },
+ });
+}
+
+const arg = new Temporal.ZonedDateTime(0n, "UTC");
+const instance = new Temporal.Calendar("iso8601");
+instance.month(arg);
+assert.compareArray(actual, []);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..a17f4788fa
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/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.calendar.prototype.month
+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 calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => calendar.month(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..ee000ce22d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/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.calendar.prototype.month
+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 calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => calendar.month(datetime),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..a8c8823177
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/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.calendar.prototype.month
+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 calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => calendar.month(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..7ed7627662
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/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.calendar.prototype.month
+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 calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => calendar.month(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/basic.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/basic.js
new file mode 100644
index 0000000000..737bcc99bb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/basic.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.calendar.prototype.month
+description: Basic tests for month().
+features: [Temporal]
+---*/
+
+const iso = Temporal.Calendar.from("iso8601");
+const res = 11;
+assert.sameValue(iso.month(Temporal.PlainDate.from("1994-11-05")), res, "PlainDate");
+assert.sameValue(iso.month(Temporal.PlainDateTime.from("1994-11-05T08:15:30")), res, "PlainDateTime");
+assert.sameValue(iso.month(Temporal.PlainYearMonth.from("1994-11")), res, "PlainYearMonth");
+assert.sameValue(iso.month({ year: 1994, month: 11, day: 5 }), res, "property bag");
+assert.sameValue(iso.month("1994-11-05"), res, "string");
+assert.throws(TypeError, () => iso.month({ year: 2000 }), "property bag with missing properties");
+assert.throws(TypeError, () => iso.month(Temporal.PlainMonthDay.from("11-05")), "PlainMonthDay");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/branding.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/branding.js
new file mode 100644
index 0000000000..505485af9f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/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.calendar.prototype.month
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const month = Temporal.Calendar.prototype.month;
+
+assert.sameValue(typeof month, "function");
+
+const args = [new Temporal.PlainDate(2000, 1, 1)];
+
+assert.throws(TypeError, () => month.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => month.apply(null, args), "null");
+assert.throws(TypeError, () => month.apply(true, args), "true");
+assert.throws(TypeError, () => month.apply("", args), "empty string");
+assert.throws(TypeError, () => month.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => month.apply(1, args), "1");
+assert.throws(TypeError, () => month.apply({}, args), "plain object");
+assert.throws(TypeError, () => month.apply(Temporal.Calendar, args), "Temporal.Calendar");
+assert.throws(TypeError, () => month.apply(Temporal.Calendar.prototype, args), "Temporal.Calendar.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/browser.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/builtin.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/builtin.js
new file mode 100644
index 0000000000..398501e3ad
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/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.calendar.prototype.month
+description: >
+ Tests that Temporal.Calendar.prototype.month
+ 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.Calendar.prototype.month),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Calendar.prototype.month),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Calendar.prototype.month),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Calendar.prototype.month.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/calendar-datefromfields-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/calendar-datefromfields-called-with-options-undefined.js
new file mode 100644
index 0000000000..b6ad45d5f2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/calendar-datefromfields-called-with-options-undefined.js
@@ -0,0 +1,18 @@
+// |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.calendar.prototype.month
+description: >
+ Calendar.dateFromFields method is called with undefined as the options value
+ when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
+calendar.month({ year: 2000, month: 5, day: 3, calendar });
+assert.sameValue(calendar.dateFromFieldsCallCount, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/calendar-fields-iterable.js
new file mode 100644
index 0000000000..d43a81b98d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/calendar-fields-iterable.js
@@ -0,0 +1,37 @@
+// |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.calendar.prototype.month
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.calendar.prototype.month step 4:
+ 4. Return ? ISOMonth(_dateOrDateTime_).
+ sec-temporal-isomonth step 1.a:
+ a. Set _dateOrDateTime_ to ? ToTemporalDate(_dateOrDateTime_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToListOfType(_fieldsArray_, « String »).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+calendar1.month({ year: 2000, month: 5, day: 2, calendar: calendar2 });
+
+assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/calendar-temporal-object.js
new file mode 100644
index 0000000000..47f9b6875e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/calendar-temporal-object.js
@@ -0,0 +1,31 @@
+// |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.calendar.prototype.month
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.calendar.prototype.month step 4:
+ 4. Return ? ISOMonth(_dateOrDateTime_).
+ sec-temporal-isomonth step 1.a:
+ a. Set _dateOrDateTime_ to ? ToTemporalDate(_dateOrDateTime_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const calendar = new Temporal.Calendar("iso8601");
+ calendar.month({ year: 2000, month: 5, day: 2, calendar: temporalObject });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/date-time.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/date-time.js
new file mode 100644
index 0000000000..a78719d1fc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/date-time.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.month
+description: >
+ Temporal.Calendar.prototype.month will take PlainDateTime and return
+ the value of the month.
+info: |
+ 5. If Type(temporalDateLike) is not Object or temporalDateLike does not have
+ an [[InitializedTemporalDate]] or [[InitializedTemporalYearMonth]]
+ internal slot, then
+ a. Set temporalDateLike to ? ToTemporalDate(temporalDateLike).
+ 6. Return ! ISOMonth(temporalDateLike).
+features: [Temporal]
+---*/
+let cal = new Temporal.Calendar("iso8601");
+
+let dateTime = new Temporal.PlainDateTime(1997, 8, 23, 5, 30, 13)
+assert.sameValue(cal.month(dateTime), 8, 'cal.month(new Temporal.PlainDateTime(1997, 8, 23, 5, 30, 13)) must return 8');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/date.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/date.js
new file mode 100644
index 0000000000..c5a2aa42f4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/date.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.month
+description: >
+ Temporal.Calendar.prototype.month will take PlainDate and return
+ the value of the month.
+info: |
+ 1. Let calendar be the this value.
+ 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
+ 3. Assert: calendar.[[Identifier]] is "iso8601".
+ 4. If Type(temporalDateLike) is Object and temporalDateLike has an
+ [[InitializedTemporalMonthDay]] internal slot, then
+ a. Throw a TypeError exception.
+ 5. If Type(temporalDateLike) is not Object or temporalDateLike does not have
+ an [[InitializedTemporalDate]] or [[InitializedTemporalYearMonth]]
+ internal slot, then
+ a. Set temporalDateLike to ? ToTemporalDate(temporalDateLike).
+ 6. Return ! ISOMonth(temporalDateLike).
+features: [Temporal]
+---*/
+let cal = new Temporal.Calendar("iso8601");
+
+let date = new Temporal.PlainDate(2021, 7, 15);
+assert.sameValue(cal.month(date), 7, 'cal.month(new Temporal.PlainDate(2021, 7, 15)) must return 7');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..c54d8d66c5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/infinity-throws-rangeerror.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.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.calendar.prototype.month
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+const base = { year: 2000, month: 5, day: 2 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.month({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.month({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/length.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/length.js
new file mode 100644
index 0000000000..29b41d9b57
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/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.calendar.prototype.month
+description: Temporal.Calendar.prototype.month.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.Calendar.prototype.month, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/month-day-throw-type-error.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/month-day-throw-type-error.js
new file mode 100644
index 0000000000..2d03d4aa6d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/month-day-throw-type-error.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.month
+description: >
+ Temporal.Calendar.prototype.month throws TypeError if temporalDateLike
+ is a PlainMonthDay object.
+ ToTemporalDate when temporalDateLike is invalid string.
+info: |
+ 4. If Type(temporalDateLike) is Object and temporalDateLike has an
+ [[InitializedTemporalMonthDay]] internal slot, then
+ a. Throw a TypeError exception.
+features: [Temporal, arrow-function]
+---*/
+let cal = new Temporal.Calendar("iso8601");
+
+let monthDay = new Temporal.PlainMonthDay(12, 25);
+assert.throws(TypeError, () => cal.month(monthDay),
+ 'cal.month(monthDay) throws a TypeError exception');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/name.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/name.js
new file mode 100644
index 0000000000..1a154f7878
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/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.calendar.prototype.month
+description: Temporal.Calendar.prototype.month.name is "month".
+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.Calendar.prototype.month, "name", {
+ value: "month",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/not-a-constructor.js
new file mode 100644
index 0000000000..daab6ff41d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/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.calendar.prototype.month
+description: >
+ Temporal.Calendar.prototype.month 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.Calendar.prototype.month();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Calendar.prototype.month), false,
+ "isConstructor(Temporal.Calendar.prototype.month)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/prop-desc.js
new file mode 100644
index 0000000000..3359b2fdaf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/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.calendar.prototype.month
+description: The "month" property of Temporal.Calendar.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Calendar.prototype.month,
+ "function",
+ "`typeof Calendar.prototype.month` is `function`"
+);
+
+verifyProperty(Temporal.Calendar.prototype, "month", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/shell.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/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/Calendar/prototype/month/string.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/string.js
new file mode 100644
index 0000000000..8b6e2f20c4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/string.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.month
+description: >
+ Temporal.Calendar.prototype.month will take ISO8601 string and return
+ the value of the month.
+info: |
+ 5. If Type(temporalDateLike) is not Object or temporalDateLike does not have
+ an [[InitializedTemporalDate]] or [[InitializedTemporalYearMonth]] internal
+ slot, then
+ a. Set temporalDateLike to ? ToTemporalDate(temporalDateLike).
+ 6. Return ! ISOMonth(temporalDateLike).
+features: [Temporal]
+---*/
+let cal = new Temporal.Calendar("iso8601");
+
+assert.sameValue(cal.month("2019-03-15"), 3, 'cal.month("2019-03-15") must return 3');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/throw-range-error-ToTemporalDate.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/throw-range-error-ToTemporalDate.js
new file mode 100644
index 0000000000..9b166de02b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/throw-range-error-ToTemporalDate.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.month
+description: >
+ Temporal.Calendar.prototype.month throws RangeError on
+ ToTemporalDate when temporalDateLike is invalid string.
+info: |
+ 5. If Type(temporalDateLike) is not Object or temporalDateLike
+ does not have an [[InitializedTemporalDate]] or
+ [[InitializedTemporalYearMonth]] internal slot, then
+ a. Set temporalDateLike to ? ToTemporalDate(temporalDateLike).
+features: [Temporal, arrow-function]
+---*/
+let cal = new Temporal.Calendar("iso8601");
+
+assert.throws(RangeError, () => cal.month("invalid string"),
+ 'cal.month("invalid string") throws a RangeError exception');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/year-month.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/year-month.js
new file mode 100644
index 0000000000..23c6dbc614
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/year-month.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.month
+description: >
+ Temporal.Calendar.prototype.month will take PlainYearMonth and return
+ the value of the month.
+info: |
+ 6. Return ! ISOMonth(temporalDateLike).
+features: [Temporal]
+---*/
+let cal = new Temporal.Calendar("iso8601");
+
+let yearMonth = new Temporal.PlainYearMonth(1999, 6);
+assert.sameValue(cal.month(yearMonth), 6, 'cal.month(new Temporal.PlainYearMonth(1999, 6)) must return 6');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/year-zero.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/year-zero.js
new file mode 100644
index 0000000000..8468e9c316
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/month/year-zero.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.calendar.prototype.month
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T00:45",
+ "-000000-10-31T00:45+01:00",
+ "-000000-10-31T00:45+00:00[UTC]",
+];
+const instance = new Temporal.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.month(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..8e5875e685
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-builtin-calendar-no-array-iteration.js
@@ -0,0 +1,24 @@
+// |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.calendar.prototype.monthcode
+description: >
+ Calling the method with a property bag argument with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.Calendar("iso8601");
+const arg = { year: 2000, month: 5, day: 2, calendar: "iso8601" };
+instance.monthCode(arg);
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-calendar-datefromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..1a62ba4e88
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-calendar-datefromfields-called-with-null-prototype-fields.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.calendar.prototype.monthcode
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.Calendar("iso8601");
+const arg = { year: 2000, month: 5, day: 2, calendar };
+instance.monthCode(arg);
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..d79f744214
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-constructor-in-calendar-fields.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.calendar.prototype.monthcode
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.Calendar("iso8601");
+
+assert.throws(RangeError, () => instance.monthCode(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-duplicate-calendar-fields.js
new file mode 100644
index 0000000000..a2693c6070
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-duplicate-calendar-fields.js
@@ -0,0 +1,19 @@
+// |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.calendar.prototype.monthcode
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['day'], ['month'], ['monthCode'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar };
+ const instance = new Temporal.Calendar("iso8601");
+
+ assert.throws(RangeError, () => instance.monthCode(arg));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-leap-second.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-leap-second.js
new file mode 100644
index 0000000000..7a6fd3166b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-leap-second.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.calendar.prototype.monthcode
+description: Leap second is a valid ISO string for PlainDate
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+let arg = "2016-12-31T23:59:60";
+const result1 = instance.monthCode(arg);
+assert.sameValue(
+ result1,
+ "M12",
+ "leap second is a valid ISO string for PlainDate"
+);
+
+arg = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+const result2 = instance.monthCode(arg);
+assert.sameValue(
+ result2,
+ "M12",
+ "second: 60 is ignored in property bag for PlainDate"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-number.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-number.js
new file mode 100644
index 0000000000..8d4f0a0bdf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/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.calendar.prototype.monthcode
+description: A number cannot be used in place of a Temporal.PlainDate
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const numbers = [
+ 1,
+ 19761118,
+ -19761118,
+ 1234567890,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => instance.monthCode(arg),
+ 'Numbers cannot be used in place of an ISO string for PlainDate'
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-propertybag-calendar-case-insensitive.js
new file mode 100644
index 0000000000..678a9771fe
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-propertybag-calendar-case-insensitive.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.calendar.prototype.monthcode
+description: The calendar name is case-insensitive
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const calendar = "IsO8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.monthCode(arg);
+assert.sameValue(result, "M11", "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-propertybag-calendar-leap-second.js
new file mode 100644
index 0000000000..d15cc3d900
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-propertybag-calendar-leap-second.js
@@ -0,0 +1,23 @@
+// |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.calendar.prototype.monthcode
+description: Leap second is a valid ISO string for a calendar in a property bag
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const calendar = "2016-12-31T23:59:60";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.monthCode(arg);
+assert.sameValue(
+ result,
+ "M11",
+ "leap second is a valid ISO string for calendar"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-propertybag-calendar-number.js
new file mode 100644
index 0000000000..a56e94238d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-propertybag-calendar-number.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.calendar.prototype.monthcode
+description: A number as calendar in a property bag is not accepted
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const numbers = [
+ 1,
+ 19970327,
+ -19970327,
+ 1234567890,
+];
+
+for (const calendar of numbers) {
+ const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+ assert.throws(
+ TypeError,
+ () => instance.monthCode(arg),
+ "Numbers cannot be used as a calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-propertybag-calendar-string.js
new file mode 100644
index 0000000000..272b8a6495
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-propertybag-calendar-string.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.calendar.prototype.monthcode
+description: A calendar ID is valid input for Calendar
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const calendar = "iso8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.monthCode(arg);
+assert.sameValue(result, "M11", `Calendar created from string "${calendar}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-propertybag-calendar-wrong-type.js
new file mode 100644
index 0000000000..457e61182b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-propertybag-calendar-wrong-type.js
@@ -0,0 +1,46 @@
+// |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.calendar.prototype.monthcode
+description: >
+ Appropriate error thrown when a calendar property from a property bag cannot
+ be converted to a calendar object or string
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.Calendar("iso8601");
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [calendar, description] of primitiveTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ typeof calendar === 'string' ? RangeError : TypeError,
+ () => instance.monthCode(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+ [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields()
+];
+
+for (const [calendar, description] of typeErrorTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(TypeError, () => instance.monthCode(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/Calendar/prototype/monthCode/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-propertybag-calendar-year-zero.js
new file mode 100644
index 0000000000..24178dfdb9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-propertybag-calendar-year-zero.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.calendar.prototype.monthcode
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T17:45",
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+01:00",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+const instance = new Temporal.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.monthCode(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-proto-in-calendar-fields.js
new file mode 100644
index 0000000000..ce95efc5da
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-proto-in-calendar-fields.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.calendar.prototype.monthcode
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.Calendar("iso8601");
+
+assert.throws(RangeError, () => instance.monthCode(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..6fdc89f24b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-string-calendar-annotation.js
@@ -0,0 +1,33 @@
+// |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.calendar.prototype.monthcode
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02[u-ca=iso8601]", "without time or time zone"],
+ ["2000-05-02[UTC][u-ca=iso8601]", "with time zone and no time"],
+ ["2000-05-02T15:23[u-ca=iso8601]", "without time zone"],
+ ["2000-05-02T15:23[UTC][u-ca=iso8601]", "with time zone"],
+ ["2000-05-02T15:23[!u-ca=iso8601]", "with ! and no time zone"],
+ ["2000-05-02T15:23[UTC][!u-ca=iso8601]", "with ! and time zone"],
+ ["2000-05-02T15:23[u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+const instance = new Temporal.Calendar("iso8601");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.monthCode(arg);
+
+ assert.sameValue(
+ result,
+ "M05",
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..eb87fb6809
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-string-critical-unknown-annotation.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.calendar.prototype.monthcode
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[!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.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.monthCode(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..1079461479
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-string-date-with-utc-offset.js
@@ -0,0 +1,48 @@
+// |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.calendar.prototype.monthcode
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const validStrings = [
+ "2000-05-02T00+00:00",
+ "2000-05-02T00+00:00[UTC]",
+ "2000-05-02T00+00:00[!UTC]",
+ "2000-05-02T00-02:30[America/St_Johns]",
+];
+
+for (const arg of validStrings) {
+ const result = instance.monthCode(arg);
+
+ assert.sameValue(
+ result,
+ "M05",
+ `"${arg}" is a valid UTC offset with time for PlainDate`
+ );
+}
+
+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.monthCode(arg),
+ `"${arg}" UTC offset without time is not valid for PlainDate`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-string-invalid.js
new file mode 100644
index 0000000000..06f1861f25
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-string-invalid.js
@@ -0,0 +1,64 @@
+// |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.calendar.prototype.monthcode
+description: >
+ RangeError thrown if an invalid ISO string (or syntactically valid ISO string
+ that is not supported) is used as a PlainDate
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ // invalid ISO strings:
+ "",
+ "invalid iso8601",
+ "2020-01-00",
+ "2020-01-32",
+ "2020-02-30",
+ "2021-02-29",
+ "2020-00-01",
+ "2020-13-01",
+ "2020-01-01T",
+ "2020-01-01T25:00:00",
+ "2020-01-01T01:60:00",
+ "2020-01-01T01:60:61",
+ "2020-01-01junk",
+ "2020-01-01T00:00:00junk",
+ "2020-01-01T00:00:00+00:00junk",
+ "2020-01-01T00:00:00+00:00[UTC]junk",
+ "2020-01-01T00:00:00+00:00[UTC][u-ca=iso8601]junk",
+ "02020-01-01",
+ "2020-001-01",
+ "2020-01-001",
+ "2020-01-01T001",
+ "2020-01-01T01:001",
+ "2020-01-01T01:01:001",
+ // valid, but forms not supported in Temporal:
+ "2020-W01-1",
+ "2020-001",
+ "+0002020-01-01",
+ // valid, but this calendar must not exist:
+ "2020-01-01[u-ca=notexist]",
+ // may be valid in other contexts, but insufficient information for PlainDate:
+ "2020-01",
+ "+002020-01",
+ "01-01",
+ "2020-W01",
+ "P1Y",
+ "-P12Y",
+ // valid, but outside the supported range:
+ "-999999-01-01",
+ "+999999-01-01",
+];
+const instance = new Temporal.Calendar("iso8601");
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => instance.monthCode(arg),
+ `"${arg}" should not be a valid ISO string for a PlainDate`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..57d58733e8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/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.calendar.prototype.monthcode
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[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.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.monthCode(arg),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..eee5dca1fe
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-string-multiple-time-zone.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.calendar.prototype.monthcode
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[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.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.monthCode(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-string-time-separators.js
new file mode 100644
index 0000000000..a51b45457f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-string-time-separators.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.calendar.prototype.monthcode
+description: Time separator in string argument can vary
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02T15:23", "uppercase T"],
+ ["2000-05-02t15:23", "lowercase T"],
+ ["2000-05-02 15:23", "space between date and time"],
+];
+
+const instance = new Temporal.Calendar("iso8601");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.monthCode(arg);
+
+ assert.sameValue(
+ result,
+ "M05",
+ `variant time separators (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..0aebca570e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-string-time-zone-annotation.js
@@ -0,0 +1,38 @@
+// |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.calendar.prototype.monthcode
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02[Asia/Kolkata]", "named, with no time"],
+ ["2000-05-02[!Europe/Vienna]", "named, with ! and no time"],
+ ["2000-05-02[+00:00]", "numeric, with no time"],
+ ["2000-05-02[!-02:30]", "numeric, with ! and no time"],
+ ["2000-05-02T15:23[America/Sao_Paulo]", "named, with no offset"],
+ ["2000-05-02T15:23[!Asia/Tokyo]", "named, with ! and no offset"],
+ ["2000-05-02T15:23[-02:30]", "numeric, with no offset"],
+ ["2000-05-02T15:23[!+00:00]", "numeric, with ! and no offset"],
+ ["2000-05-02T15:23+00:00[America/New_York]", "named, with offset"],
+ ["2000-05-02T15:23+00:00[!UTC]", "named, with offset and !"],
+ ["2000-05-02T15:23+00:00[+01:00]", "numeric, with offset"],
+ ["2000-05-02T15:23+00:00[!-08:00]", "numeric, with offset and !"],
+];
+
+const instance = new Temporal.Calendar("iso8601");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.monthCode(arg);
+
+ assert.sameValue(
+ result,
+ "M05",
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..c129776c7f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-string-unknown-annotation.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.calendar.prototype.monthcode
+description: Various forms of unknown annotation
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02[foo=bar]", "without time"],
+ ["2000-05-02T15:23[foo=bar]", "alone"],
+ ["2000-05-02T15:23[UTC][foo=bar]", "with time zone"],
+ ["2000-05-02T15:23[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["2000-05-02T15:23[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["2000-05-02T15:23[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+const instance = new Temporal.Calendar("iso8601");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.monthCode(arg);
+
+ assert.sameValue(
+ result,
+ "M05",
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..bbaf4e93a7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-string-with-utc-designator.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.calendar.prototype.monthcode
+description: RangeError thrown if a string with UTC designator is used as a PlainDate
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "2019-10-01T09:00:00Z",
+ "2019-10-01T09:00:00Z[UTC]",
+];
+const instance = new Temporal.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.monthCode(arg),
+ "String with UTC designator should not be valid as a PlainDate"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-wrong-type.js
new file mode 100644
index 0000000000..c48b97ca0f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/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.calendar.prototype.monthcode
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainDate
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+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.monthCode(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.PlainDate, "Temporal.PlainDate, object"],
+ [Temporal.PlainDate.prototype, "Temporal.PlainDate.prototype, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.monthCode(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/Calendar/prototype/monthCode/argument-zoneddatetime-convert.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-zoneddatetime-convert.js
new file mode 100644
index 0000000000..310546e282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-zoneddatetime-convert.js
@@ -0,0 +1,22 @@
+// |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.calendar.prototype.monthcode
+description: An exception from TimeZone#getOffsetNanosecondsFor() is propagated.
+features: [Temporal]
+---*/
+
+class TZ extends Temporal.TimeZone {
+ constructor() { super("UTC") }
+ getOffsetNanosecondsFor() { throw new Test262Error() }
+}
+
+const tz = new TZ();
+const arg = new Temporal.ZonedDateTime(0n, tz);
+const instance = new Temporal.Calendar("iso8601");
+
+assert.throws(Test262Error, () => instance.monthCode(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-zoneddatetime-slots.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-zoneddatetime-slots.js
new file mode 100644
index 0000000000..66651d80d8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-zoneddatetime-slots.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.calendar.prototype.monthcode
+description: Getters are not called when converting a ZonedDateTime to a PlainDate.
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.ZonedDateTime.prototype);
+const getters = ["year", "month", "monthCode", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond", "calendar"];
+
+for (const property of getters) {
+ Object.defineProperty(Temporal.ZonedDateTime.prototype, property, {
+ get() {
+ actual.push(`get ${property}`);
+ const value = prototypeDescrs[property].get.call(this);
+ return {
+ toString() {
+ actual.push(`toString ${property}`);
+ return value.toString();
+ },
+ valueOf() {
+ actual.push(`valueOf ${property}`);
+ return value;
+ },
+ };
+ },
+ });
+}
+
+const arg = new Temporal.ZonedDateTime(0n, "UTC");
+const instance = new Temporal.Calendar("iso8601");
+instance.monthCode(arg);
+assert.compareArray(actual, []);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..75cc814a12
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/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.calendar.prototype.monthcode
+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 calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => calendar.monthCode(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..52d16e579f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/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.calendar.prototype.monthcode
+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 calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => calendar.monthCode(datetime),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..c9a0bdf5a0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/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.calendar.prototype.monthcode
+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 calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => calendar.monthCode(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..c5f71da604
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/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.calendar.prototype.monthcode
+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 calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => calendar.monthCode(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/basic.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/basic.js
new file mode 100644
index 0000000000..6d841cf1f9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/basic.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.calendar.prototype.monthcode
+description: Basic tests for monthCode().
+features: [Temporal]
+---*/
+
+const iso = Temporal.Calendar.from("iso8601");
+const res = "M11";
+assert.sameValue(iso.monthCode(Temporal.PlainDate.from("1994-11-05")), res, "PlainDate");
+assert.sameValue(iso.monthCode(Temporal.PlainDateTime.from("1994-11-05T08:15:30")), res, "PlainDateTime");
+assert.sameValue(iso.monthCode(Temporal.PlainYearMonth.from("1994-11")), res, "PlainYearMonth");
+assert.sameValue(iso.monthCode(Temporal.PlainMonthDay.from("11-05")), res, "PlainMonthDay");
+assert.sameValue(iso.monthCode({ year: 1994, month: 11, day: 5 }), res, "property bag");
+assert.sameValue(iso.monthCode("1994-11-05"), res, "string");
+assert.throws(TypeError, () => iso.monthCode({ year: 2000 }), "property bag with missing properties");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/branding.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/branding.js
new file mode 100644
index 0000000000..cd47822052
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/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.calendar.prototype.monthcode
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const monthCode = Temporal.Calendar.prototype.monthCode;
+
+assert.sameValue(typeof monthCode, "function");
+
+const args = [new Temporal.PlainDate(2000, 1, 1)];
+
+assert.throws(TypeError, () => monthCode.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => monthCode.apply(null, args), "null");
+assert.throws(TypeError, () => monthCode.apply(true, args), "true");
+assert.throws(TypeError, () => monthCode.apply("", args), "empty string");
+assert.throws(TypeError, () => monthCode.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => monthCode.apply(1, args), "1");
+assert.throws(TypeError, () => monthCode.apply({}, args), "plain object");
+assert.throws(TypeError, () => monthCode.apply(Temporal.Calendar, args), "Temporal.Calendar");
+assert.throws(TypeError, () => monthCode.apply(Temporal.Calendar.prototype, args), "Temporal.Calendar.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/browser.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/builtin.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/builtin.js
new file mode 100644
index 0000000000..7ec3413f6d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/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.calendar.prototype.monthcode
+description: >
+ Tests that Temporal.Calendar.prototype.monthCode
+ 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.Calendar.prototype.monthCode),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Calendar.prototype.monthCode),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Calendar.prototype.monthCode),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Calendar.prototype.monthCode.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/calendar-datefromfields-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/calendar-datefromfields-called-with-options-undefined.js
new file mode 100644
index 0000000000..cb047b7afe
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/calendar-datefromfields-called-with-options-undefined.js
@@ -0,0 +1,18 @@
+// |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.calendar.prototype.monthcode
+description: >
+ Calendar.dateFromFields method is called with undefined as the options value
+ when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
+calendar.monthCode({ year: 2000, month: 5, day: 3, calendar });
+assert.sameValue(calendar.dateFromFieldsCallCount, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/calendar-fields-iterable.js
new file mode 100644
index 0000000000..848ff29298
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/calendar-fields-iterable.js
@@ -0,0 +1,37 @@
+// |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.calendar.prototype.monthcode
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.calendar.prototype.monthcode step 4:
+ 4. Return ? ISOMonthCode(_dateOrDateTime_).
+ sec-temporal-isomonthcode step 1.a:
+ a. Set _dateOrDateTime_ to ? ToTemporalDate(_dateOrDateTime_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToListOfType(_fieldsArray_, « String »).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+calendar1.monthCode({ year: 2000, month: 5, day: 2, calendar: calendar2 });
+
+assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/calendar-temporal-object.js
new file mode 100644
index 0000000000..097aede616
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/calendar-temporal-object.js
@@ -0,0 +1,31 @@
+// |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.calendar.prototype.monthcode
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.calendar.prototype.monthcode step 4:
+ 4. Return ? ISOMonthCode(_dateOrDateTime_).
+ sec-temporal-isomonthcode step 1.a:
+ a. Set _dateOrDateTime_ to ? ToTemporalDate(_dateOrDateTime_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const calendar = new Temporal.Calendar("iso8601");
+ calendar.monthCode({ year: 2000, month: 5, day: 2, calendar: temporalObject });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/date-time.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/date-time.js
new file mode 100644
index 0000000000..067d4187ad
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/date-time.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.monthCode
+description: >
+ Temporal.Calendar.prototype.month will take PlainDateTime and return
+ the value of the monthCode.
+info: |
+ 6. Return ! ISOMonthCode(temporalDateLike).
+features: [Temporal]
+---*/
+let cal = new Temporal.Calendar("iso8601");
+
+let dateTime = new Temporal.PlainDateTime(1997, 8, 23, 5, 30, 13)
+assert.sameValue(
+ cal.monthCode(dateTime),
+ "M08",
+ 'cal.monthCode(new Temporal.PlainDateTime(1997, 8, 23, 5, 30, 13)) must return "M08"'
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/date.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/date.js
new file mode 100644
index 0000000000..4493a17377
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/date.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.monthCode
+description: >
+ Temporal.Calendar.prototype.monthCode will take PlainDate and return
+ the value of the monthCode.
+info: |
+ 5. Return ! ISOMonthCode(temporalDateLike).
+features: [Temporal]
+---*/
+let cal = new Temporal.Calendar("iso8601");
+
+let date = new Temporal.PlainDate(2021, 7, 15);
+assert.sameValue(cal.monthCode(date), "M07", 'cal.monthCode(new Temporal.PlainDate(2021, 7, 15)) must return "M07"');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..838f979d19
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/infinity-throws-rangeerror.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.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.calendar.prototype.monthcode
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+const base = { year: 2000, month: 5, day: 2 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.monthCode({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.monthCode({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/length.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/length.js
new file mode 100644
index 0000000000..7556b38e55
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/length.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.calendar.prototype.monthcode
+description: Temporal.Calendar.prototype.monthCode.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.Calendar.prototype.monthCode, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/month-day.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/month-day.js
new file mode 100644
index 0000000000..23b0f65468
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/month-day.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.monthCode
+description: >
+ Temporal.Calendar.prototype.monthCode will take PlainMonthDay and return
+ the value of the monthCode.
+info: |
+ 6. Return ! ISOMonthCode(temporalDateLike).
+features: [Temporal]
+---*/
+let cal = new Temporal.Calendar("iso8601");
+
+let monthDay = new Temporal.PlainMonthDay(12, 25);
+assert.sameValue(
+ cal.monthCode(monthDay),
+ "M12",
+ 'cal.monthCode(new Temporal.PlainMonthDay(12, 25)) must return "M12"'
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/name.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/name.js
new file mode 100644
index 0000000000..4550c2cdc3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/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.calendar.prototype.monthcode
+description: Temporal.Calendar.prototype.monthCode.name is "monthCode".
+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.Calendar.prototype.monthCode, "name", {
+ value: "monthCode",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/not-a-constructor.js
new file mode 100644
index 0000000000..e79c22ca20
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/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.calendar.prototype.monthcode
+description: >
+ Temporal.Calendar.prototype.monthCode 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.Calendar.prototype.monthCode();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Calendar.prototype.monthCode), false,
+ "isConstructor(Temporal.Calendar.prototype.monthCode)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/prop-desc.js
new file mode 100644
index 0000000000..2e22953632
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/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.calendar.prototype.monthcode
+description: The "monthCode" property of Temporal.Calendar.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Calendar.prototype.monthCode,
+ "function",
+ "`typeof Calendar.prototype.monthCode` is `function`"
+);
+
+verifyProperty(Temporal.Calendar.prototype, "monthCode", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/shell.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/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/Calendar/prototype/monthCode/string.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/string.js
new file mode 100644
index 0000000000..1bd095b893
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/string.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.monthCode
+description: >
+ Temporal.Calendar.prototype.monthCode will take ISO8601 string and return
+ the value of the monthCode.
+info: |
+ 5. If Type(temporalDateLike) is not Object or temporalDateLike does not have
+ an [[InitializedTemporalDate]] or [[InitializedTemporalYearMonth]] internal
+ slot, then
+ a. Set temporalDateLike to ? ToTemporalDate(temporalDateLike).
+ 6. Return ! ISOYear(temporalDateLike).
+features: [Temporal]
+---*/
+let cal = new Temporal.Calendar("iso8601");
+assert.sameValue(
+ cal.monthCode("2019-03-15"),
+ "M03",
+ 'cal.monthCode("2019-03-15") must return "M03"'
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/throw-range-error-ToTemporalDate.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/throw-range-error-ToTemporalDate.js
new file mode 100644
index 0000000000..80ea2299f0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/throw-range-error-ToTemporalDate.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.monthCode
+description: >
+ Temporal.Calendar.prototype.monthCode throws RangeError on
+ ToTemporalDate when temporalDateLike is invalid string.
+info: |
+ 4. If Type(temporalDateLike) is not Object or temporalDateLike
+ does not have an [[InitializedTemporalDate]] or
+ [[InitializedTemporalYearMonth]] internal slot, then
+ a. Set temporalDateLike to ? ToTemporalDate(temporalDateLike).
+features: [Temporal, arrow-function]
+---*/
+let cal = new Temporal.Calendar("iso8601");
+
+assert.throws(RangeError, () => cal.monthCode("invalid string"),
+ 'cal.monthCode("invalid string") throws a RangeError exception');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/year-month.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/year-month.js
new file mode 100644
index 0000000000..c0b553658e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/year-month.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.monthCode
+description: >
+ Temporal.Calendar.prototype.monthCode will take PlainYearMonth and return
+ the value of the monthCode.
+info: |
+ 6. Return ! ISOMonthCode(temporalDateLike).
+features: [Temporal]
+---*/
+let cal = new Temporal.Calendar("iso8601");
+
+let yearMonth = new Temporal.PlainYearMonth(1999, 6);
+assert.sameValue(
+ cal.monthCode(yearMonth),
+ "M06",
+ 'cal.monthCode(new Temporal.PlainYearMonth(1999, 6)) must return "M06"'
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/year-zero.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/year-zero.js
new file mode 100644
index 0000000000..9a7df06b6c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthCode/year-zero.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.calendar.prototype.monthcode
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T00:45",
+ "-000000-10-31T00:45+01:00",
+ "-000000-10-31T00:45+00:00[UTC]",
+];
+const instance = new Temporal.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.monthCode(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/basic.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/basic.js
new file mode 100644
index 0000000000..09ca1c9b00
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/basic.js
@@ -0,0 +1,51 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.monthdayfromfields
+description: Temporal.Calendar.prototype.monthDayFromFields will return correctly with valid data.
+info: |
+ 1. Let calendar be the this value.
+ 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
+ 3. Assert: calendar.[[Identifier]] is "iso8601".
+ 4. If Type(fields) is not Object, throw a TypeError exception.
+ 5. Set options to ? GetOptionsObject(options).
+ 6. Set fields to ? PrepareTemporalFields(fields, « "day", "month", "monthCode", "year" », « "day" »).
+ 7. Let overflow be ? ToTemporalOverflow(options).
+ 8. Perform ? ISOResolveMonth(fields).
+ 9. Let result be ? ISOMonthDayFromFields(fields, overflow).
+ 10. Return ? CreateTemporalMonthDay(result.[[Month]], result.[[Day]], "iso8601", result.[[ReferenceISOYear]]).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const cal = new Temporal.Calendar("iso8601");
+
+const options = [
+ { overflow: "constrain" },
+ { overflow: "reject" },
+ {},
+ undefined,
+];
+options.forEach((opt) => {
+ const optionsDesc = opt && JSON.stringify(opt);
+ result = cal.monthDayFromFields({ year: 2021, month: 7, day: 3 }, opt);
+ TemporalHelpers.assertPlainMonthDay(result, "M07", 3, `month 7, day 3, with year, options = ${optionsDesc}`);
+ result = cal.monthDayFromFields({ year: 2021, month: 12, day: 31 }, opt);
+ TemporalHelpers.assertPlainMonthDay(result, "M12", 31, `month 12, day 31, with year, options = ${optionsDesc}`);
+ result = cal.monthDayFromFields({ monthCode: "M07", day: 3 }, opt);
+ TemporalHelpers.assertPlainMonthDay(result, "M07", 3, `monthCode M07, day 3, options = ${optionsDesc}`);
+ result = cal.monthDayFromFields({ monthCode: "M12", day: 31 }, opt);
+ TemporalHelpers.assertPlainMonthDay(result, "M12", 31, `monthCode M12, day 31, options = ${optionsDesc}`);
+});
+
+TemporalHelpers.ISOMonths.forEach(({ month, monthCode, daysInMonth }) => {
+ result = cal.monthDayFromFields({ month, day: daysInMonth });
+ TemporalHelpers.assertPlainMonthDay(result, monthCode, daysInMonth, `month ${month}, day ${daysInMonth}`);
+
+ result = cal.monthDayFromFields({ monthCode, day: daysInMonth });
+ TemporalHelpers.assertPlainMonthDay(result, monthCode, daysInMonth, `monthCode ${monthCode}, day ${daysInMonth}`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/branding.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/branding.js
new file mode 100644
index 0000000000..3fe68f7011
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/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.calendar.prototype.monthdayfromfields
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const monthDayFromFields = Temporal.Calendar.prototype.monthDayFromFields;
+
+assert.sameValue(typeof monthDayFromFields, "function");
+
+const args = [{ monthCode: "M01", day: 1 }];
+
+assert.throws(TypeError, () => monthDayFromFields.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => monthDayFromFields.apply(null, args), "null");
+assert.throws(TypeError, () => monthDayFromFields.apply(true, args), "true");
+assert.throws(TypeError, () => monthDayFromFields.apply("", args), "empty string");
+assert.throws(TypeError, () => monthDayFromFields.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => monthDayFromFields.apply(1, args), "1");
+assert.throws(TypeError, () => monthDayFromFields.apply({}, args), "plain object");
+assert.throws(TypeError, () => monthDayFromFields.apply(Temporal.Calendar, args), "Temporal.Calendar");
+assert.throws(TypeError, () => monthDayFromFields.apply(Temporal.Calendar.prototype, args), "Temporal.Calendar.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/browser.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/builtin.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/builtin.js
new file mode 100644
index 0000000000..0928552344
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/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.calendar.prototype.monthdayfromfields
+description: >
+ Tests that Temporal.Calendar.prototype.monthDayFromFields
+ 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.Calendar.prototype.monthDayFromFields),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Calendar.prototype.monthDayFromFields),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Calendar.prototype.monthDayFromFields),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Calendar.prototype.monthDayFromFields.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/fields-missing-properties.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/fields-missing-properties.js
new file mode 100644
index 0000000000..12ca8c5475
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/fields-missing-properties.js
@@ -0,0 +1,31 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.monthdayfromfields
+description: Temporal.Calendar.prototype.monthDayFromFields will throw TypeError with incorrect input data type.
+info: |
+ 1. Let calendar be the this value.
+ 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
+ 3. Assert: calendar.[[Identifier]] is "iso8601".
+ 4. If Type(fields) is not Object, throw a TypeError exception.
+ 5. Set options to ? GetOptionsObject(options).
+ 6. Set fields to ? PrepareTemporalFields(fields, « "day", "month", "monthCode", "year" », « "day" »).
+ 7. Let overflow be ? ToTemporalOverflow(options).
+ 8. Perform ? ISOResolveMonth(fields).
+ 9. Let result be ? ISOMonthDayFromFields(fields, overflow).
+ 10. Return ? CreateTemporalMonthDay(result.[[Month]], result.[[Day]], "iso8601", result.[[ReferenceISOYear]]).
+features: [Temporal]
+---*/
+
+let cal = new Temporal.Calendar("iso8601")
+
+assert.throws(TypeError, () => cal.monthDayFromFields({}), "at least one correctly spelled property is required");
+assert.throws(TypeError, () => cal.monthDayFromFields({ month: 12 }), "day is required with month");
+assert.throws(TypeError, () => cal.monthDayFromFields({ monthCode: "M12" }), "day is required with monthCode");
+assert.throws(TypeError, () => cal.monthDayFromFields({ year: 2021, month: 12 }), "day is required with year and month");
+assert.throws(TypeError, () => cal.monthDayFromFields({ year: 2021, monthCode: "M12" }), "day is required with year and monthCode");
+assert.throws(TypeError, () => cal.monthDayFromFields({ year: 2021, day: 17 }), "either month or monthCode is required");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/fields-not-object.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/fields-not-object.js
new file mode 100644
index 0000000000..77991bbcef
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/fields-not-object.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.calendar.prototype.monthdayfromfields
+description: Throw a TypeError if the fields is not an object
+features: [Symbol, Temporal]
+---*/
+
+const tests = [undefined, null, true, false, "string", Symbol("sym"), Math.PI, Infinity, NaN, 42n];
+const iso = Temporal.Calendar.from("iso8601");
+for (const fields of tests) {
+ assert.throws(TypeError, () => iso.monthDayFromFields(fields, {}));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..b07372e0a7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/infinity-throws-rangeerror.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.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.calendar.prototype.monthdayfromfields
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+const base = { year: 2000, month: 5, day: 2 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day"].forEach((prop) => {
+ ["constrain", "reject"].forEach((overflow) => {
+ assert.throws(RangeError, () => instance.monthDayFromFields({ ...base, [prop]: inf }, { overflow }), `${prop} property cannot be ${inf} (overflow ${overflow}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.monthDayFromFields({ ...base, [prop]: obj }, { overflow }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/length.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/length.js
new file mode 100644
index 0000000000..ab0bc26c02
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/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.calendar.prototype.monthdayfromfields
+description: Temporal.Calendar.prototype.monthDayFromFields.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.Calendar.prototype.monthDayFromFields, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/missing-properties.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/missing-properties.js
new file mode 100644
index 0000000000..85331cb3d5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/missing-properties.js
@@ -0,0 +1,39 @@
+// |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.calendar.prototype.monthdayfromfields
+description: Errors due to missing properties on fields object are thrown in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const missingDay = {
+ get year() {
+ TemporalHelpers.assertUnreachable("day should be checked first");
+ },
+ get month() {
+ TemporalHelpers.assertUnreachable("day should be checked first");
+ },
+ get monthCode() {
+ TemporalHelpers.assertUnreachable("day should be checked first");
+ },
+};
+assert.throws(TypeError, () => instance.monthDayFromFields(missingDay), "day should be checked before year and month");
+
+let got = [];
+const fieldsSpy = TemporalHelpers.propertyBagObserver(got, { day: 1 });
+assert.throws(TypeError, () => instance.monthDayFromFields(fieldsSpy), "incomplete fields should be rejected (but after reading all non-required fields)");
+assert.compareArray(got, [
+ "get day",
+ "get day.valueOf",
+ "call day.valueOf",
+ "get month",
+ "get monthCode",
+ "get year",
+], "fields should be read in alphabetical order");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/monthcode-invalid.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/monthcode-invalid.js
new file mode 100644
index 0000000000..04424ff5c4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/monthcode-invalid.js
@@ -0,0 +1,34 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.monthdayfromfields
+description: Throw RangeError for an out-of-range, conflicting, or ill-formed monthCode
+info: |
+ 1. Let calendar be the this value.
+ 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
+ 3. Assert: calendar.[[Identifier]] is "iso8601".
+ 4. If Type(fields) is not Object, throw a TypeError exception.
+ 5. Set options to ? GetOptionsObject(options).
+ 6. Let result be ? ISOMonthDayFromFields(fields, options).
+ 7. Return ? CreateTemporalMonthDay(result.[[Month]], result.[[Day]], calendar, result.[[ReferenceISOYear]]).
+features: [Temporal]
+---*/
+
+const cal = new Temporal.Calendar("iso8601");
+
+["m1", "M1", "m01"].forEach((monthCode) => {
+ assert.throws(RangeError, () => cal.monthDayFromFields({ monthCode, day: 17 }),
+ `monthCode '${monthCode}' is not well-formed`);
+});
+
+assert.throws(RangeError, () => cal.monthDayFromFields({ year: 2021, month: 12, monthCode: "M11", day: 17 }),
+ "monthCode and month conflict");
+
+["M00", "M19", "M99", "M13"].forEach((monthCode) => {
+ assert.throws(RangeError, () => cal.monthDayFromFields({ monthCode, day: 17 }),
+ `monthCode '${monthCode}' is not valid for ISO 8601 calendar`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/name.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/name.js
new file mode 100644
index 0000000000..116caa43cd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/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.calendar.prototype.monthdayfromfields
+description: Temporal.Calendar.prototype.monthDayFromFields.name is "monthDayFromFields".
+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.Calendar.prototype.monthDayFromFields, "name", {
+ value: "monthDayFromFields",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/not-a-constructor.js
new file mode 100644
index 0000000000..a9950ff3e3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/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.calendar.prototype.monthdayfromfields
+description: >
+ Temporal.Calendar.prototype.monthDayFromFields 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.Calendar.prototype.monthDayFromFields();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Calendar.prototype.monthDayFromFields), false,
+ "isConstructor(Temporal.Calendar.prototype.monthDayFromFields)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/one-of-era-erayear-undefined.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/one-of-era-erayear-undefined.js
new file mode 100644
index 0000000000..bc8f5b6f27
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/one-of-era-erayear-undefined.js
@@ -0,0 +1,19 @@
+// |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.calendar.prototype.monthDayFromFields
+description: Does not throw a RangeError if only one of era/eraYear fields is present
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const base = { year: 2000, month: 5, day: 2, era: 'ce' };
+const instance = new Temporal.Calendar('iso8601');
+TemporalHelpers.assertPlainMonthDay(instance.monthDayFromFields({ ...base }), 'M05', 2);
+
+const base2 = { year: 2000, month: 5, day: 2, eraYear: 1 };
+TemporalHelpers.assertPlainMonthDay(instance.monthDayFromFields({ ...base }), 'M05', 2);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/options-object.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/options-object.js
new file mode 100644
index 0000000000..277bec6757
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/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.calendar.prototype.monthdayfromfields
+description: Empty or a function object may be used as options
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const result1 = instance.monthDayFromFields({ monthCode: "M12", day: 15 }, {});
+TemporalHelpers.assertPlainMonthDay(
+ result1, "M12", 15,
+ "options may be an empty plain object"
+);
+
+const result2 = instance.monthDayFromFields({ monthCode: "M12", day: 15 }, () => {});
+TemporalHelpers.assertPlainMonthDay(
+ result2, "M12", 15,
+ "options may be a function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/options-wrong-type.js
new file mode 100644
index 0000000000..4ffb442486
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/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.calendar.prototype.monthdayfromfields
+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.Calendar("iso8601");
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.monthDayFromFields({ monthCode: "M12", day: 15 }, value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/order-of-operations.js
new file mode 100644
index 0000000000..e7e7458af8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/order-of-operations.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.calendar.prototype.monthdayfromfields
+description: Properties on objects passed to monthDayFromFields() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "get fields.day",
+ "get fields.day.valueOf",
+ "call fields.day.valueOf",
+ "get fields.month",
+ "get fields.month.valueOf",
+ "call fields.month.valueOf",
+ "get fields.monthCode",
+ "get fields.monthCode.toString",
+ "call fields.monthCode.toString",
+ "get fields.year",
+ "get fields.year.valueOf",
+ "call fields.year.valueOf",
+ "get options.overflow",
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+];
+const actual = [];
+
+const instance = new Temporal.Calendar("iso8601");
+
+const fields = TemporalHelpers.propertyBagObserver(actual, {
+ year: 1.7,
+ month: 1.7,
+ monthCode: "M01",
+ day: 1.7,
+}, "fields");
+
+const options = TemporalHelpers.propertyBagObserver(actual, {
+ overflow: "reject",
+}, "options");
+
+const result = instance.monthDayFromFields(fields, options);
+TemporalHelpers.assertPlainMonthDay(result, "M01", 1, "monthDay result");
+assert.sameValue(result.getISOFields().calendar, "iso8601", "calendar slot should store a string");
+assert.compareArray(actual, expected, "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/overflow-constrain.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/overflow-constrain.js
new file mode 100644
index 0000000000..072e99e3f7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/overflow-constrain.js
@@ -0,0 +1,68 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.monthdayfromfields
+description: Temporal.Calendar.prototype.monthDayFromFields will return correctly with data and overflow set to 'constrain'.
+info: |
+ 1. Let calendar be the this value.
+ 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
+ 3. Assert: calendar.[[Identifier]] is "iso8601".
+ 4. If Type(fields) is not Object, throw a TypeError exception.
+ 5. Set options to ? GetOptionsObject(options).
+ 6. Set fields to ? PrepareTemporalFields(fields, « "day", "month", "monthCode", "year" », « "day" »).
+ 7. Let overflow be ? ToTemporalOverflow(options).
+ 8. Perform ? ISOResolveMonth(fields).
+ 9. Let result be ? ISOMonthDayFromFields(fields, overflow).
+ 10. Return ? CreateTemporalMonthDay(result.[[Month]], result.[[Day]], "iso8601", result.[[ReferenceISOYear]]).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const cal = new Temporal.Calendar("iso8601");
+const opt = { overflow: "constrain" };
+
+let result = cal.monthDayFromFields({ year: 2021, month: 13, day: 1 }, opt);
+TemporalHelpers.assertPlainMonthDay(result, "M12", 1, "month 13 is constrained to 12");
+
+result = cal.monthDayFromFields({ year: 2021, month: 999999, day: 500 }, opt);
+TemporalHelpers.assertPlainMonthDay(result, "M12", 31, "month 999999 is constrained to 12 and day 500 is constrained to 31");
+
+[-99999, -1, 0].forEach((month) => {
+ assert.throws(
+ RangeError,
+ () => cal.monthDayFromFields({ year: 2021, month, day: 1 }, opt),
+ `Month ${month} is out of range for 2021 even with overflow: constrain`
+ );
+});
+
+TemporalHelpers.ISOMonths.forEach(({ month, monthCode, daysInMonth }) => {
+ const day = daysInMonth + 1;
+
+ result = cal.monthDayFromFields({ month, day }, opt);
+ TemporalHelpers.assertPlainMonthDay(result, monthCode, daysInMonth,
+ `day is constrained from ${day} to ${daysInMonth} in month ${month}`);
+
+ result = cal.monthDayFromFields({ month, day: 9001 }, opt);
+ TemporalHelpers.assertPlainMonthDay(result, monthCode, daysInMonth,
+ `day is constrained to ${daysInMonth} in month ${month}`);
+
+ result = cal.monthDayFromFields({ monthCode, day }, opt);
+ TemporalHelpers.assertPlainMonthDay(result, monthCode, daysInMonth,
+ `day is constrained from ${day} to ${daysInMonth} in monthCode ${monthCode}`);
+
+ result = cal.monthDayFromFields({ monthCode, day: 9001 }, opt);
+ TemporalHelpers.assertPlainMonthDay(result, monthCode, daysInMonth,
+ `day is constrained to ${daysInMonth} in monthCode ${monthCode}`);
+});
+
+[ ["month", 2], ["monthCode", "M02"] ].forEach(([ name, value ]) => {
+ result = cal.monthDayFromFields({ year: 2020, [name]: value, day: 30 }, opt);
+ TemporalHelpers.assertPlainMonthDay(result, "M02", 29, `${name} ${value} is constrained to 29 in leap year 2020`);
+
+ result = cal.monthDayFromFields({ year: 2021, [name]: value, day: 29 }, opt);
+ TemporalHelpers.assertPlainMonthDay(result, "M02", 28, `${name} ${value} is constrained to 28 in common year 2021`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/overflow-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/overflow-invalid-string.js
new file mode 100644
index 0000000000..b028e3fdfc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/overflow-invalid-string.js
@@ -0,0 +1,30 @@
+// |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.calendar.prototype.monthdayfromfields
+description: RangeError thrown when overflow option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-isomonthdayfromfields step 2:
+ 2. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.calendar.prototype.monthdayfromfields step 6:
+ 6. Let _result_ be ? ISOMonthDayFromFields(_fields_, _options_).
+features: [Temporal]
+---*/
+
+const calendar = new Temporal.Calendar("iso8601");
+const badOverflows = ["", "CONSTRAIN", "balance", "other string", "constra\u0131n", "reject\0"];
+for (const overflow of badOverflows) {
+ assert.throws(
+ RangeError,
+ () => calendar.monthDayFromFields({ year: 2000, month: 5, day: 2 }, { overflow }),
+ `invalid overflow ("${overflow}")`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/overflow-reject.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/overflow-reject.js
new file mode 100644
index 0000000000..fbc8f9351a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/overflow-reject.js
@@ -0,0 +1,65 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.monthdayfromfields
+description: Throw RangeError for input data out of range with overflow reject
+info: |
+ 1. Let calendar be the this value.
+ 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
+ 3. Assert: calendar.[[Identifier]] is "iso8601".
+ 4. If Type(fields) is not Object, throw a TypeError exception.
+ 5. Set options to ? GetOptionsObject(options).
+ 6. Set fields to ? PrepareTemporalFields(fields, « "day", "month", "monthCode", "year" », « "day" »).
+ 7. Let overflow be ? ToTemporalOverflow(options).
+ 8. Perform ? ISOResolveMonth(fields).
+ 9. Let result be ? ISOMonthDayFromFields(fields, overflow).
+ 10. Return ? CreateTemporalMonthDay(result.[[Month]], result.[[Day]], "iso8601", result.[[ReferenceISOYear]]).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const cal = new Temporal.Calendar("iso8601");
+
+[-1, 0, 13, 9995].forEach((month) => {
+ assert.throws(
+ RangeError,
+ () => cal.monthDayFromFields({year: 2021, month, day: 5}, { overflow: "reject" }),
+ `Month ${month} is out of range for 2021 with overflow: reject`
+ );
+});
+
+[-1, 0, 32, 999].forEach((day) => {
+ assert.throws(
+ RangeError,
+ () => cal.monthDayFromFields({ year: 2021, month: 12, day }, { overflow: "reject" }),
+ `Day ${day} is out of range for 2021-12 with overflow: reject`
+ );
+ assert.throws(
+ RangeError,
+ () => cal.monthDayFromFields({ monthCode: "M12", day }, { overflow: "reject" }),
+ `Day ${day} is out of range for 2021-M12 with overflow: reject`
+ );
+});
+
+TemporalHelpers.ISOMonths.forEach(({ month, monthCode, daysInMonth }) => {
+ const day = daysInMonth + 1;
+ assert.throws(RangeError,
+ () => cal.monthDayFromFields({ month, day }, { overflow: "reject" }),
+ `Day ${day} is out of range for month ${month} with overflow: reject`);
+ assert.throws(RangeError,
+ () => cal.monthDayFromFields({ monthCode, day }, { overflow: "reject" }),
+ `Day ${day} is out of range for monthCode ${monthCode} with overflow: reject`);
+});
+
+[ ["month", 2], ["monthCode", "M02"] ].forEach(([ name, value ]) => {
+ assert.throws(RangeError,
+ () => cal.monthDayFromFields({ year: 2020, [name]: value, day: 30 }, { overflow: "reject" }),
+ `Day 30 is out of range for ${name} ${value} in leap year 2020 with overflow: reject`);
+ assert.throws(RangeError,
+ () => cal.monthDayFromFields({ year: 2021, [name]: value, day: 29 }, { overflow: "reject" }),
+ `Day 29 is out of range for ${name} ${value} in common year 2021 with overflow: reject`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/overflow-undefined.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/overflow-undefined.js
new file mode 100644
index 0000000000..a44b81623e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/overflow-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.calendar.prototype.monthdayfromfields
+description: Fallback value for overflow option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-isomonthdayfromfields step 2:
+ 2. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.calendar.prototype.monthdayfromfields step 6:
+ 6. Let _result_ be ? ISOMonthDayFromFields(_fields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = new Temporal.Calendar("iso8601");
+
+const explicit = calendar.monthDayFromFields({ year: 2000, month: 15, day: 2 }, { overflow: undefined });
+TemporalHelpers.assertPlainMonthDay(explicit, "M12", 2, "default overflow is constrain");
+const implicit = calendar.monthDayFromFields({ year: 2000, month: 15, day: 2 }, {});
+TemporalHelpers.assertPlainMonthDay(implicit, "M12", 2, "default overflow is constrain");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/overflow-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/overflow-wrong-type.js
new file mode 100644
index 0000000000..bd1622c3c8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/overflow-wrong-type.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.calendar.prototype.monthdayfromfields
+description: Type conversions for overflow option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-isomonthdayfromfields step 2:
+ 2. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.calendar.prototype.monthdayfromfields step 6:
+ 6. Let _result_ be ? ISOMonthDayFromFields(_fields_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = new Temporal.Calendar("iso8601");
+TemporalHelpers.checkStringOptionWrongType("overflow", "constrain",
+ (overflow) => calendar.monthDayFromFields({ year: 2000, month: 5, day: 2 }, { overflow }),
+ (result, descr) => TemporalHelpers.assertPlainMonthDay(result, "M05", 2, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/prop-desc.js
new file mode 100644
index 0000000000..f19dfbb6b5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/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.calendar.prototype.monthdayfromfields
+description: The "monthDayFromFields" property of Temporal.Calendar.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Calendar.prototype.monthDayFromFields,
+ "function",
+ "`typeof Calendar.prototype.monthDayFromFields` is `function`"
+);
+
+verifyProperty(Temporal.Calendar.prototype, "monthDayFromFields", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/shell.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthDayFromFields/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/Calendar/prototype/monthsInYear/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..35c86dbff2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-builtin-calendar-no-array-iteration.js
@@ -0,0 +1,24 @@
+// |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.calendar.prototype.monthsinyear
+description: >
+ Calling the method with a property bag argument with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.Calendar("iso8601");
+const arg = { year: 2000, month: 5, day: 2, calendar: "iso8601" };
+instance.monthsInYear(arg);
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-calendar-datefromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..6f6e691a59
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-calendar-datefromfields-called-with-null-prototype-fields.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.calendar.prototype.monthsinyear
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.Calendar("iso8601");
+const arg = { year: 2000, month: 5, day: 2, calendar };
+instance.monthsInYear(arg);
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..2fca9d4da2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-constructor-in-calendar-fields.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.calendar.prototype.monthsinyear
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.Calendar("iso8601");
+
+assert.throws(RangeError, () => instance.monthsInYear(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-duplicate-calendar-fields.js
new file mode 100644
index 0000000000..f6da9031af
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-duplicate-calendar-fields.js
@@ -0,0 +1,19 @@
+// |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.calendar.prototype.monthsinyear
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['day'], ['month'], ['monthCode'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar };
+ const instance = new Temporal.Calendar("iso8601");
+
+ assert.throws(RangeError, () => instance.monthsInYear(arg));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-leap-second.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-leap-second.js
new file mode 100644
index 0000000000..ff7496e9b1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-leap-second.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.calendar.prototype.monthsinyear
+description: Leap second is a valid ISO string for PlainDate
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+let arg = "2016-12-31T23:59:60";
+const result1 = instance.monthsInYear(arg);
+assert.sameValue(
+ result1,
+ 12,
+ "leap second is a valid ISO string for PlainDate"
+);
+
+arg = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+const result2 = instance.monthsInYear(arg);
+assert.sameValue(
+ result2,
+ 12,
+ "second: 60 is ignored in property bag for PlainDate"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-number.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-number.js
new file mode 100644
index 0000000000..2a99f73f8b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/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.calendar.prototype.monthsinyear
+description: A number cannot be used in place of a Temporal.PlainDate
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const numbers = [
+ 1,
+ 19761118,
+ -19761118,
+ 1234567890,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => instance.monthsInYear(arg),
+ 'Numbers cannot be used in place of an ISO string for PlainDate'
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-propertybag-calendar-case-insensitive.js
new file mode 100644
index 0000000000..b8ee4e933b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-propertybag-calendar-case-insensitive.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.calendar.prototype.monthsinyear
+description: The calendar name is case-insensitive
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const calendar = "IsO8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.monthsInYear(arg);
+assert.sameValue(result, 12, "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-propertybag-calendar-leap-second.js
new file mode 100644
index 0000000000..0d6c0d6ef4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-propertybag-calendar-leap-second.js
@@ -0,0 +1,23 @@
+// |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.calendar.prototype.monthsinyear
+description: Leap second is a valid ISO string for a calendar in a property bag
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const calendar = "2016-12-31T23:59:60";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.monthsInYear(arg);
+assert.sameValue(
+ result,
+ 12,
+ "leap second is a valid ISO string for calendar"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-propertybag-calendar-number.js
new file mode 100644
index 0000000000..a0f70626e5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-propertybag-calendar-number.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.calendar.prototype.monthsinyear
+description: A number as calendar in a property bag is not accepted
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const numbers = [
+ 1,
+ 19970327,
+ -19970327,
+ 1234567890,
+];
+
+for (const calendar of numbers) {
+ const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+ assert.throws(
+ TypeError,
+ () => instance.monthsInYear(arg),
+ "Numbers cannot be used as a calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-propertybag-calendar-string.js
new file mode 100644
index 0000000000..460a47804c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-propertybag-calendar-string.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.calendar.prototype.monthsinyear
+description: A calendar ID is valid input for Calendar
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const calendar = "iso8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.monthsInYear(arg);
+assert.sameValue(result, 12, `Calendar created from string "${calendar}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-propertybag-calendar-wrong-type.js
new file mode 100644
index 0000000000..2b01fe1a0e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-propertybag-calendar-wrong-type.js
@@ -0,0 +1,46 @@
+// |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.calendar.prototype.monthsinyear
+description: >
+ Appropriate error thrown when a calendar property from a property bag cannot
+ be converted to a calendar object or string
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.Calendar("iso8601");
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [calendar, description] of primitiveTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ typeof calendar === 'string' ? RangeError : TypeError,
+ () => instance.monthsInYear(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+ [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields()
+];
+
+for (const [calendar, description] of typeErrorTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(TypeError, () => instance.monthsInYear(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/Calendar/prototype/monthsInYear/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-propertybag-calendar-year-zero.js
new file mode 100644
index 0000000000..c05540c245
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-propertybag-calendar-year-zero.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.calendar.prototype.monthsinyear
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T17:45",
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+01:00",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+const instance = new Temporal.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.monthsInYear(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-proto-in-calendar-fields.js
new file mode 100644
index 0000000000..0ee2a148c3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-proto-in-calendar-fields.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.calendar.prototype.monthsinyear
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.Calendar("iso8601");
+
+assert.throws(RangeError, () => instance.monthsInYear(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..3612b56cf3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-string-calendar-annotation.js
@@ -0,0 +1,33 @@
+// |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.calendar.prototype.monthsinyear
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02[u-ca=iso8601]", "without time or time zone"],
+ ["2000-05-02[UTC][u-ca=iso8601]", "with time zone and no time"],
+ ["2000-05-02T15:23[u-ca=iso8601]", "without time zone"],
+ ["2000-05-02T15:23[UTC][u-ca=iso8601]", "with time zone"],
+ ["2000-05-02T15:23[!u-ca=iso8601]", "with ! and no time zone"],
+ ["2000-05-02T15:23[UTC][!u-ca=iso8601]", "with ! and time zone"],
+ ["2000-05-02T15:23[u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+const instance = new Temporal.Calendar("iso8601");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.monthsInYear(arg);
+
+ assert.sameValue(
+ result,
+ 12,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..578dc6f78a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-string-critical-unknown-annotation.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.calendar.prototype.monthsinyear
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[!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.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.monthsInYear(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..5b5536623f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-string-date-with-utc-offset.js
@@ -0,0 +1,48 @@
+// |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.calendar.prototype.monthsinyear
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const validStrings = [
+ "2000-05-02T00+00:00",
+ "2000-05-02T00+00:00[UTC]",
+ "2000-05-02T00+00:00[!UTC]",
+ "2000-05-02T00-02:30[America/St_Johns]",
+];
+
+for (const arg of validStrings) {
+ const result = instance.monthsInYear(arg);
+
+ assert.sameValue(
+ result,
+ 12,
+ `"${arg}" is a valid UTC offset with time for PlainDate`
+ );
+}
+
+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.monthsInYear(arg),
+ `"${arg}" UTC offset without time is not valid for PlainDate`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-string-invalid.js
new file mode 100644
index 0000000000..fc2949e0c5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-string-invalid.js
@@ -0,0 +1,64 @@
+// |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.calendar.prototype.monthsinyear
+description: >
+ RangeError thrown if an invalid ISO string (or syntactically valid ISO string
+ that is not supported) is used as a PlainDate
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ // invalid ISO strings:
+ "",
+ "invalid iso8601",
+ "2020-01-00",
+ "2020-01-32",
+ "2020-02-30",
+ "2021-02-29",
+ "2020-00-01",
+ "2020-13-01",
+ "2020-01-01T",
+ "2020-01-01T25:00:00",
+ "2020-01-01T01:60:00",
+ "2020-01-01T01:60:61",
+ "2020-01-01junk",
+ "2020-01-01T00:00:00junk",
+ "2020-01-01T00:00:00+00:00junk",
+ "2020-01-01T00:00:00+00:00[UTC]junk",
+ "2020-01-01T00:00:00+00:00[UTC][u-ca=iso8601]junk",
+ "02020-01-01",
+ "2020-001-01",
+ "2020-01-001",
+ "2020-01-01T001",
+ "2020-01-01T01:001",
+ "2020-01-01T01:01:001",
+ // valid, but forms not supported in Temporal:
+ "2020-W01-1",
+ "2020-001",
+ "+0002020-01-01",
+ // valid, but this calendar must not exist:
+ "2020-01-01[u-ca=notexist]",
+ // may be valid in other contexts, but insufficient information for PlainDate:
+ "2020-01",
+ "+002020-01",
+ "01-01",
+ "2020-W01",
+ "P1Y",
+ "-P12Y",
+ // valid, but outside the supported range:
+ "-999999-01-01",
+ "+999999-01-01",
+];
+const instance = new Temporal.Calendar("iso8601");
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => instance.monthsInYear(arg),
+ `"${arg}" should not be a valid ISO string for a PlainDate`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..0be9d3322c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/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.calendar.prototype.monthsinyear
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[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.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.monthsInYear(arg),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..76d6a2889f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-string-multiple-time-zone.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.calendar.prototype.monthsinyear
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[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.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.monthsInYear(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-string-time-separators.js
new file mode 100644
index 0000000000..05b17c402e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-string-time-separators.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.calendar.prototype.monthsinyear
+description: Time separator in string argument can vary
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02T15:23", "uppercase T"],
+ ["2000-05-02t15:23", "lowercase T"],
+ ["2000-05-02 15:23", "space between date and time"],
+];
+
+const instance = new Temporal.Calendar("iso8601");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.monthsInYear(arg);
+
+ assert.sameValue(
+ result,
+ 12,
+ `variant time separators (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..dd25899d21
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-string-time-zone-annotation.js
@@ -0,0 +1,38 @@
+// |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.calendar.prototype.monthsinyear
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02[Asia/Kolkata]", "named, with no time"],
+ ["2000-05-02[!Europe/Vienna]", "named, with ! and no time"],
+ ["2000-05-02[+00:00]", "numeric, with no time"],
+ ["2000-05-02[!-02:30]", "numeric, with ! and no time"],
+ ["2000-05-02T15:23[America/Sao_Paulo]", "named, with no offset"],
+ ["2000-05-02T15:23[!Asia/Tokyo]", "named, with ! and no offset"],
+ ["2000-05-02T15:23[-02:30]", "numeric, with no offset"],
+ ["2000-05-02T15:23[!+00:00]", "numeric, with ! and no offset"],
+ ["2000-05-02T15:23+00:00[America/New_York]", "named, with offset"],
+ ["2000-05-02T15:23+00:00[!UTC]", "named, with offset and !"],
+ ["2000-05-02T15:23+00:00[+01:00]", "numeric, with offset"],
+ ["2000-05-02T15:23+00:00[!-08:00]", "numeric, with offset and !"],
+];
+
+const instance = new Temporal.Calendar("iso8601");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.monthsInYear(arg);
+
+ assert.sameValue(
+ result,
+ 12,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..bb7b7ee3f2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-string-unknown-annotation.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.calendar.prototype.monthsinyear
+description: Various forms of unknown annotation
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02[foo=bar]", "without time"],
+ ["2000-05-02T15:23[foo=bar]", "alone"],
+ ["2000-05-02T15:23[UTC][foo=bar]", "with time zone"],
+ ["2000-05-02T15:23[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["2000-05-02T15:23[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["2000-05-02T15:23[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+const instance = new Temporal.Calendar("iso8601");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.monthsInYear(arg);
+
+ assert.sameValue(
+ result,
+ 12,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..1d40c304b9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-string-with-utc-designator.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.calendar.prototype.monthsinyear
+description: RangeError thrown if a string with UTC designator is used as a PlainDate
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "2019-10-01T09:00:00Z",
+ "2019-10-01T09:00:00Z[UTC]",
+];
+const instance = new Temporal.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.monthsInYear(arg),
+ "String with UTC designator should not be valid as a PlainDate"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-string.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-string.js
new file mode 100644
index 0000000000..ef3a529db2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-string.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.monthsinyear
+description: An ISO 8601 date string should be converted as input
+info: |
+ a. Perform ? ToTemporalDate(temporalDateLike).
+ 5. Return 12𝔽.
+features: [Temporal]
+---*/
+
+let cal = new Temporal.Calendar("iso8601");
+
+assert.sameValue(cal.monthsInYear("3456-12-20"), 12);
+assert.sameValue(cal.monthsInYear("+000998-01-28"), 12);
+assert.sameValue(cal.monthsInYear("3456-12-20T03:04:05+00:00[UTC]"), 12);
+assert.sameValue(cal.monthsInYear("+000998-01-28T03:04:05+00:00[UTC]"), 12);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-wrong-type.js
new file mode 100644
index 0000000000..bbe7c6bec4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/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.calendar.prototype.monthsinyear
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainDate
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+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.monthsInYear(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.PlainDate, "Temporal.PlainDate, object"],
+ [Temporal.PlainDate.prototype, "Temporal.PlainDate.prototype, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.monthsInYear(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/Calendar/prototype/monthsInYear/argument-zoneddatetime-convert.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-zoneddatetime-convert.js
new file mode 100644
index 0000000000..63120471a8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-zoneddatetime-convert.js
@@ -0,0 +1,22 @@
+// |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.calendar.prototype.monthsinyear
+description: An exception from TimeZone#getOffsetNanosecondsFor() is propagated.
+features: [Temporal]
+---*/
+
+class TZ extends Temporal.TimeZone {
+ constructor() { super("UTC") }
+ getOffsetNanosecondsFor() { throw new Test262Error() }
+}
+
+const tz = new TZ();
+const arg = new Temporal.ZonedDateTime(0n, tz);
+const instance = new Temporal.Calendar("iso8601");
+
+assert.throws(Test262Error, () => instance.monthsInYear(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-zoneddatetime-slots.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-zoneddatetime-slots.js
new file mode 100644
index 0000000000..e937a035ca
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-zoneddatetime-slots.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.calendar.prototype.monthsinyear
+description: Getters are not called when converting a ZonedDateTime to a PlainDate.
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.ZonedDateTime.prototype);
+const getters = ["year", "month", "monthCode", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond", "calendar"];
+
+for (const property of getters) {
+ Object.defineProperty(Temporal.ZonedDateTime.prototype, property, {
+ get() {
+ actual.push(`get ${property}`);
+ const value = prototypeDescrs[property].get.call(this);
+ return {
+ toString() {
+ actual.push(`toString ${property}`);
+ return value.toString();
+ },
+ valueOf() {
+ actual.push(`valueOf ${property}`);
+ return value;
+ },
+ };
+ },
+ });
+}
+
+const arg = new Temporal.ZonedDateTime(0n, "UTC");
+const instance = new Temporal.Calendar("iso8601");
+instance.monthsInYear(arg);
+assert.compareArray(actual, []);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..a49579b197
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/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.calendar.prototype.monthsinyear
+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 calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => calendar.monthsInYear(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..aced8bc1b5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/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.calendar.prototype.monthsinyear
+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 calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => calendar.monthsInYear(datetime),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..5a1618fd50
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/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.calendar.prototype.monthsinyear
+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 calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => calendar.monthsInYear(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..794fa1edd6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/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.calendar.prototype.monthsinyear
+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 calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => calendar.monthsInYear(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/basic.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/basic.js
new file mode 100644
index 0000000000..a74818422a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/basic.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.calendar.prototype.monthsinyear
+description: Basic tests for monthsInYear().
+features: [Temporal]
+---*/
+
+const iso = Temporal.Calendar.from("iso8601");
+const res = 12;
+assert.sameValue(iso.monthsInYear(new Temporal.PlainDate(1994, 11, 5)), res, "PlainDate");
+assert.sameValue(iso.monthsInYear(new Temporal.PlainDateTime(1994, 11, 5, 8, 15, 30)), res, "PlainDateTime");
+assert.sameValue(iso.monthsInYear(new Temporal.PlainYearMonth(1994, 11)), res, "PlainYearMonth");
+assert.sameValue(iso.monthsInYear({ year: 1994, month: 11, day: 5 }), res, "property bag");
+assert.sameValue(iso.monthsInYear("1994-11-05"), res, "string");
+assert.throws(TypeError, () => iso.monthsInYear({ year: 2000 }), "property bag with missing properties");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/branding.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/branding.js
new file mode 100644
index 0000000000..f0733876d6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/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.calendar.prototype.monthsinyear
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const monthsInYear = Temporal.Calendar.prototype.monthsInYear;
+
+assert.sameValue(typeof monthsInYear, "function");
+
+const args = [new Temporal.PlainDate(2021, 3, 4)];
+
+assert.throws(TypeError, () => monthsInYear.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => monthsInYear.apply(null, args), "null");
+assert.throws(TypeError, () => monthsInYear.apply(true, args), "true");
+assert.throws(TypeError, () => monthsInYear.apply("", args), "empty string");
+assert.throws(TypeError, () => monthsInYear.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => monthsInYear.apply(1, args), "1");
+assert.throws(TypeError, () => monthsInYear.apply({}, args), "plain object");
+assert.throws(TypeError, () => monthsInYear.apply(Temporal.Calendar, args), "Temporal.Calendar");
+assert.throws(TypeError, () => monthsInYear.apply(Temporal.Calendar.prototype, args), "Temporal.Calendar.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/browser.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/builtin.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/builtin.js
new file mode 100644
index 0000000000..66ec0ebe99
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/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.calendar.prototype.monthsinyear
+description: >
+ Tests that Temporal.Calendar.prototype.monthsInYear
+ 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.Calendar.prototype.monthsInYear),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Calendar.prototype.monthsInYear),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Calendar.prototype.monthsInYear),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Calendar.prototype.monthsInYear.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/calendar-datefromfields-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/calendar-datefromfields-called-with-options-undefined.js
new file mode 100644
index 0000000000..416cc559a1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/calendar-datefromfields-called-with-options-undefined.js
@@ -0,0 +1,18 @@
+// |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.calendar.prototype.monthsinyear
+description: >
+ Calendar.dateFromFields method is called with undefined as the options value
+ when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
+calendar.monthsInYear({ year: 2000, month: 5, day: 3, calendar });
+assert.sameValue(calendar.dateFromFieldsCallCount, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/calendar-fields-iterable.js
new file mode 100644
index 0000000000..80daa1bc7b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/calendar-fields-iterable.js
@@ -0,0 +1,35 @@
+// |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.calendar.prototype.monthsinyear
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.calendar.prototype.monthsinyear step 4:
+ 4. Perform ? ToTemporalDate(_dateOrDateTime_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToListOfType(_fieldsArray_, « String »).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+calendar1.monthsInYear({ year: 2000, month: 5, day: 2, calendar: calendar2 });
+
+assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/calendar-temporal-object.js
new file mode 100644
index 0000000000..b65eff793b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/calendar-temporal-object.js
@@ -0,0 +1,29 @@
+// |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.calendar.prototype.monthsinyear
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.calendar.prototype.monthsinyear step 4:
+ 4. Perform ? ToTemporalDate(_dateOrDateTime_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const calendar = new Temporal.Calendar("iso8601");
+ calendar.monthsInYear({ year: 2000, month: 5, day: 2, calendar: temporalObject });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..d0937c3663
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/infinity-throws-rangeerror.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.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.calendar.prototype.monthsinyear
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+const base = { year: 2000, month: 5, day: 2 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.monthsInYear({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.monthsInYear({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/length.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/length.js
new file mode 100644
index 0000000000..14d9d9d9e1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/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.calendar.prototype.monthsinyear
+description: Temporal.Calendar.prototype.monthsInYear.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.Calendar.prototype.monthsInYear, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/name.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/name.js
new file mode 100644
index 0000000000..bb140c095e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/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.calendar.prototype.monthsinyear
+description: Temporal.Calendar.prototype.monthsInYear.name is "monthsInYear".
+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.Calendar.prototype.monthsInYear, "name", {
+ value: "monthsInYear",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/not-a-constructor.js
new file mode 100644
index 0000000000..bf5940e1df
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/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.calendar.prototype.monthsinyear
+description: >
+ Temporal.Calendar.prototype.monthsInYear 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.Calendar.prototype.monthsInYear();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Calendar.prototype.monthsInYear), false,
+ "isConstructor(Temporal.Calendar.prototype.monthsInYear)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/prop-desc.js
new file mode 100644
index 0000000000..b6e58dea1e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/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.calendar.prototype.monthsinyear
+description: The "monthsInYear" property of Temporal.Calendar.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Calendar.prototype.monthsInYear,
+ "function",
+ "`typeof Calendar.prototype.monthsInYear` is `function`"
+);
+
+verifyProperty(Temporal.Calendar.prototype, "monthsInYear", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/shell.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/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/Calendar/prototype/monthsInYear/year-zero.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/year-zero.js
new file mode 100644
index 0000000000..bb6d5c9da8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/monthsInYear/year-zero.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.calendar.prototype.monthsinyear
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T00:45",
+ "-000000-10-31T00:45+01:00",
+ "-000000-10-31T00:45+00:00[UTC]",
+];
+const instance = new Temporal.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.monthsInYear(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/prop-desc.js
new file mode 100644
index 0000000000..04b8e87d06
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/prop-desc.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-calendar-prototype
+description: The "prototype" property of Temporal.Calendar
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(typeof Temporal.Calendar.prototype, "object");
+assert.notSameValue(Temporal.Calendar.prototype, null);
+
+verifyProperty(Temporal.Calendar, "prototype", {
+ writable: false,
+ enumerable: false,
+ configurable: false,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/shell.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toJSON/branding.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toJSON/branding.js
new file mode 100644
index 0000000000..3c0caacf81
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toJSON/branding.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.calendar.prototype.tojson
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toJSON = Temporal.Calendar.prototype.toJSON;
+
+assert.sameValue(typeof toJSON, "function");
+
+assert.throws(TypeError, () => toJSON.call(undefined), "undefined");
+assert.throws(TypeError, () => toJSON.call(null), "null");
+assert.throws(TypeError, () => toJSON.call(true), "true");
+assert.throws(TypeError, () => toJSON.call(""), "empty string");
+assert.throws(TypeError, () => toJSON.call(Symbol()), "symbol");
+assert.throws(TypeError, () => toJSON.call(1), "1");
+assert.throws(TypeError, () => toJSON.call({}), "plain object");
+assert.throws(TypeError, () => toJSON.call(Temporal.Calendar), "Temporal.Calendar");
+assert.throws(TypeError, () => toJSON.call(Temporal.Calendar.prototype), "Temporal.Calendar.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toJSON/browser.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toJSON/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toJSON/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toJSON/builtin.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toJSON/builtin.js
new file mode 100644
index 0000000000..21a148167a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toJSON/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.calendar.prototype.tojson
+description: >
+ Tests that Temporal.Calendar.prototype.toJSON
+ 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.Calendar.prototype.toJSON),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Calendar.prototype.toJSON),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Calendar.prototype.toJSON),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Calendar.prototype.toJSON.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toJSON/length.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toJSON/length.js
new file mode 100644
index 0000000000..7328585d32
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toJSON/length.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.calendar.prototype.tojson
+description: Temporal.Calendar.prototype.toJSON.length is 0
+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.Calendar.prototype.toJSON, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toJSON/name.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toJSON/name.js
new file mode 100644
index 0000000000..15a3bf26bf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toJSON/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.calendar.prototype.tojson
+description: Temporal.Calendar.prototype.toJSON.name is "toJSON".
+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.Calendar.prototype.toJSON, "name", {
+ value: "toJSON",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toJSON/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toJSON/not-a-constructor.js
new file mode 100644
index 0000000000..fa6dd3872a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toJSON/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.calendar.prototype.tojson
+description: >
+ Temporal.Calendar.prototype.toJSON 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.Calendar.prototype.toJSON();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Calendar.prototype.toJSON), false,
+ "isConstructor(Temporal.Calendar.prototype.toJSON)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toJSON/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toJSON/prop-desc.js
new file mode 100644
index 0000000000..8b0e819c89
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toJSON/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.calendar.prototype.tojson
+description: The "toJSON" property of Temporal.Calendar.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Calendar.prototype.toJSON,
+ "function",
+ "`typeof Calendar.prototype.toJSON` is `function`"
+);
+
+verifyProperty(Temporal.Calendar.prototype, "toJSON", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toJSON/returns-identifier-slot.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toJSON/returns-identifier-slot.js
new file mode 100644
index 0000000000..297b066c40
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toJSON/returns-identifier-slot.js
@@ -0,0 +1,26 @@
+// |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.calendar.prototype.tojson
+description: toJSON() returns the internal slot value
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+
+const calendar = new Temporal.Calendar("iso8601");
+TemporalHelpers.observeProperty(actual, calendar, Symbol.toPrimitive, undefined);
+TemporalHelpers.observeProperty(actual, calendar, "id", "bogus");
+TemporalHelpers.observeProperty(actual, calendar, "toString", function () {
+ actual.push("call calendar.toString");
+ return "gregory";
+});
+
+const result = calendar.toJSON();
+assert.sameValue(result, "iso8601", "toJSON gets the internal slot value");
+assert.compareArray(actual, [], "should not invoke any observable operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toJSON/shell.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toJSON/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toJSON/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/Calendar/prototype/toString/branding.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toString/branding.js
new file mode 100644
index 0000000000..947edcb284
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toString/branding.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.calendar.prototype.tostring
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toString = Temporal.Calendar.prototype.toString;
+
+assert.sameValue(typeof toString, "function");
+
+assert.throws(TypeError, () => toString.call(undefined), "undefined");
+assert.throws(TypeError, () => toString.call(null), "null");
+assert.throws(TypeError, () => toString.call(true), "true");
+assert.throws(TypeError, () => toString.call(""), "empty string");
+assert.throws(TypeError, () => toString.call(Symbol()), "symbol");
+assert.throws(TypeError, () => toString.call(1), "1");
+assert.throws(TypeError, () => toString.call({}), "plain object");
+assert.throws(TypeError, () => toString.call(Temporal.Calendar), "Temporal.Calendar");
+assert.throws(TypeError, () => toString.call(Temporal.Calendar.prototype), "Temporal.Calendar.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toString/browser.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toString/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toString/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toString/builtin.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toString/builtin.js
new file mode 100644
index 0000000000..6f0e53c4d3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toString/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.calendar.prototype.tostring
+description: >
+ Tests that Temporal.Calendar.prototype.toString
+ 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.Calendar.prototype.toString),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Calendar.prototype.toString),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Calendar.prototype.toString),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Calendar.prototype.toString.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toString/length.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toString/length.js
new file mode 100644
index 0000000000..00c4b7f57f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toString/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.calendar.prototype.tostring
+description: Temporal.Calendar.prototype.toString.length is 0
+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.Calendar.prototype.toString, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toString/name.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toString/name.js
new file mode 100644
index 0000000000..b5268eef32
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toString/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.calendar.prototype.tostring
+description: Temporal.Calendar.prototype.toString.name is "toString".
+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.Calendar.prototype.toString, "name", {
+ value: "toString",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toString/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toString/not-a-constructor.js
new file mode 100644
index 0000000000..03f2a139ec
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toString/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.calendar.prototype.tostring
+description: >
+ Temporal.Calendar.prototype.toString 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.Calendar.prototype.toString();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Calendar.prototype.toString), false,
+ "isConstructor(Temporal.Calendar.prototype.toString)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toString/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toString/prop-desc.js
new file mode 100644
index 0000000000..d0b3d53e99
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toString/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.calendar.prototype.tostring
+description: The "toString" property of Temporal.Calendar.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Calendar.prototype.toString,
+ "function",
+ "`typeof Calendar.prototype.toString` is `function`"
+);
+
+verifyProperty(Temporal.Calendar.prototype, "toString", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toString/shell.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toString/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toString/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/Calendar/prototype/toStringTag/browser.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toStringTag/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toStringTag/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toStringTag/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toStringTag/prop-desc.js
new file mode 100644
index 0000000000..9ef3a23e21
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toStringTag/prop-desc.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.calendar.prototype-@@tostringtag
+description: The @@toStringTag property of Temporal.Calendar
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Calendar.prototype, Symbol.toStringTag, {
+ value: "Temporal.Calendar",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toStringTag/shell.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toStringTag/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/toStringTag/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..fd0e559c7d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-builtin-calendar-no-array-iteration.js
@@ -0,0 +1,24 @@
+// |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.calendar.prototype.weekofyear
+description: >
+ Calling the method with a property bag argument with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.Calendar("iso8601");
+const arg = { year: 2000, month: 5, day: 2, calendar: "iso8601" };
+instance.weekOfYear(arg);
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-calendar-datefromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..2e3ae9291d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-calendar-datefromfields-called-with-null-prototype-fields.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.calendar.prototype.weekofyear
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.Calendar("iso8601");
+const arg = { year: 2000, month: 5, day: 2, calendar };
+instance.weekOfYear(arg);
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..24b26e1281
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-constructor-in-calendar-fields.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.calendar.prototype.weekofyear
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.Calendar("iso8601");
+
+assert.throws(RangeError, () => instance.weekOfYear(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-duplicate-calendar-fields.js
new file mode 100644
index 0000000000..cc1de124c3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-duplicate-calendar-fields.js
@@ -0,0 +1,19 @@
+// |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.calendar.prototype.weekofyear
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['day'], ['month'], ['monthCode'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar };
+ const instance = new Temporal.Calendar("iso8601");
+
+ assert.throws(RangeError, () => instance.weekOfYear(arg));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-leap-second.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-leap-second.js
new file mode 100644
index 0000000000..00140dd078
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-leap-second.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.calendar.prototype.weekofyear
+description: Leap second is a valid ISO string for PlainDate
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+let arg = "2016-12-31T23:59:60";
+const result1 = instance.weekOfYear(arg);
+assert.sameValue(
+ result1,
+ 52,
+ "leap second is a valid ISO string for PlainDate"
+);
+
+arg = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+const result2 = instance.weekOfYear(arg);
+assert.sameValue(
+ result2,
+ 52,
+ "second: 60 is ignored in property bag for PlainDate"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-number.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-number.js
new file mode 100644
index 0000000000..d631e5fe18
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/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.calendar.prototype.weekofyear
+description: A number cannot be used in place of a Temporal.PlainDate
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const numbers = [
+ 1,
+ 19761118,
+ -19761118,
+ 1234567890,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => instance.weekOfYear(arg),
+ 'Numbers cannot be used in place of an ISO string for PlainDate'
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-plaindate.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-plaindate.js
new file mode 100644
index 0000000000..b7fda66665
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-plaindate.js
@@ -0,0 +1,79 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.weekofyear
+description: >
+ Temporal.Calendar.prototype.weekOfYear will take Temporal.PlainDate object
+ and return the week of year of that date.
+info: |
+ 4. Let temporalDate be ? ToTemporalDate(temporalDateLike).
+ 5. Return 𝔽(! ToISOWeekOfYear(temporalDate.[[ISOYear]], temporalDate.[[ISOMonth]], temporalDate.[[ISODay]])).
+features: [Temporal]
+---*/
+
+const cal = new Temporal.Calendar("iso8601");
+
+// The following week numbers are taken from the table "Examples of contemporary
+// dates around New Year's Day" from
+// https://en.wikipedia.org/wiki/ISO_week_date#Relation_with_the_Gregorian_calendar
+
+let d = new Temporal.PlainDate(1977, 1, 1);
+assert.sameValue(cal.weekOfYear(d), 53, "1977-01-01 is in w53");
+
+d = new Temporal.PlainDate(1977, 1, 2);
+assert.sameValue(cal.weekOfYear(d), 53, "1977-01-02 is in w53");
+
+d = new Temporal.PlainDate(1977, 12, 31);
+assert.sameValue(cal.weekOfYear(d), 52, "1977-12-31 is in w52");
+
+d = new Temporal.PlainDate(1978, 1, 1);
+assert.sameValue(cal.weekOfYear(d), 52, "1978-01-01 is in w52");
+
+d = new Temporal.PlainDate(1978, 1, 2);
+assert.sameValue(cal.weekOfYear(d), 1, "1978-01-02 is in w01");
+
+d = new Temporal.PlainDate(1978, 12, 31);
+assert.sameValue(cal.weekOfYear(d), 52, "1978-12-31 is in w52");
+
+d = new Temporal.PlainDate(1979, 1, 1);
+assert.sameValue(cal.weekOfYear(d), 1, "1979-01-01 is in w01");
+
+d = new Temporal.PlainDate(1979, 12, 30);
+assert.sameValue(cal.weekOfYear(d), 52, "1979-12-30 is in w52");
+
+d = new Temporal.PlainDate(1979, 12, 31);
+assert.sameValue(cal.weekOfYear(d), 1, "1979-12-31 is in w01");
+
+d = new Temporal.PlainDate(1980, 1, 1);
+assert.sameValue(cal.weekOfYear(d), 1, "1980-01-01 is in w01");
+
+d = new Temporal.PlainDate(1980, 12, 28);
+assert.sameValue(cal.weekOfYear(d), 52, "1980-12-28 is in w52");
+
+d = new Temporal.PlainDate(1980, 12, 29);
+assert.sameValue(cal.weekOfYear(d), 1, "1980-12-29 is in w01");
+
+d = new Temporal.PlainDate(1980, 12, 30);
+assert.sameValue(cal.weekOfYear(d), 1, "1980-12-30 is in w01");
+
+d = new Temporal.PlainDate(1980, 12, 31);
+assert.sameValue(cal.weekOfYear(d), 1, "1980-12-31 is in w01");
+
+d = new Temporal.PlainDate(1981, 1, 1);
+assert.sameValue(cal.weekOfYear(d), 1, "1981-01-01 is in w01");
+
+d = new Temporal.PlainDate(1981, 12, 31);
+assert.sameValue(cal.weekOfYear(d), 53, "1981-12-31 is in w53");
+
+d = new Temporal.PlainDate(1982, 1, 1);
+assert.sameValue(cal.weekOfYear(d), 53, "1982-01-01 is in w53");
+
+d = new Temporal.PlainDate(1982, 1, 2);
+assert.sameValue(cal.weekOfYear(d), 53, "1982-01-02 is in w53");
+
+d = new Temporal.PlainDate(1982, 1, 3);
+assert.sameValue(cal.weekOfYear(d), 53, "1982-01-03 is in w53");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-plaindatetime.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-plaindatetime.js
new file mode 100644
index 0000000000..b92cd47f7e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-plaindatetime.js
@@ -0,0 +1,79 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.weekofyear
+description: >
+ Temporal.Calendar.prototype.weekOfYear will take Temporal.PlainDateTime object
+ and return the week of year of that date.
+info: |
+ 4. Let temporalDate be ? ToTemporalDate(temporalDateLike).
+ 5. Return 𝔽(! ToISOWeekOfYear(temporalDate.[[ISOYear]], temporalDate.[[ISOMonth]], temporalDate.[[ISODay]])).
+features: [Temporal]
+---*/
+
+const cal = new Temporal.Calendar("iso8601");
+
+// The following week numbers are taken from the table "Examples of contemporary
+// dates around New Year's Day" from
+// https://en.wikipedia.org/wiki/ISO_week_date#Relation_with_the_Gregorian_calendar
+
+let dt = new Temporal.PlainDateTime(1977, 1, 1, 9, 8);
+assert.sameValue(cal.weekOfYear(dt), 53, "1977-01-01 is in w53");
+
+dt = new Temporal.PlainDateTime(1977, 1, 2, 9, 8);
+assert.sameValue(cal.weekOfYear(dt), 53, "1977-01-02 is in w53");
+
+dt = new Temporal.PlainDateTime(1977, 12, 31, 9, 8);
+assert.sameValue(cal.weekOfYear(dt), 52, "1977-12-31 is in w52");
+
+dt = new Temporal.PlainDateTime(1978, 1, 1, 9, 8);
+assert.sameValue(cal.weekOfYear(dt), 52, "1978-01-01 is in w52");
+
+dt = new Temporal.PlainDateTime(1978, 1, 2, 9, 8);
+assert.sameValue(cal.weekOfYear(dt), 1, "1978-01-02 is in w01");
+
+dt = new Temporal.PlainDateTime(1978, 12, 31, 9, 8);
+assert.sameValue(cal.weekOfYear(dt), 52, "1978-12-31 is in w52");
+
+dt = new Temporal.PlainDateTime(1979, 1, 1, 9, 8);
+assert.sameValue(cal.weekOfYear(dt), 1, "1979-01-01 is in w01");
+
+dt = new Temporal.PlainDateTime(1979, 12, 30, 9, 8);
+assert.sameValue(cal.weekOfYear(dt), 52, "1979-12-30 is in w52");
+
+dt = new Temporal.PlainDateTime(1979, 12, 31, 9, 8);
+assert.sameValue(cal.weekOfYear(dt), 1, "1979-12-31 is in w01");
+
+dt = new Temporal.PlainDateTime(1980, 1, 1, 9, 8);
+assert.sameValue(cal.weekOfYear(dt), 1, "1980-01-01 is in w01");
+
+dt = new Temporal.PlainDateTime(1980, 12, 28, 9, 8);
+assert.sameValue(cal.weekOfYear(dt), 52, "1980-12-28 is in w52");
+
+dt = new Temporal.PlainDateTime(1980, 12, 29, 9, 8);
+assert.sameValue(cal.weekOfYear(dt), 1, "1980-12-29 is in w01");
+
+dt = new Temporal.PlainDateTime(1980, 12, 30, 9, 8);
+assert.sameValue(cal.weekOfYear(dt), 1, "1980-12-30 is in w01");
+
+dt = new Temporal.PlainDateTime(1980, 12, 31, 9, 8);
+assert.sameValue(cal.weekOfYear(dt), 1, "1980-12-31 is in w01");
+
+dt = new Temporal.PlainDateTime(1981, 1, 1, 9, 8);
+assert.sameValue(cal.weekOfYear(dt), 1, "1981-01-01 is in w01");
+
+dt = new Temporal.PlainDateTime(1981, 12, 31, 9, 8);
+assert.sameValue(cal.weekOfYear(dt), 53, "1981-12-31 is in w53");
+
+dt = new Temporal.PlainDateTime(1982, 1, 1, 9, 8);
+assert.sameValue(cal.weekOfYear(dt), 53, "1982-01-01 is in w53");
+
+dt = new Temporal.PlainDateTime(1982, 1, 2, 9, 8);
+assert.sameValue(cal.weekOfYear(dt), 53, "1982-01-02 is in w53");
+
+dt = new Temporal.PlainDateTime(1982, 1, 3, 9, 8);
+assert.sameValue(cal.weekOfYear(dt), 53, "1982-01-03 is in w53");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-propertybag-calendar-case-insensitive.js
new file mode 100644
index 0000000000..5970881125
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-propertybag-calendar-case-insensitive.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.calendar.prototype.weekofyear
+description: The calendar name is case-insensitive
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const calendar = "IsO8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.weekOfYear(arg);
+assert.sameValue(result, 47, "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-propertybag-calendar-leap-second.js
new file mode 100644
index 0000000000..787d8c128e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-propertybag-calendar-leap-second.js
@@ -0,0 +1,23 @@
+// |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.calendar.prototype.weekofyear
+description: Leap second is a valid ISO string for a calendar in a property bag
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const calendar = "2016-12-31T23:59:60";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.weekOfYear(arg);
+assert.sameValue(
+ result,
+ 47,
+ "leap second is a valid ISO string for calendar"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-propertybag-calendar-number.js
new file mode 100644
index 0000000000..cb4c271731
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-propertybag-calendar-number.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.calendar.prototype.weekofyear
+description: A number as calendar in a property bag is not accepted
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const numbers = [
+ 1,
+ 19970327,
+ -19970327,
+ 1234567890,
+];
+
+for (const calendar of numbers) {
+ const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+ assert.throws(
+ TypeError,
+ () => instance.weekOfYear(arg),
+ "Numbers cannot be used as a calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-propertybag-calendar-string.js
new file mode 100644
index 0000000000..ca01bb614b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-propertybag-calendar-string.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.calendar.prototype.weekofyear
+description: A calendar ID is valid input for Calendar
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const calendar = "iso8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.weekOfYear(arg);
+assert.sameValue(result, 47, `Calendar created from string "${calendar}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-propertybag-calendar-wrong-type.js
new file mode 100644
index 0000000000..9c70b5e3fc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-propertybag-calendar-wrong-type.js
@@ -0,0 +1,46 @@
+// |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.calendar.prototype.weekofyear
+description: >
+ Appropriate error thrown when a calendar property from a property bag cannot
+ be converted to a calendar object or string
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.Calendar("iso8601");
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [calendar, description] of primitiveTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ typeof calendar === 'string' ? RangeError : TypeError,
+ () => instance.weekOfYear(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+ [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields()
+];
+
+for (const [calendar, description] of typeErrorTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(TypeError, () => instance.weekOfYear(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/Calendar/prototype/weekOfYear/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-propertybag-calendar-year-zero.js
new file mode 100644
index 0000000000..023b2ca730
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-propertybag-calendar-year-zero.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.calendar.prototype.weekofyear
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T17:45",
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+01:00",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+const instance = new Temporal.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.weekOfYear(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-proto-in-calendar-fields.js
new file mode 100644
index 0000000000..d0ef9a8fd8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-proto-in-calendar-fields.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.calendar.prototype.weekofyear
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.Calendar("iso8601");
+
+assert.throws(RangeError, () => instance.weekOfYear(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..89d3de01b9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-string-calendar-annotation.js
@@ -0,0 +1,33 @@
+// |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.calendar.prototype.weekofyear
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02[u-ca=iso8601]", "without time or time zone"],
+ ["2000-05-02[UTC][u-ca=iso8601]", "with time zone and no time"],
+ ["2000-05-02T15:23[u-ca=iso8601]", "without time zone"],
+ ["2000-05-02T15:23[UTC][u-ca=iso8601]", "with time zone"],
+ ["2000-05-02T15:23[!u-ca=iso8601]", "with ! and no time zone"],
+ ["2000-05-02T15:23[UTC][!u-ca=iso8601]", "with ! and time zone"],
+ ["2000-05-02T15:23[u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+const instance = new Temporal.Calendar("iso8601");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.weekOfYear(arg);
+
+ assert.sameValue(
+ result,
+ 18,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..c031c1d9f6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-string-critical-unknown-annotation.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.calendar.prototype.weekofyear
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[!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.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.weekOfYear(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..caf708e268
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-string-date-with-utc-offset.js
@@ -0,0 +1,48 @@
+// |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.calendar.prototype.weekofyear
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const validStrings = [
+ "2000-05-02T00+00:00",
+ "2000-05-02T00+00:00[UTC]",
+ "2000-05-02T00+00:00[!UTC]",
+ "2000-05-02T00-02:30[America/St_Johns]",
+];
+
+for (const arg of validStrings) {
+ const result = instance.weekOfYear(arg);
+
+ assert.sameValue(
+ result,
+ 18,
+ `"${arg}" is a valid UTC offset with time for PlainDate`
+ );
+}
+
+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.weekOfYear(arg),
+ `"${arg}" UTC offset without time is not valid for PlainDate`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-string-invalid.js
new file mode 100644
index 0000000000..9325337841
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-string-invalid.js
@@ -0,0 +1,64 @@
+// |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.calendar.prototype.weekofyear
+description: >
+ RangeError thrown if an invalid ISO string (or syntactically valid ISO string
+ that is not supported) is used as a PlainDate
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ // invalid ISO strings:
+ "",
+ "invalid iso8601",
+ "2020-01-00",
+ "2020-01-32",
+ "2020-02-30",
+ "2021-02-29",
+ "2020-00-01",
+ "2020-13-01",
+ "2020-01-01T",
+ "2020-01-01T25:00:00",
+ "2020-01-01T01:60:00",
+ "2020-01-01T01:60:61",
+ "2020-01-01junk",
+ "2020-01-01T00:00:00junk",
+ "2020-01-01T00:00:00+00:00junk",
+ "2020-01-01T00:00:00+00:00[UTC]junk",
+ "2020-01-01T00:00:00+00:00[UTC][u-ca=iso8601]junk",
+ "02020-01-01",
+ "2020-001-01",
+ "2020-01-001",
+ "2020-01-01T001",
+ "2020-01-01T01:001",
+ "2020-01-01T01:01:001",
+ // valid, but forms not supported in Temporal:
+ "2020-W01-1",
+ "2020-001",
+ "+0002020-01-01",
+ // valid, but this calendar must not exist:
+ "2020-01-01[u-ca=notexist]",
+ // may be valid in other contexts, but insufficient information for PlainDate:
+ "2020-01",
+ "+002020-01",
+ "01-01",
+ "2020-W01",
+ "P1Y",
+ "-P12Y",
+ // valid, but outside the supported range:
+ "-999999-01-01",
+ "+999999-01-01",
+];
+const instance = new Temporal.Calendar("iso8601");
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => instance.weekOfYear(arg),
+ `"${arg}" should not be a valid ISO string for a PlainDate`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..9cc6e3b820
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/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.calendar.prototype.weekofyear
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[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.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.weekOfYear(arg),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..72ff52c876
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-string-multiple-time-zone.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.calendar.prototype.weekofyear
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[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.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.weekOfYear(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-string-time-separators.js
new file mode 100644
index 0000000000..7a6839eee8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-string-time-separators.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.calendar.prototype.weekofyear
+description: Time separator in string argument can vary
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02T15:23", "uppercase T"],
+ ["2000-05-02t15:23", "lowercase T"],
+ ["2000-05-02 15:23", "space between date and time"],
+];
+
+const instance = new Temporal.Calendar("iso8601");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.weekOfYear(arg);
+
+ assert.sameValue(
+ result,
+ 18,
+ `variant time separators (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..86332e48bc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-string-time-zone-annotation.js
@@ -0,0 +1,38 @@
+// |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.calendar.prototype.weekofyear
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02[Asia/Kolkata]", "named, with no time"],
+ ["2000-05-02[!Europe/Vienna]", "named, with ! and no time"],
+ ["2000-05-02[+00:00]", "numeric, with no time"],
+ ["2000-05-02[!-02:30]", "numeric, with ! and no time"],
+ ["2000-05-02T15:23[America/Sao_Paulo]", "named, with no offset"],
+ ["2000-05-02T15:23[!Asia/Tokyo]", "named, with ! and no offset"],
+ ["2000-05-02T15:23[-02:30]", "numeric, with no offset"],
+ ["2000-05-02T15:23[!+00:00]", "numeric, with ! and no offset"],
+ ["2000-05-02T15:23+00:00[America/New_York]", "named, with offset"],
+ ["2000-05-02T15:23+00:00[!UTC]", "named, with offset and !"],
+ ["2000-05-02T15:23+00:00[+01:00]", "numeric, with offset"],
+ ["2000-05-02T15:23+00:00[!-08:00]", "numeric, with offset and !"],
+];
+
+const instance = new Temporal.Calendar("iso8601");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.weekOfYear(arg);
+
+ assert.sameValue(
+ result,
+ 18,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..9f91ef3d31
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-string-unknown-annotation.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.calendar.prototype.weekofyear
+description: Various forms of unknown annotation
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02[foo=bar]", "without time"],
+ ["2000-05-02T15:23[foo=bar]", "alone"],
+ ["2000-05-02T15:23[UTC][foo=bar]", "with time zone"],
+ ["2000-05-02T15:23[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["2000-05-02T15:23[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["2000-05-02T15:23[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+const instance = new Temporal.Calendar("iso8601");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.weekOfYear(arg);
+
+ assert.sameValue(
+ result,
+ 18,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..f7fc0c82b7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-string-with-utc-designator.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.calendar.prototype.weekofyear
+description: RangeError thrown if a string with UTC designator is used as a PlainDate
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "2019-10-01T09:00:00Z",
+ "2019-10-01T09:00:00Z[UTC]",
+];
+const instance = new Temporal.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.weekOfYear(arg),
+ "String with UTC designator should not be valid as a PlainDate"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-string.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-string.js
new file mode 100644
index 0000000000..0a7b4b90d8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-string.js
@@ -0,0 +1,42 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.weekofyear
+description: >
+ Temporal.Calendar.prototype.weekOfYear will take an ISO 8601 date string and
+ return the week of year of that date.
+info: |
+ 4. Let temporalDate be ? ToTemporalDate(temporalDateLike).
+ 5. Return 𝔽(! ToISOWeekOfYear(temporalDate.[[ISOYear]], temporalDate.[[ISOMonth]], temporalDate.[[ISODay]])).
+features: [Temporal]
+---*/
+
+const cal = new Temporal.Calendar("iso8601");
+
+// The following week numbers are taken from the table "Examples of contemporary
+// dates around New Year's Day" from
+// https://en.wikipedia.org/wiki/ISO_week_date#Relation_with_the_Gregorian_calendar
+
+assert.sameValue(cal.weekOfYear("1977-01-01"), 53, "1977-01-01 is in w53");
+assert.sameValue(cal.weekOfYear("1977-01-02"), 53, "1977-01-02 is in w53");
+assert.sameValue(cal.weekOfYear("1977-12-31"), 52, "1977-12-31 is in w52");
+assert.sameValue(cal.weekOfYear("1978-01-01"), 52, "1978-01-01 is in w52");
+assert.sameValue(cal.weekOfYear("1978-01-02"), 1, "1978-01-02 is in w01");
+assert.sameValue(cal.weekOfYear("1978-12-31"), 52, "1978-12-31 is in w52");
+assert.sameValue(cal.weekOfYear("1979-01-01"), 1, "1979-01-01 is in w01");
+assert.sameValue(cal.weekOfYear("1979-12-30"), 52, "1979-12-30 is in w52");
+assert.sameValue(cal.weekOfYear("1979-12-31"), 1, "1979-12-31 is in w01");
+assert.sameValue(cal.weekOfYear("1980-01-01"), 1, "1980-01-01 is in w01");
+assert.sameValue(cal.weekOfYear("1980-12-28"), 52, "1980-12-28 is in w52");
+assert.sameValue(cal.weekOfYear("1980-12-29"), 1, "1980-12-29 is in w01");
+assert.sameValue(cal.weekOfYear("1980-12-30"), 1, "1980-12-30 is in w01");
+assert.sameValue(cal.weekOfYear("1980-12-31"), 1, "1980-12-31 is in w01");
+assert.sameValue(cal.weekOfYear("1981-01-01"), 1, "1981-01-01 is in w01");
+assert.sameValue(cal.weekOfYear("1981-12-31"), 53, "1981-12-31 is in w53");
+assert.sameValue(cal.weekOfYear("1982-01-01"), 53, "1982-01-01 is in w53");
+assert.sameValue(cal.weekOfYear("1982-01-02"), 53, "1982-01-02 is in w53");
+assert.sameValue(cal.weekOfYear("1982-01-03"), 53, "1982-01-03 is in w53");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-wrong-type.js
new file mode 100644
index 0000000000..85aedab4fa
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/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.calendar.prototype.weekofyear
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainDate
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+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.weekOfYear(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.PlainDate, "Temporal.PlainDate, object"],
+ [Temporal.PlainDate.prototype, "Temporal.PlainDate.prototype, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.weekOfYear(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/Calendar/prototype/weekOfYear/argument-zoneddatetime-convert.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-zoneddatetime-convert.js
new file mode 100644
index 0000000000..60bc7f87ce
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-zoneddatetime-convert.js
@@ -0,0 +1,22 @@
+// |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.calendar.prototype.weekofyear
+description: An exception from TimeZone#getOffsetNanosecondsFor() is propagated.
+features: [Temporal]
+---*/
+
+class TZ extends Temporal.TimeZone {
+ constructor() { super("UTC") }
+ getOffsetNanosecondsFor() { throw new Test262Error() }
+}
+
+const tz = new TZ();
+const arg = new Temporal.ZonedDateTime(0n, tz);
+const instance = new Temporal.Calendar("iso8601");
+
+assert.throws(Test262Error, () => instance.weekOfYear(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-zoneddatetime-slots.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-zoneddatetime-slots.js
new file mode 100644
index 0000000000..f88b773cf7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-zoneddatetime-slots.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.calendar.prototype.weekofyear
+description: Getters are not called when converting a ZonedDateTime to a PlainDate.
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.ZonedDateTime.prototype);
+const getters = ["year", "month", "monthCode", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond", "calendar"];
+
+for (const property of getters) {
+ Object.defineProperty(Temporal.ZonedDateTime.prototype, property, {
+ get() {
+ actual.push(`get ${property}`);
+ const value = prototypeDescrs[property].get.call(this);
+ return {
+ toString() {
+ actual.push(`toString ${property}`);
+ return value.toString();
+ },
+ valueOf() {
+ actual.push(`valueOf ${property}`);
+ return value;
+ },
+ };
+ },
+ });
+}
+
+const arg = new Temporal.ZonedDateTime(0n, "UTC");
+const instance = new Temporal.Calendar("iso8601");
+instance.weekOfYear(arg);
+assert.compareArray(actual, []);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..5476520dbc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/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.calendar.prototype.weekofyear
+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 calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => calendar.weekOfYear(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..49cfdd2577
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/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.calendar.prototype.weekofyear
+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 calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => calendar.weekOfYear(datetime),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..546d44e176
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/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.calendar.prototype.weekofyear
+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 calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => calendar.weekOfYear(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..72f5d659de
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/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.calendar.prototype.weekofyear
+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 calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => calendar.weekOfYear(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/basic.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/basic.js
new file mode 100644
index 0000000000..1eb5ea7100
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/basic.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.calendar.prototype.weekofyear
+description: Basic tests for weekOfYear().
+features: [Temporal]
+---*/
+
+const iso = Temporal.Calendar.from("iso8601");
+const res = 44;
+assert.sameValue(iso.weekOfYear(Temporal.PlainDate.from("1994-11-05")), res, "PlainDate");
+assert.sameValue(iso.weekOfYear(Temporal.PlainDateTime.from("1994-11-05T08:15:30")), res, "PlainDateTime");
+assert.sameValue(iso.weekOfYear({ year: 1994, month: 11, day: 5 }), res, "property bag");
+assert.sameValue(iso.weekOfYear("1994-11-05"), res, "string");
+assert.throws(TypeError, () => iso.weekOfYear({ year: 2000 }), "property bag with missing properties");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/branding.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/branding.js
new file mode 100644
index 0000000000..ee012dbfa8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/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.calendar.prototype.weekofyear
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const weekOfYear = Temporal.Calendar.prototype.weekOfYear;
+
+assert.sameValue(typeof weekOfYear, "function");
+
+const args = [new Temporal.PlainDate(2021, 7, 20)];
+
+assert.throws(TypeError, () => weekOfYear.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => weekOfYear.apply(null, args), "null");
+assert.throws(TypeError, () => weekOfYear.apply(true, args), "true");
+assert.throws(TypeError, () => weekOfYear.apply("", args), "empty string");
+assert.throws(TypeError, () => weekOfYear.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => weekOfYear.apply(1, args), "1");
+assert.throws(TypeError, () => weekOfYear.apply({}, args), "plain object");
+assert.throws(TypeError, () => weekOfYear.apply(Temporal.Calendar, args), "Temporal.Calendar");
+assert.throws(TypeError, () => weekOfYear.apply(Temporal.Calendar.prototype, args), "Temporal.Calendar.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/browser.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/builtin.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/builtin.js
new file mode 100644
index 0000000000..9a59d31cc0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/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.calendar.prototype.weekofyear
+description: >
+ Tests that Temporal.Calendar.prototype.weekOfYear
+ 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.Calendar.prototype.weekOfYear),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Calendar.prototype.weekOfYear),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Calendar.prototype.weekOfYear),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Calendar.prototype.weekOfYear.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/calendar-datefromfields-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/calendar-datefromfields-called-with-options-undefined.js
new file mode 100644
index 0000000000..f4259f0eb9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/calendar-datefromfields-called-with-options-undefined.js
@@ -0,0 +1,18 @@
+// |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.calendar.prototype.weekofyear
+description: >
+ Calendar.dateFromFields method is called with undefined as the options value
+ when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
+calendar.weekOfYear({ year: 2000, month: 5, day: 3, calendar });
+assert.sameValue(calendar.dateFromFieldsCallCount, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/calendar-fields-iterable.js
new file mode 100644
index 0000000000..b243c771e7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/calendar-fields-iterable.js
@@ -0,0 +1,35 @@
+// |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.calendar.prototype.weekofyear
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.calendar.prototype.weekofyear step 4:
+ 4. Let _date_ be ? ToTemporalDate(_dateOrDateTime_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToListOfType(_fieldsArray_, « String »).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+calendar1.weekOfYear({ year: 2000, month: 5, day: 2, calendar: calendar2 });
+
+assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/calendar-temporal-object.js
new file mode 100644
index 0000000000..3f2769d5b3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/calendar-temporal-object.js
@@ -0,0 +1,29 @@
+// |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.calendar.prototype.weekofyear
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.calendar.prototype.weekofyear step 4:
+ 4. Let _date_ be ? ToTemporalDate(_dateOrDateTime_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const calendar = new Temporal.Calendar("iso8601");
+ calendar.weekOfYear({ year: 2000, month: 5, day: 2, calendar: temporalObject });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/cross-year.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/cross-year.js
new file mode 100644
index 0000000000..d65ef91a26
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/cross-year.js
@@ -0,0 +1,15 @@
+// |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.calendar.prototype.weekofyear
+description: weekOfYear() crossing year boundaries.
+features: [Temporal]
+---*/
+
+const iso = Temporal.Calendar.from("iso8601");
+assert.sameValue(iso.weekOfYear(Temporal.PlainDate.from("2019-12-31")), 1, "week 1 from next year");
+assert.sameValue(iso.weekOfYear(Temporal.PlainDate.from("2021-01-01")), 53, "week 1 from next year");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..f1724eb32d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/infinity-throws-rangeerror.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.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.calendar.prototype.weekofyear
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+const base = { year: 2000, month: 5, day: 2 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.weekOfYear({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.weekOfYear({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/length.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/length.js
new file mode 100644
index 0000000000..97c6fb0a30
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/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.calendar.prototype.weekofyear
+description: Temporal.Calendar.prototype.weekOfYear.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.Calendar.prototype.weekOfYear, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/name.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/name.js
new file mode 100644
index 0000000000..91d4d267a5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/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.calendar.prototype.weekofyear
+description: Temporal.Calendar.prototype.weekOfYear.name is "weekOfYear".
+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.Calendar.prototype.weekOfYear, "name", {
+ value: "weekOfYear",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/not-a-constructor.js
new file mode 100644
index 0000000000..21889a2671
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/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.calendar.prototype.weekofyear
+description: >
+ Temporal.Calendar.prototype.weekOfYear 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.Calendar.prototype.weekOfYear();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Calendar.prototype.weekOfYear), false,
+ "isConstructor(Temporal.Calendar.prototype.weekOfYear)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/prop-desc.js
new file mode 100644
index 0000000000..31a0f5e4fb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/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.calendar.prototype.weekofyear
+description: The "weekOfYear" property of Temporal.Calendar.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Calendar.prototype.weekOfYear,
+ "function",
+ "`typeof Calendar.prototype.weekOfYear` is `function`"
+);
+
+verifyProperty(Temporal.Calendar.prototype, "weekOfYear", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/shell.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/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/Calendar/prototype/weekOfYear/year-zero.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/year-zero.js
new file mode 100644
index 0000000000..e027bfca5c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/weekOfYear/year-zero.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.calendar.prototype.weekofyear
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T00:45",
+ "-000000-10-31T00:45+01:00",
+ "-000000-10-31T00:45+00:00[UTC]",
+];
+const instance = new Temporal.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.weekOfYear(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..ca4afbce33
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-builtin-calendar-no-array-iteration.js
@@ -0,0 +1,24 @@
+// |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.calendar.prototype.year
+description: >
+ Calling the method with a property bag argument with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.Calendar("iso8601");
+const arg = { year: 2000, month: 5, day: 2, calendar: "iso8601" };
+instance.year(arg);
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-calendar-datefromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..c5b697a625
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-calendar-datefromfields-called-with-null-prototype-fields.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.calendar.prototype.year
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.Calendar("iso8601");
+const arg = { year: 2000, month: 5, day: 2, calendar };
+instance.year(arg);
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..cbf629b522
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-constructor-in-calendar-fields.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.calendar.prototype.year
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.Calendar("iso8601");
+
+assert.throws(RangeError, () => instance.year(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-duplicate-calendar-fields.js
new file mode 100644
index 0000000000..d9ef764654
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-duplicate-calendar-fields.js
@@ -0,0 +1,19 @@
+// |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.calendar.prototype.year
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['day'], ['month'], ['monthCode'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar };
+ const instance = new Temporal.Calendar("iso8601");
+
+ assert.throws(RangeError, () => instance.year(arg));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-leap-second.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-leap-second.js
new file mode 100644
index 0000000000..ed9d642ec9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-leap-second.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.calendar.prototype.year
+description: Leap second is a valid ISO string for PlainDate
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+let arg = "2016-12-31T23:59:60";
+const result1 = instance.year(arg);
+assert.sameValue(
+ result1,
+ 2016,
+ "leap second is a valid ISO string for PlainDate"
+);
+
+arg = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+const result2 = instance.year(arg);
+assert.sameValue(
+ result2,
+ 2016,
+ "second: 60 is ignored in property bag for PlainDate"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-number.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-number.js
new file mode 100644
index 0000000000..109a5ff177
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/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.calendar.prototype.year
+description: A number cannot be used in place of a Temporal.PlainDate
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const numbers = [
+ 1,
+ 19761118,
+ -19761118,
+ 1234567890,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => instance.year(arg),
+ 'Numbers cannot be used in place of an ISO string for PlainDate'
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-propertybag-calendar-case-insensitive.js
new file mode 100644
index 0000000000..c0eb531ee1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-propertybag-calendar-case-insensitive.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.calendar.prototype.year
+description: The calendar name is case-insensitive
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const calendar = "IsO8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.year(arg);
+assert.sameValue(result, 1976, "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-propertybag-calendar-leap-second.js
new file mode 100644
index 0000000000..6079a33b9c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-propertybag-calendar-leap-second.js
@@ -0,0 +1,23 @@
+// |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.calendar.prototype.year
+description: Leap second is a valid ISO string for a calendar in a property bag
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const calendar = "2016-12-31T23:59:60";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.year(arg);
+assert.sameValue(
+ result,
+ 1976,
+ "leap second is a valid ISO string for calendar"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-propertybag-calendar-number.js
new file mode 100644
index 0000000000..7ab6fa7b56
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-propertybag-calendar-number.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.calendar.prototype.year
+description: A number as calendar in a property bag is not accepted
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const numbers = [
+ 1,
+ 19970327,
+ -19970327,
+ 1234567890,
+];
+
+for (const calendar of numbers) {
+ const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+ assert.throws(
+ TypeError,
+ () => instance.year(arg),
+ "Numbers cannot be used as a calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-propertybag-calendar-string.js
new file mode 100644
index 0000000000..ebd1c87cb4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-propertybag-calendar-string.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.calendar.prototype.year
+description: A calendar ID is valid input for Calendar
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const calendar = "iso8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.year(arg);
+assert.sameValue(result, 1976, `Calendar created from string "${calendar}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-propertybag-calendar-wrong-type.js
new file mode 100644
index 0000000000..803b29f747
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-propertybag-calendar-wrong-type.js
@@ -0,0 +1,46 @@
+// |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.calendar.prototype.year
+description: >
+ Appropriate error thrown when a calendar property from a property bag cannot
+ be converted to a calendar object or string
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.Calendar("iso8601");
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [calendar, description] of primitiveTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ typeof calendar === 'string' ? RangeError : TypeError,
+ () => instance.year(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+ [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields()
+];
+
+for (const [calendar, description] of typeErrorTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(TypeError, () => instance.year(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/Calendar/prototype/year/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-propertybag-calendar-year-zero.js
new file mode 100644
index 0000000000..742326e600
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-propertybag-calendar-year-zero.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.calendar.prototype.year
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T17:45",
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+01:00",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+const instance = new Temporal.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.year(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-proto-in-calendar-fields.js
new file mode 100644
index 0000000000..9f3120fdcd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-proto-in-calendar-fields.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.calendar.prototype.year
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.Calendar("iso8601");
+
+assert.throws(RangeError, () => instance.year(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..b43409dfec
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-string-calendar-annotation.js
@@ -0,0 +1,33 @@
+// |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.calendar.prototype.year
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02[u-ca=iso8601]", "without time or time zone"],
+ ["2000-05-02[UTC][u-ca=iso8601]", "with time zone and no time"],
+ ["2000-05-02T15:23[u-ca=iso8601]", "without time zone"],
+ ["2000-05-02T15:23[UTC][u-ca=iso8601]", "with time zone"],
+ ["2000-05-02T15:23[!u-ca=iso8601]", "with ! and no time zone"],
+ ["2000-05-02T15:23[UTC][!u-ca=iso8601]", "with ! and time zone"],
+ ["2000-05-02T15:23[u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+const instance = new Temporal.Calendar("iso8601");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.year(arg);
+
+ assert.sameValue(
+ result,
+ 2000,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..054e3e1151
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-string-critical-unknown-annotation.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.calendar.prototype.year
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[!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.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.year(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..a8d1259e03
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-string-date-with-utc-offset.js
@@ -0,0 +1,48 @@
+// |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.calendar.prototype.year
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const validStrings = [
+ "2000-05-02T00+00:00",
+ "2000-05-02T00+00:00[UTC]",
+ "2000-05-02T00+00:00[!UTC]",
+ "2000-05-02T00-02:30[America/St_Johns]",
+];
+
+for (const arg of validStrings) {
+ const result = instance.year(arg);
+
+ assert.sameValue(
+ result,
+ 2000,
+ `"${arg}" is a valid UTC offset with time for PlainDate`
+ );
+}
+
+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.year(arg),
+ `"${arg}" UTC offset without time is not valid for PlainDate`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-string-invalid.js
new file mode 100644
index 0000000000..625844661f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-string-invalid.js
@@ -0,0 +1,64 @@
+// |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.calendar.prototype.year
+description: >
+ RangeError thrown if an invalid ISO string (or syntactically valid ISO string
+ that is not supported) is used as a PlainDate
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ // invalid ISO strings:
+ "",
+ "invalid iso8601",
+ "2020-01-00",
+ "2020-01-32",
+ "2020-02-30",
+ "2021-02-29",
+ "2020-00-01",
+ "2020-13-01",
+ "2020-01-01T",
+ "2020-01-01T25:00:00",
+ "2020-01-01T01:60:00",
+ "2020-01-01T01:60:61",
+ "2020-01-01junk",
+ "2020-01-01T00:00:00junk",
+ "2020-01-01T00:00:00+00:00junk",
+ "2020-01-01T00:00:00+00:00[UTC]junk",
+ "2020-01-01T00:00:00+00:00[UTC][u-ca=iso8601]junk",
+ "02020-01-01",
+ "2020-001-01",
+ "2020-01-001",
+ "2020-01-01T001",
+ "2020-01-01T01:001",
+ "2020-01-01T01:01:001",
+ // valid, but forms not supported in Temporal:
+ "2020-W01-1",
+ "2020-001",
+ "+0002020-01-01",
+ // valid, but this calendar must not exist:
+ "2020-01-01[u-ca=notexist]",
+ // may be valid in other contexts, but insufficient information for PlainDate:
+ "2020-01",
+ "+002020-01",
+ "01-01",
+ "2020-W01",
+ "P1Y",
+ "-P12Y",
+ // valid, but outside the supported range:
+ "-999999-01-01",
+ "+999999-01-01",
+];
+const instance = new Temporal.Calendar("iso8601");
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => instance.year(arg),
+ `"${arg}" should not be a valid ISO string for a PlainDate`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..41f297f57e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/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.calendar.prototype.year
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[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.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.year(arg),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..f1f47b87b7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-string-multiple-time-zone.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.calendar.prototype.year
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[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.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.year(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-string-time-separators.js
new file mode 100644
index 0000000000..ee9b15226c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-string-time-separators.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.calendar.prototype.year
+description: Time separator in string argument can vary
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02T15:23", "uppercase T"],
+ ["2000-05-02t15:23", "lowercase T"],
+ ["2000-05-02 15:23", "space between date and time"],
+];
+
+const instance = new Temporal.Calendar("iso8601");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.year(arg);
+
+ assert.sameValue(
+ result,
+ 2000,
+ `variant time separators (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..144b514253
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-string-time-zone-annotation.js
@@ -0,0 +1,38 @@
+// |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.calendar.prototype.year
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02[Asia/Kolkata]", "named, with no time"],
+ ["2000-05-02[!Europe/Vienna]", "named, with ! and no time"],
+ ["2000-05-02[+00:00]", "numeric, with no time"],
+ ["2000-05-02[!-02:30]", "numeric, with ! and no time"],
+ ["2000-05-02T15:23[America/Sao_Paulo]", "named, with no offset"],
+ ["2000-05-02T15:23[!Asia/Tokyo]", "named, with ! and no offset"],
+ ["2000-05-02T15:23[-02:30]", "numeric, with no offset"],
+ ["2000-05-02T15:23[!+00:00]", "numeric, with ! and no offset"],
+ ["2000-05-02T15:23+00:00[America/New_York]", "named, with offset"],
+ ["2000-05-02T15:23+00:00[!UTC]", "named, with offset and !"],
+ ["2000-05-02T15:23+00:00[+01:00]", "numeric, with offset"],
+ ["2000-05-02T15:23+00:00[!-08:00]", "numeric, with offset and !"],
+];
+
+const instance = new Temporal.Calendar("iso8601");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.year(arg);
+
+ assert.sameValue(
+ result,
+ 2000,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..b95e97476c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-string-unknown-annotation.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.calendar.prototype.year
+description: Various forms of unknown annotation
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02[foo=bar]", "without time"],
+ ["2000-05-02T15:23[foo=bar]", "alone"],
+ ["2000-05-02T15:23[UTC][foo=bar]", "with time zone"],
+ ["2000-05-02T15:23[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["2000-05-02T15:23[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["2000-05-02T15:23[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+const instance = new Temporal.Calendar("iso8601");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.year(arg);
+
+ assert.sameValue(
+ result,
+ 2000,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..3f690f9936
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-string-with-utc-designator.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.calendar.prototype.year
+description: RangeError thrown if a string with UTC designator is used as a PlainDate
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "2019-10-01T09:00:00Z",
+ "2019-10-01T09:00:00Z[UTC]",
+];
+const instance = new Temporal.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.year(arg),
+ "String with UTC designator should not be valid as a PlainDate"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-wrong-type.js
new file mode 100644
index 0000000000..dbce82cb1f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/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.calendar.prototype.year
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainDate
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+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.year(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.PlainDate, "Temporal.PlainDate, object"],
+ [Temporal.PlainDate.prototype, "Temporal.PlainDate.prototype, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.year(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/Calendar/prototype/year/argument-zoneddatetime-convert.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-zoneddatetime-convert.js
new file mode 100644
index 0000000000..575167a02e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-zoneddatetime-convert.js
@@ -0,0 +1,22 @@
+// |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.calendar.prototype.year
+description: An exception from TimeZone#getOffsetNanosecondsFor() is propagated.
+features: [Temporal]
+---*/
+
+class TZ extends Temporal.TimeZone {
+ constructor() { super("UTC") }
+ getOffsetNanosecondsFor() { throw new Test262Error() }
+}
+
+const tz = new TZ();
+const arg = new Temporal.ZonedDateTime(0n, tz);
+const instance = new Temporal.Calendar("iso8601");
+
+assert.throws(Test262Error, () => instance.year(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-zoneddatetime-slots.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-zoneddatetime-slots.js
new file mode 100644
index 0000000000..c3221920bc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-zoneddatetime-slots.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.calendar.prototype.year
+description: Getters are not called when converting a ZonedDateTime to a PlainDate.
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.ZonedDateTime.prototype);
+const getters = ["year", "month", "monthCode", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond", "calendar"];
+
+for (const property of getters) {
+ Object.defineProperty(Temporal.ZonedDateTime.prototype, property, {
+ get() {
+ actual.push(`get ${property}`);
+ const value = prototypeDescrs[property].get.call(this);
+ return {
+ toString() {
+ actual.push(`toString ${property}`);
+ return value.toString();
+ },
+ valueOf() {
+ actual.push(`valueOf ${property}`);
+ return value;
+ },
+ };
+ },
+ });
+}
+
+const arg = new Temporal.ZonedDateTime(0n, "UTC");
+const instance = new Temporal.Calendar("iso8601");
+instance.year(arg);
+assert.compareArray(actual, []);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..71aef5ef62
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/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.calendar.prototype.year
+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 calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => calendar.year(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..fd036d070c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/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.calendar.prototype.year
+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 calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => calendar.year(datetime),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..5b11ad3a9c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/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.calendar.prototype.year
+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 calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => calendar.year(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..071389f0ed
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/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.calendar.prototype.year
+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 calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => calendar.year(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/basic.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/basic.js
new file mode 100644
index 0000000000..65bc6c05c6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/basic.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.calendar.prototype.year
+description: Basic tests for year().
+features: [Temporal]
+---*/
+
+const iso = Temporal.Calendar.from("iso8601");
+const res = 1994;
+assert.sameValue(iso.year(Temporal.PlainDate.from("1994-11-05")), res, "PlainDate");
+assert.sameValue(iso.year(Temporal.PlainDateTime.from("1994-11-05T08:15:30")), res, "PlainDateTime");
+assert.sameValue(iso.year(Temporal.PlainYearMonth.from("1994-11")), res, "PlainYearMonth");
+assert.sameValue(iso.year({ year: 1994, month: 11, day: 5 }), res, "property bag");
+assert.sameValue(iso.year("1994-11-05"), res, "string");
+assert.throws(TypeError, () => iso.year({ month: 5 }), "property bag with missing properties");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/branding.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/branding.js
new file mode 100644
index 0000000000..a0d24974fb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/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.calendar.prototype.year
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const year = Temporal.Calendar.prototype.year;
+
+assert.sameValue(typeof year, "function");
+
+const args = [new Temporal.PlainDate(2000, 1, 1)];
+
+assert.throws(TypeError, () => year.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => year.apply(null, args), "null");
+assert.throws(TypeError, () => year.apply(true, args), "true");
+assert.throws(TypeError, () => year.apply("", args), "empty string");
+assert.throws(TypeError, () => year.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => year.apply(1, args), "1");
+assert.throws(TypeError, () => year.apply({}, args), "plain object");
+assert.throws(TypeError, () => year.apply(Temporal.Calendar, args), "Temporal.Calendar");
+assert.throws(TypeError, () => year.apply(Temporal.Calendar.prototype, args), "Temporal.Calendar.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/browser.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/builtin.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/builtin.js
new file mode 100644
index 0000000000..8f6eda4a10
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/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.calendar.prototype.year
+description: >
+ Tests that Temporal.Calendar.prototype.year
+ 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.Calendar.prototype.year),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Calendar.prototype.year),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Calendar.prototype.year),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Calendar.prototype.year.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/calendar-datefromfields-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/calendar-datefromfields-called-with-options-undefined.js
new file mode 100644
index 0000000000..f2b94e2c0c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/calendar-datefromfields-called-with-options-undefined.js
@@ -0,0 +1,18 @@
+// |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.calendar.prototype.year
+description: >
+ Calendar.dateFromFields method is called with undefined as the options value
+ when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
+calendar.year({ year: 2000, month: 5, day: 3, calendar });
+assert.sameValue(calendar.dateFromFieldsCallCount, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/calendar-fields-iterable.js
new file mode 100644
index 0000000000..eb5babfc8b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/calendar-fields-iterable.js
@@ -0,0 +1,37 @@
+// |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.calendar.prototype.year
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.calendar.prototype.year step 4:
+ 4. Return ? ISOYear(_dateOrDateTime_).
+ sec-temporal-isoyear step 1.a:
+ a. Set _dateOrDateTime_ to ? ToTemporalDate(_dateOrDateTime_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToListOfType(_fieldsArray_, « String »).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+calendar1.year({ year: 2000, month: 5, day: 2, calendar: calendar2 });
+
+assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/calendar-temporal-object.js
new file mode 100644
index 0000000000..6e1edc8bb5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/calendar-temporal-object.js
@@ -0,0 +1,31 @@
+// |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.calendar.prototype.dayofweek
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.calendar.prototype.year step 4:
+ 4. Return ? ISOYear(_dateOrDateTime_).
+ sec-temporal-isoyear step 1.a:
+ a. Set _dateOrDateTime_ to ? ToTemporalDate(_dateOrDateTime_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const calendar = new Temporal.Calendar("iso8601");
+ calendar.year({ year: 2000, month: 5, day: 2, calendar: temporalObject });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/date-time.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/date-time.js
new file mode 100644
index 0000000000..79d01e7c59
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/date-time.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.year
+description: >
+ Temporal.Calendar.prototype.year will take PlainDateTime and return
+ the value of the year.
+info: |
+ 1. Let calendar be the this value.
+ 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
+ 3. Assert: calendar.[[Identifier]] is "iso8601".
+ 4. If Type(temporalDateLike) is not Object or temporalDateLike does not have an [[InitializedTemporalDate]] or [[InitializedTemporalYearMonth]] internal slot, then
+ a. Set temporalDateLike to ? ToTemporalDate(temporalDateLike).
+ 5. Return ! ISOYear(temporalDateLike).
+features: [Temporal]
+---*/
+let cal = new Temporal.Calendar("iso8601");
+
+let dateTime = new Temporal.PlainDateTime(1997, 8, 23, 5, 30, 13)
+assert.sameValue(cal.year(dateTime), 1997, 'cal.year(new Temporal.PlainDateTime(1997, 8, 23, 5, 30, 13)) must return 1997');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/date.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/date.js
new file mode 100644
index 0000000000..22a7117552
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/date.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.year
+description: >
+ Temporal.Calendar.prototype.year will take PlainDate and return
+ the value of the year.
+info: |
+ 1. Let calendar be the this value.
+ 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
+ 3. Assert: calendar.[[Identifier]] is "iso8601".
+ 4. If Type(temporalDateLike) is not Object or temporalDateLike does not have an [[InitializedTemporalDate]] or [[InitializedTemporalYearMonth]] internal slot, then
+ a. Set temporalDateLike to ? ToTemporalDate(temporalDateLike).
+ 5. Return ! ISOYear(temporalDateLike).
+features: [Temporal]
+---*/
+let cal = new Temporal.Calendar("iso8601");
+
+let date = new Temporal.PlainDate(2021, 7, 15);
+assert.sameValue(cal.year(date), 2021, 'cal.year(new Temporal.PlainDate(2021, 7, 15)) must return 2021');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..e8cfec61d6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/infinity-throws-rangeerror.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.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.calendar.prototype.year
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+const base = { year: 2000, month: 5, day: 2 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.year({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.year({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/length.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/length.js
new file mode 100644
index 0000000000..7e336f251c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/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.calendar.prototype.year
+description: Temporal.Calendar.prototype.year.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.Calendar.prototype.year, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/name.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/name.js
new file mode 100644
index 0000000000..a7205e1c1a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/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.calendar.prototype.year
+description: Temporal.Calendar.prototype.year.name is "year".
+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.Calendar.prototype.year, "name", {
+ value: "year",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/not-a-constructor.js
new file mode 100644
index 0000000000..f1a6e11107
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/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.calendar.prototype.year
+description: >
+ Temporal.Calendar.prototype.year 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.Calendar.prototype.year();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Calendar.prototype.year), false,
+ "isConstructor(Temporal.Calendar.prototype.year)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/prop-desc.js
new file mode 100644
index 0000000000..d6caba2847
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/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.calendar.prototype.year
+description: The "year" property of Temporal.Calendar.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Calendar.prototype.year,
+ "function",
+ "`typeof Calendar.prototype.year` is `function`"
+);
+
+verifyProperty(Temporal.Calendar.prototype, "year", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/shell.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/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/Calendar/prototype/year/string.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/string.js
new file mode 100644
index 0000000000..4774f958ca
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/string.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.year
+description: >
+ Temporal.Calendar.prototype.year will take ISO8601 string and return
+ the value of the year.
+info: |
+ 1. Let calendar be the this value.
+ 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
+ 3. Assert: calendar.[[Identifier]] is "iso8601".
+ 4. If Type(temporalDateLike) is not Object or temporalDateLike does not have an [[InitializedTemporalDate]] or [[InitializedTemporalYearMonth]] internal slot, then
+ a. Set temporalDateLike to ? ToTemporalDate(temporalDateLike).
+ 5. Return ! ISOYear(temporalDateLike).
+features: [Temporal]
+---*/
+let cal = new Temporal.Calendar("iso8601");
+
+assert.sameValue(cal.year("2019-03-15"), 2019, 'cal.year("2019-03-15") must return 2019');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/throw-range-error-ToTemporalDate.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/throw-range-error-ToTemporalDate.js
new file mode 100644
index 0000000000..fa86c42a0a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/throw-range-error-ToTemporalDate.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.year
+description: >
+ Temporal.Calendar.prototype.year throws RangeError on
+ ToTemporalDate when temporalDateLike is invalid string.
+info: |
+ 4. If Type(temporalDateLike) is not Object or temporalDateLike
+ does not have an [[InitializedTemporalDate]] or
+ [[InitializedTemporalYearMonth]] internal slot, then
+ a. Set temporalDateLike to ? ToTemporalDate(temporalDateLike).
+features: [Temporal, arrow-function]
+---*/
+let cal = new Temporal.Calendar("iso8601");
+
+assert.throws(RangeError, () => cal.year("invalid string"),
+ 'cal.year("invalid string") throws a RangeError exception');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/year-month.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/year-month.js
new file mode 100644
index 0000000000..dfa6c299f4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/year-month.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.year
+description: >
+ Temporal.Calendar.prototype.year will take PlainYearMonth and return
+ the value of the year.
+info: |
+ 1. Let calendar be the this value.
+ 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
+ 3. Assert: calendar.[[Identifier]] is "iso8601".
+ 4. If Type(temporalDateLike) is not Object or temporalDateLike does not have an [[InitializedTemporalDate]] or [[InitializedTemporalYearMonth]] internal slot, then
+ a. Set temporalDateLike to ? ToTemporalDate(temporalDateLike).
+ 5. Return ! ISOYear(temporalDateLike).
+features: [Temporal]
+---*/
+let cal = new Temporal.Calendar("iso8601");
+
+let yearMonth = new Temporal.PlainYearMonth(1999, 6);
+assert.sameValue(cal.year(yearMonth), 1999, 'cal.year(new Temporal.PlainYearMonth(1999, 6)) must return 1999');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/year-zero.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/year-zero.js
new file mode 100644
index 0000000000..2185c1b630
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/year/year-zero.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.calendar.prototype.year
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T00:45",
+ "-000000-10-31T00:45+01:00",
+ "-000000-10-31T00:45+00:00[UTC]",
+];
+const instance = new Temporal.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.year(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/basic.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/basic.js
new file mode 100644
index 0000000000..65e6b5888f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/basic.js
@@ -0,0 +1,43 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.yearmonthfromfields
+description: Temporal.Calendar.prototype.yearMonthFromFields return correctly with valid data.
+info: |
+ 1. Let calendar be the this value.
+ 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
+ 3. Assert: calendar.[[Identifier]] is "iso8601".
+ 4. If Type(fields) is not Object, throw a TypeError exception.
+ 5. Set options to ? GetOptionsObject(options).
+ 6. Let result be ? ISOYearMonthFromFields(fields, options).
+ 7. Return ? CreateTemporalYearMonth(result.[[Year]], result.[[Month]], calendar, result.[[ReferenceISODay]]).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const cal = new Temporal.Calendar("iso8601")
+
+let result = cal.yearMonthFromFields({ year: 2021, month: 7 });
+TemporalHelpers.assertPlainYearMonth(result, 2021, 7, "M07", "year 2021, month 7");
+result = cal.yearMonthFromFields({ year: 2021, month: 12 });
+TemporalHelpers.assertPlainYearMonth(result, 2021, 12, "M12", "year 2021, month 12");
+result = cal.yearMonthFromFields({ year: 2021, monthCode: "M07" });
+TemporalHelpers.assertPlainYearMonth(result, 2021, 7, "M07", "year 2021, monthCode M07");
+result = cal.yearMonthFromFields({ year: 2021, monthCode: "M12" });
+TemporalHelpers.assertPlainYearMonth(result, 2021, 12, "M12", "year 2021, monthCode M12");
+
+["constrain", "reject"].forEach((overflow) => {
+ const opt = { overflow };
+ result = cal.yearMonthFromFields({ year: 2021, month: 7 }, opt);
+ TemporalHelpers.assertPlainYearMonth(result, 2021, 7, "M07", `year 2021, month 7, overflow ${overflow}`);
+ result = cal.yearMonthFromFields({ year: 2021, month: 12 }, opt);
+ TemporalHelpers.assertPlainYearMonth(result, 2021, 12, "M12", `year 2021, month 12, overflow ${overflow}`);
+ result = cal.yearMonthFromFields({ year: 2021, monthCode: "M07" }, opt);
+ TemporalHelpers.assertPlainYearMonth(result, 2021, 7, "M07", `year 2021, monthCode M07, overflow ${overflow}`);
+ result = cal.yearMonthFromFields({ year: 2021, monthCode: "M12" }, opt);
+ TemporalHelpers.assertPlainYearMonth(result, 2021, 12, "M12", `year 2021, monthCode M12, overflow ${overflow}`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/branding.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/branding.js
new file mode 100644
index 0000000000..62dd196840
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/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.calendar.prototype.yearmonthfromfields
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const yearMonthFromFields = Temporal.Calendar.prototype.yearMonthFromFields;
+
+assert.sameValue(typeof yearMonthFromFields, "function");
+
+const args = [{ year: 2021, month: 1 }];
+
+assert.throws(TypeError, () => yearMonthFromFields.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => yearMonthFromFields.apply(null, args), "null");
+assert.throws(TypeError, () => yearMonthFromFields.apply(true, args), "true");
+assert.throws(TypeError, () => yearMonthFromFields.apply("", args), "empty string");
+assert.throws(TypeError, () => yearMonthFromFields.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => yearMonthFromFields.apply(1, args), "1");
+assert.throws(TypeError, () => yearMonthFromFields.apply({}, args), "plain object");
+assert.throws(TypeError, () => yearMonthFromFields.apply(Temporal.Calendar, args), "Temporal.Calendar");
+assert.throws(TypeError, () => yearMonthFromFields.apply(Temporal.Calendar.prototype, args), "Temporal.Calendar.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/browser.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/builtin.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/builtin.js
new file mode 100644
index 0000000000..0de5aad315
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/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.calendar.prototype.yearmonthfromfields
+description: >
+ Tests that Temporal.Calendar.prototype.yearMonthFromFields
+ 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.Calendar.prototype.yearMonthFromFields),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Calendar.prototype.yearMonthFromFields),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Calendar.prototype.yearMonthFromFields),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Calendar.prototype.yearMonthFromFields.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/fields-missing-properties.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/fields-missing-properties.js
new file mode 100644
index 0000000000..528ea2998e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/fields-missing-properties.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.yearmonthfromfields
+description: Temporal.Calendar.prototype.yearMonthFromFields will throw TypeError with incorrect input data type.
+info: |
+ 1. Let calendar be the this value.
+ 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
+ 3. Assert: calendar.[[Identifier]] is "iso8601".
+ 4. If Type(fields) is not Object, throw a TypeError exception.
+ 5. Set options to ? GetOptionsObject(options).
+ 6. Let result be ? ISOYearMonthFromFields(fields, options).
+ 7. Return ? CreateTemporalYearMonth(result.[[Year]], result.[[Month]], calendar, result.[[ReferenceISODay]]).
+features: [Temporal]
+---*/
+
+const cal = new Temporal.Calendar("iso8601")
+
+assert.throws(TypeError, () => cal.yearMonthFromFields({}), "at least one correctly spelled property is required");
+assert.throws(TypeError, () => cal.yearMonthFromFields({ month: 1 }), "year is required");
+assert.throws(TypeError, () => cal.yearMonthFromFields({ year: 2021 }), "month or monthCode is required");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/fields-not-object.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/fields-not-object.js
new file mode 100644
index 0000000000..a8af3e6628
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/fields-not-object.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.calendar.prototype.yearmonthfromfields
+description: Throw a TypeError if the fields is not an object
+features: [Symbol, Temporal]
+---*/
+
+const tests = [undefined, null, true, false, "string", Symbol("sym"), Math.PI, Infinity, NaN, 42n];
+const iso = Temporal.Calendar.from("iso8601");
+for (const fields of tests) {
+ assert.throws(TypeError, () => iso.yearMonthFromFields(fields, {}));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..7c7f33f3a9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/infinity-throws-rangeerror.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.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.calendar.prototype.yearmonthfromfields
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+const base = { year: 2000, month: 5 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month"].forEach((prop) => {
+ ["constrain", "reject"].forEach((overflow) => {
+ assert.throws(RangeError, () => instance.yearMonthFromFields({ ...base, [prop]: inf }, { overflow }), `${prop} property cannot be ${inf} (overflow ${overflow}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.yearMonthFromFields({ ...base, [prop]: obj }, { overflow }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/length.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/length.js
new file mode 100644
index 0000000000..b87ac41bb3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/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.calendar.prototype.yearmonthfromfields
+description: Temporal.Calendar.prototype.yearMonthFromFields.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.Calendar.prototype.yearMonthFromFields, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/missing-properties.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/missing-properties.js
new file mode 100644
index 0000000000..9de556a653
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/missing-properties.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.calendar.prototype.yearmonthfromfields
+description: Errors due to missing properties on fields object are thrown in the correct order
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+let getMonth = false;
+let getMonthCode = false;
+const missingYearAndMonth = {
+ get month() {
+ getMonth = true;
+ },
+ get monthCode() {
+ getMonthCode = true;
+ },
+};
+assert.throws(TypeError, () => instance.yearMonthFromFields(missingYearAndMonth), "year should be checked after fetching but before resolving the month");
+assert(getMonth, "year is fetched after month");
+assert(getMonthCode, "year is fetched after monthCode");
+
+assert.throws(TypeError, () => instance.yearMonthFromFields({ year: 2000 }), "month should be resolved last");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/monthcode-invalid.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/monthcode-invalid.js
new file mode 100644
index 0000000000..41ca759058
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/monthcode-invalid.js
@@ -0,0 +1,34 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.yearmonthfromfields
+description: Throw RangeError for an out-of-range, conflicting, or ill-formed monthCode
+info: |
+ 1. Let calendar be the this value.
+ 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
+ 3. Assert: calendar.[[Identifier]] is "iso8601".
+ 4. If Type(fields) is not Object, throw a TypeError exception.
+ 5. Set options to ? GetOptionsObject(options).
+ 6. Let result be ? ISOYearMonthFromFields(fields, options).
+ 7. Return ? CreateTemporalYearMonth(result.[[Year]], result.[[Month]], calendar, result.[[ReferenceISODay]]).
+features: [Temporal]
+---*/
+
+const cal = new Temporal.Calendar("iso8601");
+
+["m1", "M1", "m01"].forEach((monthCode) => {
+ assert.throws(RangeError, () => cal.yearMonthFromFields({ year: 2021, monthCode }),
+ `monthCode '${monthCode}' is not well-formed`);
+});
+
+assert.throws(RangeError, () => cal.yearMonthFromFields({ year: 2021, month: 12, monthCode: "M11" }),
+ "monthCode and month conflict");
+
+["M00", "M19", "M99", "M13"].forEach((monthCode) => {
+ assert.throws(RangeError, () => cal.yearMonthFromFields({ year: 2021, monthCode }),
+ `monthCode '${monthCode}' is not valid for year 2021`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/name.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/name.js
new file mode 100644
index 0000000000..70c404a728
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/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.calendar.prototype.yearmonthfromfields
+description: Temporal.Calendar.prototype.yearMonthFromFields.name is "yearMonthFromFields".
+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.Calendar.prototype.yearMonthFromFields, "name", {
+ value: "yearMonthFromFields",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/not-a-constructor.js
new file mode 100644
index 0000000000..58c175cb8b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/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.calendar.prototype.yearmonthfromfields
+description: >
+ Temporal.Calendar.prototype.yearMonthFromFields 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.Calendar.prototype.yearMonthFromFields();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Calendar.prototype.yearMonthFromFields), false,
+ "isConstructor(Temporal.Calendar.prototype.yearMonthFromFields)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/one-of-era-erayear-undefined.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/one-of-era-erayear-undefined.js
new file mode 100644
index 0000000000..6696311237
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/one-of-era-erayear-undefined.js
@@ -0,0 +1,19 @@
+// |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.calendar.prototype.yearMonthFromFields
+description: Does not throw a RangeError if only one of era/eraYear fields is present
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const base = { year: 2000, month: 5, day: 2, era: 'ce' };
+const instance = new Temporal.Calendar('iso8601');
+TemporalHelpers.assertPlainYearMonth(instance.yearMonthFromFields({ ...base }), 2000, 5, 'M05');
+
+const base2 = { year: 2000, month: 5, day: 2, eraYear: 1 };
+TemporalHelpers.assertPlainYearMonth(instance.yearMonthFromFields({ ...base }), 2000, 5, 'M05');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/options-not-object.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/options-not-object.js
new file mode 100644
index 0000000000..65df607e7f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/options-not-object.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.yearmonthfromfields
+description: Throw a TypeError if options is not an object or undefined
+info: |
+ 5. Set options to ? GetOptionsObject(options).
+features: [Symbol, Temporal]
+---*/
+
+const tests = [null, true, false, "string", Symbol("sym"), Math.PI, Infinity, NaN, 42n];
+const iso = new Temporal.Calendar("iso8601");
+for (const options of tests) {
+ assert.throws(TypeError, () => iso.yearMonthFromFields({ year: 2021, month: 7 }, options),
+ "options is not object");
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/options-object.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/options-object.js
new file mode 100644
index 0000000000..4fc1e15706
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/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.calendar.prototype.yearmonthfromfields
+description: Empty or a function object may be used as options
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const result1 = instance.yearMonthFromFields({ year: 2000, monthCode: "M05" }, {});
+TemporalHelpers.assertPlainYearMonth(
+ result1, 2000, 5, "M05",
+ "options may be an empty plain object"
+);
+
+const result2 = instance.yearMonthFromFields({ year: 2000, monthCode: "M05" }, () => {});
+TemporalHelpers.assertPlainYearMonth(
+ result2, 2000, 5, "M05",
+ "options may be a function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/options-wrong-type.js
new file mode 100644
index 0000000000..4c0d750067
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/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.calendar.prototype.yearmonthfromfields
+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.Calendar("iso8601");
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.yearMonthFromFields({ year: 2000, monthCode: "M05" }, value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/order-of-operations.js
new file mode 100644
index 0000000000..f66a3915a5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/order-of-operations.js
@@ -0,0 +1,45 @@
+// |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.calendar.prototype.yearmonthfromfields
+description: Properties on objects passed to yearMonthFromFields() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "get fields.month",
+ "get fields.month.valueOf",
+ "call fields.month.valueOf",
+ "get fields.monthCode",
+ "get fields.monthCode.toString",
+ "call fields.monthCode.toString",
+ "get fields.year",
+ "get fields.year.valueOf",
+ "call fields.year.valueOf",
+ "get options.overflow",
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+];
+const actual = [];
+
+const instance = new Temporal.Calendar("iso8601");
+
+const fields = TemporalHelpers.propertyBagObserver(actual, {
+ year: 1.7,
+ month: 1.7,
+ monthCode: "M01",
+}, "fields");
+
+const options = TemporalHelpers.propertyBagObserver(actual, {
+ overflow: "reject",
+}, "options");
+
+const result = instance.yearMonthFromFields(fields, options);
+TemporalHelpers.assertPlainYearMonth(result, 1, 1, "M01", "yearMonth result");
+assert.sameValue(result.getISOFields().calendar, "iso8601", "calendar slot should store a string");
+assert.compareArray(actual, expected, "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/overflow-constrain.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/overflow-constrain.js
new file mode 100644
index 0000000000..8a7a1b1ee2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/overflow-constrain.js
@@ -0,0 +1,94 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.yearmonthfromfields
+description: Temporal.Calendar.prototype.yearMonthFromFields will return correctly with data and overflow set to 'constrain'.
+info: |
+ 1. Let calendar be the this value.
+ 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
+ 3. Assert: calendar.[[Identifier]] is "iso8601".
+ 4. If Type(fields) is not Object, throw a TypeError exception.
+ 5. Set options to ? GetOptionsObject(options).
+ 6. Let result be ? ISOYearMonthFromFields(fields, options).
+ 7. Return ? CreateTemporalYearMonth(result.[[Year]], result.[[Month]], calendar, result.[[ReferenceISODay]]).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const cal = new Temporal.Calendar("iso8601")
+const opt = { overflow: "constrain" };
+
+let result = cal.yearMonthFromFields({ year: 2021, month: 1 }, opt);
+TemporalHelpers.assertPlainYearMonth(result, 2021, 1, "M01", "month 1 with overflow constrain");
+result = cal.yearMonthFromFields({ year: 2021, month: 2 }, opt);
+TemporalHelpers.assertPlainYearMonth(result, 2021, 2, "M02", "month 2 with overflow constrain");
+result = cal.yearMonthFromFields({ year: 2021, month: 3 }, opt);
+TemporalHelpers.assertPlainYearMonth(result, 2021, 3, "M03", "month 3 with overflow constrain");
+result = cal.yearMonthFromFields({ year: 2021, month: 4 }, opt);
+TemporalHelpers.assertPlainYearMonth(result, 2021, 4, "M04", "month 4 with overflow constrain");
+result = cal.yearMonthFromFields({ year: 2021, month: 5 }, opt);
+TemporalHelpers.assertPlainYearMonth(result, 2021, 5, "M05", "month 5 with overflow constrain");
+result = cal.yearMonthFromFields({ year: 2021, month: 6 }, opt);
+TemporalHelpers.assertPlainYearMonth(result, 2021, 6, "M06", "month 6 with overflow constrain");
+result = cal.yearMonthFromFields({ year: 2021, month: 7 }, opt);
+TemporalHelpers.assertPlainYearMonth(result, 2021, 7, "M07", "month 7 with overflow constrain");
+result = cal.yearMonthFromFields({ year: 2021, month: 8 }, opt);
+TemporalHelpers.assertPlainYearMonth(result, 2021, 8, "M08", "month 8 with overflow constrain");
+result = cal.yearMonthFromFields({ year: 2021, month: 9 }, opt);
+TemporalHelpers.assertPlainYearMonth(result, 2021, 9, "M09", "month 9 with overflow constrain");
+result = cal.yearMonthFromFields({ year: 2021, month: 10 }, opt);
+TemporalHelpers.assertPlainYearMonth(result, 2021, 10, "M10", "month 10 with overflow constrain");
+result = cal.yearMonthFromFields({ year: 2021, month: 11 }, opt);
+TemporalHelpers.assertPlainYearMonth(result, 2021, 11, "M11", "month 11 with overflow constrain");
+result = cal.yearMonthFromFields({ year: 2021, month: 12 }, opt);
+TemporalHelpers.assertPlainYearMonth(result, 2021, 12, "M12", "month 12 with overflow constrain");
+
+assert.throws(
+ RangeError,
+ () => cal.yearMonthFromFields({ year: 2021, month: -99999 }, opt),
+ "negative month -99999 is out of range even with overflow constrain"
+)
+assert.throws(
+ RangeError,
+ () => cal.yearMonthFromFields({ year: 2021, month: -1 }, opt),
+ "negative month -1 is out of range even with overflow constrain"
+)
+assert.throws(
+ RangeError,
+ () => cal.yearMonthFromFields({ year: 2021, month: 0 }, opt),
+ "month zero is out of range even with overflow constrain"
+)
+
+result = cal.yearMonthFromFields({ year: 2021, month: 13 }, opt);
+TemporalHelpers.assertPlainYearMonth(result, 2021, 12, "M12", "month 13 is constrained to 12");
+result = cal.yearMonthFromFields({ year: 2021, month: 99999 }, opt);
+TemporalHelpers.assertPlainYearMonth(result, 2021, 12, "M12", "month 99999 is constrained to 12");
+
+result = cal.yearMonthFromFields({ year: 2021, monthCode: "M01" }, opt);
+TemporalHelpers.assertPlainYearMonth(result, 2021, 1, "M01", "monthCode M01 with overflow constrain");
+result = cal.yearMonthFromFields({ year: 2021, monthCode: "M02" }, opt);
+TemporalHelpers.assertPlainYearMonth(result, 2021, 2, "M02", "monthCode M02 with overflow constrain");
+result = cal.yearMonthFromFields({ year: 2021, monthCode: "M03" }, opt);
+TemporalHelpers.assertPlainYearMonth(result, 2021, 3, "M03", "monthCode M03 with overflow constrain");
+result = cal.yearMonthFromFields({ year: 2021, monthCode: "M04" }, opt);
+TemporalHelpers.assertPlainYearMonth(result, 2021, 4, "M04", "monthCode M04 with overflow constrain");
+result = cal.yearMonthFromFields({ year: 2021, monthCode: "M05" }, opt);
+TemporalHelpers.assertPlainYearMonth(result, 2021, 5, "M05", "monthCode M05 with overflow constrain");
+result = cal.yearMonthFromFields({ year: 2021, monthCode: "M06" }, opt);
+TemporalHelpers.assertPlainYearMonth(result, 2021, 6, "M06", "monthCode M06 with overflow constrain");
+result = cal.yearMonthFromFields({ year: 2021, monthCode: "M07" }, opt);
+TemporalHelpers.assertPlainYearMonth(result, 2021, 7, "M07", "monthCode M07 with overflow constrain");
+result = cal.yearMonthFromFields({ year: 2021, monthCode: "M08" }, opt);
+TemporalHelpers.assertPlainYearMonth(result, 2021, 8, "M08", "monthCode M08 with overflow constrain");
+result = cal.yearMonthFromFields({ year: 2021, monthCode: "M09" }, opt);
+TemporalHelpers.assertPlainYearMonth(result, 2021, 9, "M09", "monthCode M09 with overflow constrain");
+result = cal.yearMonthFromFields({ year: 2021, monthCode: "M10" }, opt);
+TemporalHelpers.assertPlainYearMonth(result, 2021, 10, "M10", "monthCode M10 with overflow constrain");
+result = cal.yearMonthFromFields({ year: 2021, monthCode: "M11" }, opt);
+TemporalHelpers.assertPlainYearMonth(result, 2021, 11, "M11", "monthCode M11 with overflow constrain");
+result = cal.yearMonthFromFields({ year: 2021, monthCode: "M12" }, opt);
+TemporalHelpers.assertPlainYearMonth(result, 2021, 12, "M12", "monthCode M12 with overflow constrain");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/overflow-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/overflow-invalid-string.js
new file mode 100644
index 0000000000..db60f50816
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/overflow-invalid-string.js
@@ -0,0 +1,30 @@
+// |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.calendar.prototype.yearmonthfromfields
+description: RangeError thrown when overflow option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-isoyearmonthfromfields step 2:
+ 2. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.calendar.prototype.yearmonthfromfields step 6:
+ 6. Let _result_ be ? ISOYearMonthFromFields(_fields_, _options_).
+features: [Temporal]
+---*/
+
+const calendar = new Temporal.Calendar("iso8601");
+const badOverflows = ["", "CONSTRAIN", "balance", "other string", "constra\u0131n", "reject\0"];
+for (const overflow of badOverflows) {
+ assert.throws(
+ RangeError,
+ () => calendar.yearMonthFromFields({ year: 2000, month: 5 }, { overflow }),
+ `invalid overflow ("${overflow}")`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/overflow-reject.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/overflow-reject.js
new file mode 100644
index 0000000000..8c0e3efb27
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/overflow-reject.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.yearmonthfromfields
+description: Throw RangeError for input data out of range with overflow reject
+info: |
+ 1. Let calendar be the this value.
+ 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
+ 3. Assert: calendar.[[Identifier]] is "iso8601".
+ 4. If Type(fields) is not Object, throw a TypeError exception.
+ 5. Set options to ? GetOptionsObject(options).
+ 6. Let result be ? ISOYearMonthFromFields(fields, options).
+ 7. Return ? CreateTemporalYearMonth(result.[[Year]], result.[[Month]], calendar, result.[[ReferenceISODay]]).
+features: [Temporal]
+---*/
+
+const cal = new Temporal.Calendar("iso8601");
+
+[-1, 0, 13, 9995].forEach((month) => {
+ assert.throws(
+ RangeError,
+ () => cal.yearMonthFromFields({year: 2021, month, day: 5}, { overflow: "reject" }),
+ `Month ${month} is out of range for 2021 with overflow: reject`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/overflow-undefined.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/overflow-undefined.js
new file mode 100644
index 0000000000..fe8e825846
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/overflow-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.calendar.prototype.yearmonthfromfields
+description: Fallback value for overflow option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-isoyearmonthfromfields step 2:
+ 2. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.calendar.prototype.yearmonthfromfields step 6:
+ 6. Let _result_ be ? ISOYearMonthFromFields(_fields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = new Temporal.Calendar("iso8601");
+
+const explicit = calendar.yearMonthFromFields({ year: 2000, month: 15 }, { overflow: undefined });
+TemporalHelpers.assertPlainYearMonth(explicit, 2000, 12, "M12", "default overflow is constrain");
+const implicit = calendar.yearMonthFromFields({ year: 2000, month: 15 }, {});
+TemporalHelpers.assertPlainYearMonth(implicit, 2000, 12, "M12", "default overflow is constrain");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/overflow-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/overflow-wrong-type.js
new file mode 100644
index 0000000000..422e9cc007
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/overflow-wrong-type.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.calendar.prototype.yearmonthfromfields
+description: Type conversions for overflow option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-isoyearmonthfromfields step 2:
+ 2. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.calendar.prototype.yearmonthfromfields step 6:
+ 6. Let _result_ be ? ISOYearMonthFromFields(_fields_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = new Temporal.Calendar("iso8601");
+TemporalHelpers.checkStringOptionWrongType("overflow", "constrain",
+ (overflow) => calendar.yearMonthFromFields({ year: 2000, month: 5 }, { overflow }),
+ (result, descr) => TemporalHelpers.assertPlainYearMonth(result, 2000, 5, "M05", descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/prop-desc.js
new file mode 100644
index 0000000000..b805c816df
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/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.calendar.prototype.yearmonthfromfields
+description: The "yearMonthFromFields" property of Temporal.Calendar.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Calendar.prototype.yearMonthFromFields,
+ "function",
+ "`typeof Calendar.prototype.yearMonthFromFields` is `function`"
+);
+
+verifyProperty(Temporal.Calendar.prototype, "yearMonthFromFields", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/reference-day.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/reference-day.js
new file mode 100644
index 0000000000..5c1157cda8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/reference-day.js
@@ -0,0 +1,41 @@
+// |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.calendar.prototype.yearmonthfromfields
+description: Reference ISO day is chosen to be the first of the calendar month
+info: |
+ 6.d. Perform ! CreateDataPropertyOrThrow(_fields_, *"day"*, *1*<sub>𝔽</sub>).
+ e. Let _result_ be ? CalendarDateToISO(_calendar_.[[Identifier]], _fields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const cal = new Temporal.Calendar("iso8601");
+
+const result1 = cal.yearMonthFromFields({ year: 2023, monthCode: "M01", day: 13 });
+TemporalHelpers.assertPlainYearMonth(
+ result1,
+ 2023, 1, "M01",
+ "reference day is 1 even if day is given",
+ /* era = */ undefined, /* era year = */ undefined, /* reference day = */ 1
+);
+
+const result2 = cal.yearMonthFromFields({ year: 2021, monthCode: "M02", day: 50 }, { overflow: "constrain" });
+TemporalHelpers.assertPlainYearMonth(
+ result2,
+ 2021, 2, "M02",
+ "reference day is 1 even if day is out of range (overflow constrain)",
+ /* era = */ undefined, /* era year = */ undefined, /* reference day = */ 1
+);
+
+const result3 = cal.yearMonthFromFields({ year: 2021, monthCode: "M02", day: 50 }, { overflow: "reject" });
+TemporalHelpers.assertPlainYearMonth(
+ result3,
+ 2021, 2, "M02",
+ "reference day is 1 even if day is out of range (overflow reject)",
+ /* era = */ undefined, /* era year = */ undefined, /* reference day = */ 1
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/shell.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/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/Calendar/prototype/yearOfWeek/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..7ec569149d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-builtin-calendar-no-array-iteration.js
@@ -0,0 +1,24 @@
+// |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.calendar.prototype.yearofweek
+description: >
+ Calling the method with a property bag argument with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.Calendar("iso8601");
+const arg = { year: 2000, month: 5, day: 2, calendar: "iso8601" };
+instance.yearOfWeek(arg);
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-calendar-datefromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..3fa5c6e13f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-calendar-datefromfields-called-with-null-prototype-fields.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.calendar.prototype.yearofweek
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.Calendar("iso8601");
+const arg = { year: 2000, month: 5, day: 2, calendar };
+instance.yearOfWeek(arg);
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..ce63ac519f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-constructor-in-calendar-fields.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.calendar.prototype.yearofweek
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.Calendar("iso8601");
+
+assert.throws(RangeError, () => instance.yearOfWeek(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-duplicate-calendar-fields.js
new file mode 100644
index 0000000000..d564d1f73f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-duplicate-calendar-fields.js
@@ -0,0 +1,19 @@
+// |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.calendar.prototype.yearofweek
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['day'], ['month'], ['monthCode'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar };
+ const instance = new Temporal.Calendar("iso8601");
+
+ assert.throws(RangeError, () => instance.yearOfWeek(arg));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-leap-second.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-leap-second.js
new file mode 100644
index 0000000000..1e28b31f7c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-leap-second.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.calendar.prototype.yearofweek
+description: Leap second is a valid ISO string for PlainDate
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+let arg = "2016-12-31T23:59:60";
+const result1 = instance.yearOfWeek(arg);
+assert.sameValue(
+ result1,
+ 2016,
+ "leap second is a valid ISO string for PlainDate"
+);
+
+arg = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+const result2 = instance.yearOfWeek(arg);
+assert.sameValue(
+ result2,
+ 2016,
+ "second: 60 is ignored in property bag for PlainDate"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-number.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-number.js
new file mode 100644
index 0000000000..6e3a05779d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/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.calendar.prototype.yearofweek
+description: A number cannot be used in place of a Temporal.PlainDate
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const numbers = [
+ 1,
+ 19761118,
+ -19761118,
+ 1234567890,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => instance.yearOfWeek(arg),
+ 'Numbers cannot be used in place of an ISO string for PlainDate'
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-propertybag-calendar-case-insensitive.js
new file mode 100644
index 0000000000..208f4f763f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-propertybag-calendar-case-insensitive.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.calendar.prototype.yearofweek
+description: The calendar name is case-insensitive
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const calendar = "IsO8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.yearOfWeek(arg);
+assert.sameValue(result, 1976, "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-propertybag-calendar-leap-second.js
new file mode 100644
index 0000000000..fb54865fc1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-propertybag-calendar-leap-second.js
@@ -0,0 +1,23 @@
+// |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.calendar.prototype.yearofweek
+description: Leap second is a valid ISO string for a calendar in a property bag
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const calendar = "2016-12-31T23:59:60";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.yearOfWeek(arg);
+assert.sameValue(
+ result,
+ 1976,
+ "leap second is a valid ISO string for calendar"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-propertybag-calendar-number.js
new file mode 100644
index 0000000000..c74d13eb85
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-propertybag-calendar-number.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.calendar.prototype.yearofweek
+description: A number as calendar in a property bag is not accepted
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const numbers = [
+ 1,
+ 19970327,
+ -19970327,
+ 1234567890,
+];
+
+for (const calendar of numbers) {
+ const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+ assert.throws(
+ TypeError,
+ () => instance.yearOfWeek(arg),
+ "Numbers cannot be used as a calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-propertybag-calendar-string.js
new file mode 100644
index 0000000000..686ec25927
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-propertybag-calendar-string.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.calendar.prototype.yearofweek
+description: A calendar ID is valid input for Calendar
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const calendar = "iso8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.yearOfWeek(arg);
+assert.sameValue(result, 1976, `Calendar created from string "${calendar}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-propertybag-calendar-wrong-type.js
new file mode 100644
index 0000000000..e05f76a918
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-propertybag-calendar-wrong-type.js
@@ -0,0 +1,46 @@
+// |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.calendar.prototype.yearofweek
+description: >
+ Appropriate error thrown when a calendar property from a property bag cannot
+ be converted to a calendar object or string
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.Calendar("iso8601");
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [calendar, description] of primitiveTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ typeof calendar === 'string' ? RangeError : TypeError,
+ () => instance.yearOfWeek(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+ [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields()
+];
+
+for (const [calendar, description] of typeErrorTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(TypeError, () => instance.yearOfWeek(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/Calendar/prototype/yearOfWeek/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-propertybag-calendar-year-zero.js
new file mode 100644
index 0000000000..ad33245788
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-propertybag-calendar-year-zero.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.calendar.prototype.yearofweek
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T17:45",
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+01:00",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+const instance = new Temporal.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.yearOfWeek(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-proto-in-calendar-fields.js
new file mode 100644
index 0000000000..48e0ff8e18
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-proto-in-calendar-fields.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.calendar.prototype.yearofweek
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.Calendar("iso8601");
+
+assert.throws(RangeError, () => instance.yearOfWeek(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..e278468ad2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-string-calendar-annotation.js
@@ -0,0 +1,33 @@
+// |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.calendar.prototype.yearofweek
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02[u-ca=iso8601]", "without time or time zone"],
+ ["2000-05-02[UTC][u-ca=iso8601]", "with time zone and no time"],
+ ["2000-05-02T15:23[u-ca=iso8601]", "without time zone"],
+ ["2000-05-02T15:23[UTC][u-ca=iso8601]", "with time zone"],
+ ["2000-05-02T15:23[!u-ca=iso8601]", "with ! and no time zone"],
+ ["2000-05-02T15:23[UTC][!u-ca=iso8601]", "with ! and time zone"],
+ ["2000-05-02T15:23[u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+const instance = new Temporal.Calendar("iso8601");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.yearOfWeek(arg);
+
+ assert.sameValue(
+ result,
+ 2000,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..1feab2ed81
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-string-critical-unknown-annotation.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.calendar.prototype.yearofweek
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[!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.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.yearOfWeek(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..38836a243e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-string-date-with-utc-offset.js
@@ -0,0 +1,48 @@
+// |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.calendar.prototype.yearofweek
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+const validStrings = [
+ "2000-05-02T00+00:00",
+ "2000-05-02T00+00:00[UTC]",
+ "2000-05-02T00+00:00[!UTC]",
+ "2000-05-02T00-02:30[America/St_Johns]",
+];
+
+for (const arg of validStrings) {
+ const result = instance.yearOfWeek(arg);
+
+ assert.sameValue(
+ result,
+ 2000,
+ `"${arg}" is a valid UTC offset with time for PlainDate`
+ );
+}
+
+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.yearOfWeek(arg),
+ `"${arg}" UTC offset without time is not valid for PlainDate`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-string-invalid.js
new file mode 100644
index 0000000000..b93efa8ca6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-string-invalid.js
@@ -0,0 +1,64 @@
+// |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.calendar.prototype.yearofweek
+description: >
+ RangeError thrown if an invalid ISO string (or syntactically valid ISO string
+ that is not supported) is used as a PlainDate
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ // invalid ISO strings:
+ "",
+ "invalid iso8601",
+ "2020-01-00",
+ "2020-01-32",
+ "2020-02-30",
+ "2021-02-29",
+ "2020-00-01",
+ "2020-13-01",
+ "2020-01-01T",
+ "2020-01-01T25:00:00",
+ "2020-01-01T01:60:00",
+ "2020-01-01T01:60:61",
+ "2020-01-01junk",
+ "2020-01-01T00:00:00junk",
+ "2020-01-01T00:00:00+00:00junk",
+ "2020-01-01T00:00:00+00:00[UTC]junk",
+ "2020-01-01T00:00:00+00:00[UTC][u-ca=iso8601]junk",
+ "02020-01-01",
+ "2020-001-01",
+ "2020-01-001",
+ "2020-01-01T001",
+ "2020-01-01T01:001",
+ "2020-01-01T01:01:001",
+ // valid, but forms not supported in Temporal:
+ "2020-W01-1",
+ "2020-001",
+ "+0002020-01-01",
+ // valid, but this calendar must not exist:
+ "2020-01-01[u-ca=notexist]",
+ // may be valid in other contexts, but insufficient information for PlainDate:
+ "2020-01",
+ "+002020-01",
+ "01-01",
+ "2020-W01",
+ "P1Y",
+ "-P12Y",
+ // valid, but outside the supported range:
+ "-999999-01-01",
+ "+999999-01-01",
+];
+const instance = new Temporal.Calendar("iso8601");
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => instance.yearOfWeek(arg),
+ `"${arg}" should not be a valid ISO string for a PlainDate`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..f584787616
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/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.calendar.prototype.yearofweek
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[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.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.yearOfWeek(arg),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..559c84c38c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-string-multiple-time-zone.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.calendar.prototype.yearofweek
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[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.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.yearOfWeek(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-string-time-separators.js
new file mode 100644
index 0000000000..19820a2a93
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-string-time-separators.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.calendar.prototype.yearofweek
+description: Time separator in string argument can vary
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02T15:23", "uppercase T"],
+ ["2000-05-02t15:23", "lowercase T"],
+ ["2000-05-02 15:23", "space between date and time"],
+];
+
+const instance = new Temporal.Calendar("iso8601");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.yearOfWeek(arg);
+
+ assert.sameValue(
+ result,
+ 2000,
+ `variant time separators (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..8cfe78c092
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-string-time-zone-annotation.js
@@ -0,0 +1,38 @@
+// |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.calendar.prototype.yearofweek
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02[Asia/Kolkata]", "named, with no time"],
+ ["2000-05-02[!Europe/Vienna]", "named, with ! and no time"],
+ ["2000-05-02[+00:00]", "numeric, with no time"],
+ ["2000-05-02[!-02:30]", "numeric, with ! and no time"],
+ ["2000-05-02T15:23[America/Sao_Paulo]", "named, with no offset"],
+ ["2000-05-02T15:23[!Asia/Tokyo]", "named, with ! and no offset"],
+ ["2000-05-02T15:23[-02:30]", "numeric, with no offset"],
+ ["2000-05-02T15:23[!+00:00]", "numeric, with ! and no offset"],
+ ["2000-05-02T15:23+00:00[America/New_York]", "named, with offset"],
+ ["2000-05-02T15:23+00:00[!UTC]", "named, with offset and !"],
+ ["2000-05-02T15:23+00:00[+01:00]", "numeric, with offset"],
+ ["2000-05-02T15:23+00:00[!-08:00]", "numeric, with offset and !"],
+];
+
+const instance = new Temporal.Calendar("iso8601");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.yearOfWeek(arg);
+
+ assert.sameValue(
+ result,
+ 2000,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..7778eec61f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-string-unknown-annotation.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.calendar.prototype.yearofweek
+description: Various forms of unknown annotation
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02[foo=bar]", "without time"],
+ ["2000-05-02T15:23[foo=bar]", "alone"],
+ ["2000-05-02T15:23[UTC][foo=bar]", "with time zone"],
+ ["2000-05-02T15:23[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["2000-05-02T15:23[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["2000-05-02T15:23[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+const instance = new Temporal.Calendar("iso8601");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.yearOfWeek(arg);
+
+ assert.sameValue(
+ result,
+ 2000,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..bebc2b7b8c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-string-with-utc-designator.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.calendar.prototype.yearofweek
+description: RangeError thrown if a string with UTC designator is used as a PlainDate
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "2019-10-01T09:00:00Z",
+ "2019-10-01T09:00:00Z[UTC]",
+];
+const instance = new Temporal.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.yearOfWeek(arg),
+ "String with UTC designator should not be valid as a PlainDate"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-string.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-string.js
new file mode 100644
index 0000000000..100982120b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-string.js
@@ -0,0 +1,39 @@
+// |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.calendar.prototype.yearofweek
+description: >
+ Temporal.Calendar.prototype.yearOfWeek will take an ISO 8601 date string and
+ return the ISO week calendar year of that date.
+features: [Temporal]
+---*/
+
+const cal = new Temporal.Calendar("iso8601");
+
+// The following week calendar years are taken from the table "Examples of
+// contemporary dates around New Year's Day" from
+// https://en.wikipedia.org/wiki/ISO_week_date#Relation_with_the_Gregorian_calendar
+
+assert.sameValue(cal.yearOfWeek("1977-01-01"), 1976, "1977-01-01 is in yearOfWeek 1976");
+assert.sameValue(cal.yearOfWeek("1977-01-02"), 1976, "1977-01-02 is in yearOfWeek 1976");
+assert.sameValue(cal.yearOfWeek("1977-12-31"), 1977, "1977-12-31 is in yearOfWeek 1977");
+assert.sameValue(cal.yearOfWeek("1978-01-01"), 1977, "1978-01-01 is in yearOfWeek 1977");
+assert.sameValue(cal.yearOfWeek("1978-01-02"), 1978, "1978-01-02 is in yearOfWeek 1978");
+assert.sameValue(cal.yearOfWeek("1978-12-31"), 1978, "1978-12-31 is in yearOfWeek 1978");
+assert.sameValue(cal.yearOfWeek("1979-01-01"), 1979, "1979-01-01 is in yearOfWeek 1979");
+assert.sameValue(cal.yearOfWeek("1979-12-30"), 1979, "1979-12-30 is in yearOfWeek 1979");
+assert.sameValue(cal.yearOfWeek("1979-12-31"), 1980, "1979-12-31 is in yearOfWeek 1980");
+assert.sameValue(cal.yearOfWeek("1980-01-01"), 1980, "1980-01-01 is in yearOfWeek 1980");
+assert.sameValue(cal.yearOfWeek("1980-12-28"), 1980, "1980-12-28 is in yearOfWeek 1980");
+assert.sameValue(cal.yearOfWeek("1980-12-29"), 1981, "1980-12-29 is in yearOfWeek 1981");
+assert.sameValue(cal.yearOfWeek("1980-12-30"), 1981, "1980-12-30 is in yearOfWeek 1981");
+assert.sameValue(cal.yearOfWeek("1980-12-31"), 1981, "1980-12-31 is in yearOfWeek 1981");
+assert.sameValue(cal.yearOfWeek("1981-01-01"), 1981, "1981-01-01 is in yearOfWeek 1981");
+assert.sameValue(cal.yearOfWeek("1981-12-31"), 1981, "1981-12-31 is in yearOfWeek 1981");
+assert.sameValue(cal.yearOfWeek("1982-01-01"), 1981, "1982-01-01 is in yearOfWeek 1981");
+assert.sameValue(cal.yearOfWeek("1982-01-02"), 1981, "1982-01-02 is in yearOfWeek 1981");
+assert.sameValue(cal.yearOfWeek("1982-01-03"), 1981, "1982-01-03 is in yearOfWeek 1981");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-wrong-type.js
new file mode 100644
index 0000000000..9d872b2e35
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/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.calendar.prototype.yearofweek
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainDate
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+
+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.yearOfWeek(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.PlainDate, "Temporal.PlainDate, object"],
+ [Temporal.PlainDate.prototype, "Temporal.PlainDate.prototype, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.yearOfWeek(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/Calendar/prototype/yearOfWeek/argument-zoneddatetime-convert.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-zoneddatetime-convert.js
new file mode 100644
index 0000000000..1e694bc160
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-zoneddatetime-convert.js
@@ -0,0 +1,22 @@
+// |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.calendar.prototype.yearofweek
+description: An exception from TimeZone#getOffsetNanosecondsFor() is propagated.
+features: [Temporal]
+---*/
+
+class TZ extends Temporal.TimeZone {
+ constructor() { super("UTC") }
+ getOffsetNanosecondsFor() { throw new Test262Error() }
+}
+
+const tz = new TZ();
+const arg = new Temporal.ZonedDateTime(0n, tz);
+const instance = new Temporal.Calendar("iso8601");
+
+assert.throws(Test262Error, () => instance.yearOfWeek(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-zoneddatetime-slots.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-zoneddatetime-slots.js
new file mode 100644
index 0000000000..86f4d6326c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-zoneddatetime-slots.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.calendar.prototype.yearofweek
+description: Getters are not called when converting a ZonedDateTime to a PlainDate.
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.ZonedDateTime.prototype);
+const getters = ["year", "month", "monthCode", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond", "calendar"];
+
+for (const property of getters) {
+ Object.defineProperty(Temporal.ZonedDateTime.prototype, property, {
+ get() {
+ actual.push(`get ${property}`);
+ const value = prototypeDescrs[property].get.call(this);
+ return {
+ toString() {
+ actual.push(`toString ${property}`);
+ return value.toString();
+ },
+ valueOf() {
+ actual.push(`valueOf ${property}`);
+ return value;
+ },
+ };
+ },
+ });
+}
+
+const arg = new Temporal.ZonedDateTime(0n, "UTC");
+const instance = new Temporal.Calendar("iso8601");
+instance.yearOfWeek(arg);
+assert.compareArray(actual, []);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..9277758fb4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.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.calendar.prototype.yearofweek
+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 calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => calendar.yearOfWeek(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..f76c1b0ef3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
@@ -0,0 +1,23 @@
+// |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.calendar.prototype.yearofweek
+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 calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => calendar.yearOfWeek(datetime),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..abdfc3a615
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/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) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.yearofweek
+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 calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => calendar.yearOfWeek(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..2e48de6dbe
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.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.calendar.prototype.yearofweek
+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 calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => calendar.yearOfWeek(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/basic.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/basic.js
new file mode 100644
index 0000000000..6d003929f4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/basic.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.calendar.prototype.yearofweek
+description: Basic tests for yearOfWeek().
+features: [Temporal]
+---*/
+
+const iso = Temporal.Calendar.from("iso8601");
+const res = 1994;
+assert.sameValue(iso.yearOfWeek(Temporal.PlainDate.from("1994-11-05")), res, "PlainDate");
+assert.sameValue(iso.yearOfWeek(Temporal.PlainDateTime.from("1994-11-05T08:15:30")), res, "PlainDateTime");
+assert.sameValue(iso.yearOfWeek({ year: 1994, month: 11, day: 5 }), res, "property bag");
+assert.sameValue(iso.yearOfWeek("1994-11-05"), res, "string");
+assert.throws(TypeError, () => iso.yearOfWeek({ year: 2000 }), "property bag with missing properties");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/branding.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/branding.js
new file mode 100644
index 0000000000..82cba7fbe4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/branding.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.calendar.prototype.yearofweek
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const yearOfWeek = Temporal.Calendar.prototype.yearOfWeek;
+
+assert.sameValue(typeof yearOfWeek, "function");
+
+const args = [new Temporal.PlainDate(2021, 7, 20)];
+
+assert.throws(TypeError, () => yearOfWeek.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => yearOfWeek.apply(null, args), "null");
+assert.throws(TypeError, () => yearOfWeek.apply(true, args), "true");
+assert.throws(TypeError, () => yearOfWeek.apply("", args), "empty string");
+assert.throws(TypeError, () => yearOfWeek.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => yearOfWeek.apply(1, args), "1");
+assert.throws(TypeError, () => yearOfWeek.apply({}, args), "plain object");
+assert.throws(TypeError, () => yearOfWeek.apply(Temporal.Calendar, args), "Temporal.Calendar");
+assert.throws(TypeError, () => yearOfWeek.apply(Temporal.Calendar.prototype, args), "Temporal.Calendar.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/browser.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/builtin.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/builtin.js
new file mode 100644
index 0000000000..af26883b52
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/builtin.js
@@ -0,0 +1,36 @@
+// |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.calendar.prototype.yearofweek
+description: >
+ Tests that Temporal.Calendar.prototype.yearOfWeek
+ 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.Calendar.prototype.yearOfWeek),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Calendar.prototype.yearOfWeek),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Calendar.prototype.yearOfWeek),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Calendar.prototype.yearOfWeek.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/calendar-datefromfields-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/calendar-datefromfields-called-with-options-undefined.js
new file mode 100644
index 0000000000..3fee75ad1e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/calendar-datefromfields-called-with-options-undefined.js
@@ -0,0 +1,18 @@
+// |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.calendar.prototype.yearofweek
+description: >
+ Calendar.dateFromFields method is called with undefined as the options value
+ when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
+calendar.yearOfWeek({ year: 2000, month: 5, day: 3, calendar });
+assert.sameValue(calendar.dateFromFieldsCallCount, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/calendar-fields-iterable.js
new file mode 100644
index 0000000000..a3d2aeb80f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/calendar-fields-iterable.js
@@ -0,0 +1,35 @@
+// |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.calendar.prototype.yearofweek
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.calendar.prototype.yearofweek step 4:
+ 4. Let _date_ be ? ToTemporalDate(_dateOrDateTime_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToListOfType(_fieldsArray_, « String »).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+calendar1.yearOfWeek({ year: 2000, month: 5, day: 2, calendar: calendar2 });
+
+assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/calendar-temporal-object.js
new file mode 100644
index 0000000000..461137a7d7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/calendar-temporal-object.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.calendar.prototype.yearofweek
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.calendar.prototype.yearofweek step 4:
+ 4. Let _date_ be ? ToTemporalDate(_dateOrDateTime_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const calendar = new Temporal.Calendar("iso8601");
+ calendar.yearOfWeek({ year: 2000, month: 5, day: 2, calendar: temporalObject });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/cross-year.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/cross-year.js
new file mode 100644
index 0000000000..1ca8d9baff
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/cross-year.js
@@ -0,0 +1,15 @@
+// |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.calendar.prototype.yearOfWeek
+description: yearOfWeek() where the result is different from the calendar year
+features: [Temporal]
+---*/
+
+const iso = Temporal.Calendar.from("iso8601");
+assert.sameValue(iso.yearOfWeek(Temporal.PlainDate.from("2019-12-31")), 2020, "next year");
+assert.sameValue(iso.yearOfWeek(Temporal.PlainDate.from("2021-01-01")), 2020, "previous year");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..10df67cf65
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/infinity-throws-rangeerror.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.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.calendar.prototype.yearofweek
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+const base = { year: 2000, month: 5, day: 2 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.yearOfWeek({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.yearOfWeek({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/length.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/length.js
new file mode 100644
index 0000000000..ef513c2254
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/length.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.calendar.prototype.yearofweek
+description: Temporal.Calendar.prototype.yearOfWeek.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.Calendar.prototype.yearOfWeek, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/name.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/name.js
new file mode 100644
index 0000000000..4bbf63865c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/name.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.calendar.prototype.yearofweek
+description: Temporal.Calendar.prototype.yearOfWeek.name is "yearOfWeek".
+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.Calendar.prototype.yearOfWeek, "name", {
+ value: "yearOfWeek",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/not-a-constructor.js
new file mode 100644
index 0000000000..33016b31bd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/not-a-constructor.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.calendar.prototype.yearofweek
+description: >
+ Temporal.Calendar.prototype.yearOfWeek 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.Calendar.prototype.yearOfWeek();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Calendar.prototype.yearOfWeek), false,
+ "isConstructor(Temporal.Calendar.prototype.yearOfWeek)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/prop-desc.js
new file mode 100644
index 0000000000..ddffa0dd35
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/prop-desc.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.calendar.prototype.yearofweek
+description: The "yearOfWeek" property of Temporal.Calendar.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Calendar.prototype.yearOfWeek,
+ "function",
+ "`typeof Calendar.prototype.yearOfWeek` is `function`"
+);
+
+verifyProperty(Temporal.Calendar.prototype, "yearOfWeek", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/shell.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/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/Calendar/prototype/yearOfWeek/year-zero.js b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/year-zero.js
new file mode 100644
index 0000000000..c6a42fb11e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/prototype/yearOfWeek/year-zero.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.calendar.prototype.yearofweek
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T00:45",
+ "-000000-10-31T00:45+01:00",
+ "-000000-10-31T00:45+00:00[UTC]",
+];
+const instance = new Temporal.Calendar("iso8601");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.yearOfWeek(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/shell.js b/js/src/tests/test262/built-ins/Temporal/Calendar/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Calendar/subclass.js b/js/src/tests/test262/built-ins/Temporal/Calendar/subclass.js
new file mode 100644
index 0000000000..cb13924cf3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Calendar/subclass.js
@@ -0,0 +1,20 @@
+// |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.calendar
+description: Test for Temporal.Calendar subclassing.
+features: [Temporal]
+---*/
+
+class CustomCalendar extends Temporal.Calendar {
+}
+
+const instance = new CustomCalendar("iso8601");
+assert.sameValue(instance.toString(), "iso8601");
+assert.sameValue(Object.getPrototypeOf(instance), CustomCalendar.prototype, "Instance of CustomCalendar");
+assert(instance instanceof CustomCalendar, "Instance of CustomCalendar");
+assert(instance instanceof Temporal.Calendar, "Instance of Temporal.Calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/basic.js b/js/src/tests/test262/built-ins/Temporal/Duration/basic.js
new file mode 100644
index 0000000000..a3434b1ff7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/basic.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.duration
+description: Basic constructor tests.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.assertDuration(new Temporal.Duration(5, 5, 5, 5, 5, 5, 5, 5, 5, 0),
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, "positive");
+TemporalHelpers.assertDuration(new Temporal.Duration(-5, -5, -5, -5, -5, -5, -5, -5, -5, 0),
+ -5, -5, -5, -5, -5, -5, -5, -5, -5, 0, "negative");
+TemporalHelpers.assertDuration(new Temporal.Duration(-0, -0, -0, -0, -0, -0, -0, -0, -0, -0),
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "negative zero");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/browser.js b/js/src/tests/test262/built-ins/Temporal/Duration/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/builtin.js b/js/src/tests/test262/built-ins/Temporal/Duration/builtin.js
new file mode 100644
index 0000000000..9a54bf294a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/builtin.js
@@ -0,0 +1,30 @@
+// |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.duration
+description: Tests that Temporal.Duration meets the requirements for built-in objects
+info: |
+ 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.Duration),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Duration),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Duration),
+ Function.prototype, "prototype");
+
+assert.sameValue(typeof Temporal.Duration.prototype,
+ "object", "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/call-builtin.js b/js/src/tests/test262/built-ins/Temporal/Duration/call-builtin.js
new file mode 100644
index 0000000000..99f9c24428
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/call-builtin.js
@@ -0,0 +1,18 @@
+// |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.duration
+description: Constructor should not call built-in functions.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+Number.isFinite = () => { throw new Test262Error("should not call Number.isFinite") };
+Math.sign = () => { throw new Test262Error("should not call Math.sign") };
+
+const duration = new Temporal.Duration(1, 1);
+TemporalHelpers.assertDuration(duration, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/argument-cast.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/argument-cast.js
new file mode 100644
index 0000000000..f2ead6ecf4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/argument-cast.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.duration.compare
+description: Strings and objects are supported arguments.
+features: [Temporal]
+---*/
+
+assert.sameValue(Temporal.Duration.compare("PT12H", new Temporal.Duration()), 1,
+ "first argument string");
+assert.sameValue(Temporal.Duration.compare({ hours: 12 }, new Temporal.Duration()), 1,
+ "first argument object");
+assert.throws(TypeError, () => Temporal.Duration.compare({ hour: 12 }, new Temporal.Duration()),
+ "first argument missing property");
+
+assert.sameValue(Temporal.Duration.compare(new Temporal.Duration(), "PT12H"), -1,
+ "second argument string");
+assert.sameValue(Temporal.Duration.compare(new Temporal.Duration(), { hours: 12 }), -1,
+ "second argument object");
+assert.throws(TypeError, () => Temporal.Duration.compare(new Temporal.Duration(), { hour: 12 }),
+ "second argument missing property");
+
+assert.sameValue(Temporal.Duration.compare({ hours: 12, minute: 5 }, { hours: 12, day: 5 }), 0,
+ "ignores incorrect properties");
+
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/argument-duration-max.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/argument-duration-max.js
new file mode 100644
index 0000000000..c642aecce6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/argument-duration-max.js
@@ -0,0 +1,47 @@
+// |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.duration.compare
+description: Maximum allowed duration
+features: [Temporal]
+---*/
+
+const maxCases = [
+ ["P104249991374DT7H36M31.999999999S", "string with max days"],
+ [{ days: 104249991374, nanoseconds: 27391999999999 }, "property bag with max days"],
+ ["PT2501999792983H36M31.999999999S", "string with max hours"],
+ [{ hours: 2501999792983, nanoseconds: 2191999999999 }, "property bag with max hours"],
+ ["PT150119987579016M31.999999999S", "string with max minutes"],
+ [{ minutes: 150119987579016, nanoseconds: 31999999999 }, "property bag with max minutes"],
+ ["PT9007199254740991.999999999S", "string with max seconds"],
+ [{ seconds: 9007199254740991, nanoseconds: 999999999 }, "property bag with max seconds"],
+];
+
+for (const [arg, descr] of maxCases) {
+ const result1 = Temporal.Duration.compare(arg, new Temporal.Duration());
+ assert.sameValue(result1, 1, `operation succeeds with ${descr} (first argument)`);
+ const result2 = Temporal.Duration.compare(new Temporal.Duration(), arg);
+ assert.sameValue(result2, -1, `operation succeeds with ${descr} (second argument)`);
+}
+
+const minCases = [
+ ["-P104249991374DT7H36M31.999999999S", "string with min days"],
+ [{ days: -104249991374, nanoseconds: -27391999999999 }, "property bag with min days"],
+ ["-PT2501999792983H36M31.999999999S", "string with min hours"],
+ [{ hours: -2501999792983, nanoseconds: -2191999999999 }, "property bag with min hours"],
+ ["-PT150119987579016M31.999999999S", "string with min minutes"],
+ [{ minutes: -150119987579016, nanoseconds: -31999999999 }, "property bag with min minutes"],
+ ["-PT9007199254740991.999999999S", "string with min seconds"],
+ [{ seconds: -9007199254740991, nanoseconds: -999999999 }, "property bag with min seconds"],
+];
+
+for (const [arg, descr] of minCases) {
+ const result1 = Temporal.Duration.compare(arg, new Temporal.Duration());
+ assert.sameValue(result1, -1, `operation succeeds with ${descr} (first argument)`);
+ const result2 = Temporal.Duration.compare(new Temporal.Duration(), arg);
+ assert.sameValue(result2, 1, `operation succeeds with ${descr} (second argument)`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/argument-duration-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/argument-duration-out-of-range.js
new file mode 100644
index 0000000000..e8515159b9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/argument-duration-out-of-range.js
@@ -0,0 +1,74 @@
+// |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.duration.compare
+description: Duration-like argument that is out of range
+features: [Temporal]
+---*/
+
+const cases = [
+ // 2^32 = 4294967296
+ ["P4294967296Y", "string with years > max"],
+ [{ years: 4294967296 }, "property bag with years > max"],
+ ["-P4294967296Y", "string with years < min"],
+ [{ years: -4294967296 }, "property bag with years < min"],
+ ["P4294967296M", "string with months > max"],
+ [{ months: 4294967296 }, "property bag with months > max"],
+ ["-P4294967296M", "string with months < min"],
+ [{ months: -4294967296 }, "property bag with months < min"],
+ ["P4294967296W", "string with weeks > max"],
+ [{ weeks: 4294967296 }, "property bag with weeks > max"],
+ ["-P4294967296W", "string with weeks < min"],
+ [{ weeks: -4294967296 }, "property bag with weeks < min"],
+
+ // ceil(max safe integer / 86400) = 104249991375
+ ["P104249991375D", "string with days > max"],
+ [{ days: 104249991375 }, "property bag with days > max"],
+ ["P104249991374DT24H", "string where hours balance into days > max"],
+ [{ days: 104249991374, hours: 24 }, "property bag where hours balance into days > max"],
+ ["-P104249991375D", "string with days < min"],
+ [{ days: -104249991375 }, "property bag with days < min"],
+ ["-P104249991374DT24H", "string where hours balance into days < min"],
+ [{ days: -104249991374, hours: -24 }, "property bag where hours balance into days < min"],
+
+ // ceil(max safe integer / 3600) = 2501999792984
+ ["PT2501999792984H", "string with hours > max"],
+ [{ hours: 2501999792984 }, "property bag with hours > max"],
+ ["PT2501999792983H60M", "string where minutes balance into hours > max"],
+ [{ hours: 2501999792983, minutes: 60 }, "property bag where minutes balance into hours > max"],
+ ["-PT2501999792984H", "string with hours < min"],
+ [{ hours: -2501999792984 }, "property bag with hours < min"],
+ ["-PT2501999792983H60M", "string where minutes balance into hours < min"],
+ [{ hours: -2501999792983, minutes: -60 }, "property bag where minutes balance into hours < min"],
+
+ // ceil(max safe integer / 60) = 150119987579017
+ ["PT150119987579017M", "string with minutes > max"],
+ [{ minutes: 150119987579017 }, "property bag with minutes > max"],
+ ["PT150119987579016M60S", "string where seconds balance into minutes > max"],
+ [{ minutes: 150119987579016, seconds: 60 }, "property bag where seconds balance into minutes > max"],
+ ["-PT150119987579017M", "string with minutes < min"],
+ [{ minutes: -150119987579017 }, "property bag with minutes < min"],
+ ["-PT150119987579016M60S", "string where seconds balance into minutes < min"],
+ [{ minutes: -150119987579016, seconds: -60 }, "property bag where seconds balance into minutes < min"],
+
+ // 2^53 = 9007199254740992
+ ["PT9007199254740992S", "string with seconds > max"],
+ [{ seconds: 9007199254740992 }, "property bag with seconds > max"],
+ [{ seconds: 9007199254740991, milliseconds: 1000 }, "property bag where milliseconds balance into seconds > max"],
+ [{ seconds: 9007199254740991, microseconds: 1000000 }, "property bag where microseconds balance into seconds > max"],
+ [{ seconds: 9007199254740991, nanoseconds: 1000000000 }, "property bag where nanoseconds balance into seconds > max"],
+ ["-PT9007199254740992S", "string with seconds < min"],
+ [{ seconds: -9007199254740992 }, "property bag with seconds < min"],
+ [{ seconds: -9007199254740991, milliseconds: -1000 }, "property bag where milliseconds balance into seconds < min"],
+ [{ seconds: -9007199254740991, microseconds: -1000000 }, "property bag where microseconds balance into seconds < min"],
+ [{ seconds: -9007199254740991, nanoseconds: -1000000000 }, "property bag where nanoseconds balance into seconds < min"],
+];
+
+for (const [arg, descr] of cases) {
+ assert.throws(RangeError, () => Temporal.Duration.compare(arg, new Temporal.Duration()), `${descr} is out of range (first argument)`);
+ assert.throws(RangeError, () => Temporal.Duration.compare(new Temporal.Duration(), arg), `${descr} is out of range (second argument)`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/argument-duration-precision-exact-numerical-values.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/argument-duration-precision-exact-numerical-values.js
new file mode 100644
index 0000000000..bc0d401bca
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/argument-duration-precision-exact-numerical-values.js
@@ -0,0 +1,38 @@
+// |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.duration.from
+description: >
+ Duration-like argument performs the range check with minimal floating point
+ precision loss
+features: [Temporal]
+---*/
+
+// Based on a test case by André Bargull
+
+const cases = [
+ [
+ {
+ milliseconds: 4503599627370497_000, // ℝ(𝔽(4503599627370497000)) = 4503599627370497024
+ microseconds: 4503599627370495_000000, // ℝ(𝔽(4503599627370495000000)) = 4503599627370494951424
+ },
+ // 4503599627370497024 / 1000 + 4503599627370494951424 / 1000000 is
+ // 9007199254740991.975424, which is below the limit of 2**53
+ "case where floating point inaccuracy brings total below limit, positive"
+ ],
+ [
+ {
+ milliseconds: -4503599627370497_000,
+ microseconds: -4503599627370495_000000,
+ },
+ "case where floating point inaccuracy brings total below limit, negative"
+ ],
+];
+
+for (const [arg, descr] of cases) {
+ assert.sameValue(Temporal.Duration.compare(arg, arg), 0, descr);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/argument-string-fractional-units-rounding-mode.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/argument-string-fractional-units-rounding-mode.js
new file mode 100644
index 0000000000..02a04ed5b4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/argument-string-fractional-units-rounding-mode.js
@@ -0,0 +1,23 @@
+// |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.duration.compare
+description: Strings with fractional duration units are rounded with the correct rounding mode
+features: [Temporal]
+---*/
+
+const expectedPos = new Temporal.Duration(0, 0, 0, 0, 1, 1, 52, 500);
+const expectedNeg = new Temporal.Duration(0, 0, 0, 0, -1, -1, -52, -500);
+
+assert.sameValue(Temporal.Duration.compare("PT1.03125H", expectedPos), 0,
+ "positive fractional units rounded with correct rounding mode (first argument)");
+assert.sameValue(Temporal.Duration.compare("-PT1.03125H", expectedNeg), 0,
+ "negative fractional units rounded with correct rounding mode (first argument)");
+assert.sameValue(Temporal.Duration.compare(expectedPos, "PT1.03125H"), 0,
+ "positive fractional units rounded with correct rounding mode (second argument)");
+assert.sameValue(Temporal.Duration.compare(expectedNeg, "-PT1.03125H"), 0,
+ "negative fractional units rounded with correct rounding mode (second argument)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/argument-string-negative-fractional-units.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/argument-string-negative-fractional-units.js
new file mode 100644
index 0000000000..9e0e32ea5b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/argument-string-negative-fractional-units.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.duration.compare
+description: Strings with fractional duration units are treated with the correct sign
+features: [Temporal]
+---*/
+
+const expectedHours = new Temporal.Duration(0, 0, 0, 0, -24, -34, -4, -404, -442, -800);
+const resultHours1 = Temporal.Duration.compare("-PT24.567890123H", expectedHours);
+assert.sameValue(resultHours1, 0, "negative fractional hours (first argument)");
+const resultHours2 = Temporal.Duration.compare(expectedHours, "-PT24.567890123H");
+assert.sameValue(resultHours2, 0, "negative fractional hours (second argument)");
+
+const expectedMinutes = new Temporal.Duration(0, 0, 0, 0, 0, -1440, -34, -73, -407, -380);
+const resultMinutes1 = Temporal.Duration.compare("-PT1440.567890123M", expectedMinutes);
+assert.sameValue(resultMinutes1, 0, "negative fractional minutes (first argument)");
+const resultMinutes2 = Temporal.Duration.compare("-PT1440.567890123M", expectedMinutes);
+assert.sameValue(resultMinutes2, 0, "negative fractional minutes (second argument)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/basic.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/basic.js
new file mode 100644
index 0000000000..ba9e5fb80b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/basic.js
@@ -0,0 +1,56 @@
+// |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.duration.compare
+description: Basic comparisons.
+features: [Temporal]
+---*/
+
+const td1pos = new Temporal.Duration(0, 0, 0, 0, 5, 5, 5, 5, 5, 5);
+const td2pos = new Temporal.Duration(0, 0, 0, 0, 5, 4, 5, 5, 5, 5);
+const td1neg = new Temporal.Duration(0, 0, 0, 0, -5, -5, -5, -5, -5, -5);
+const td2neg = new Temporal.Duration(0, 0, 0, 0, -5, -4, -5, -5, -5, -5);
+assert.sameValue(Temporal.Duration.compare(td1pos, td1pos), 0,
+ "time units: equal");
+assert.sameValue(Temporal.Duration.compare(td2pos, td1pos), -1,
+ "time units: smaller/larger");
+assert.sameValue(Temporal.Duration.compare(td1pos, td2pos), 1,
+ "time units: larger/smaller");
+assert.sameValue(Temporal.Duration.compare(td1neg, td1neg), 0,
+ "time units: negative/negative equal");
+assert.sameValue(Temporal.Duration.compare(td2neg, td1neg), 1,
+ "time units: negative/negative smaller/larger");
+assert.sameValue(Temporal.Duration.compare(td1neg, td2neg), -1,
+ "time units: negative/negative larger/smaller");
+assert.sameValue(Temporal.Duration.compare(td1neg, td2pos), -1,
+ "time units: negative/positive");
+assert.sameValue(Temporal.Duration.compare(td1pos, td2neg), 1,
+ "time units: positive/negative");
+
+const dd1pos = new Temporal.Duration(5, 5, 5, 5, 5, 5, 5, 5, 5, 5);
+const dd2pos = new Temporal.Duration(5, 5, 5, 5, 5, 4, 5, 5, 5, 5);
+const dd1neg = new Temporal.Duration(-5, -5, -5, -5, -5, -5, -5, -5, -5, -5);
+const dd2neg = new Temporal.Duration(-5, -5, -5, -5, -5, -4, -5, -5, -5, -5);
+const relativeTo = Temporal.PlainDate.from("2017-01-01");
+assert.throws(RangeError, () => Temporal.Duration.compare(dd1pos, dd2pos),
+ "date units: relativeTo is required");
+assert.sameValue(Temporal.Duration.compare(dd1pos, dd1pos, { relativeTo }), 0,
+ "date units: equal");
+assert.sameValue(Temporal.Duration.compare(dd2pos, dd1pos, { relativeTo }), -1,
+ "date units: smaller/larger");
+assert.sameValue(Temporal.Duration.compare(dd1pos, dd2pos, { relativeTo }), 1,
+ "date units: larger/smaller");
+assert.sameValue(Temporal.Duration.compare(dd1neg, dd1neg, { relativeTo }), 0,
+ "date units: negative/negative equal");
+assert.sameValue(Temporal.Duration.compare(dd2neg, dd1neg, { relativeTo }), 1,
+ "date units: negative/negative smaller/larger");
+assert.sameValue(Temporal.Duration.compare(dd1neg, dd2neg, { relativeTo }), -1,
+ "date units: negative/negative larger/smaller");
+assert.sameValue(Temporal.Duration.compare(dd1neg, dd2pos, { relativeTo }), -1,
+ "date units: negative/positive");
+assert.sameValue(Temporal.Duration.compare(dd1pos, dd2neg, { relativeTo }), 1,
+ "date units: positive/negative");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/browser.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/builtin.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/builtin.js
new file mode 100644
index 0000000000..bc3ae30fef
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/builtin.js
@@ -0,0 +1,33 @@
+// |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.duration.compare
+description: Tests that Temporal.Duration.compare meets the requirements for built-in objects
+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.Duration.compare),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Duration.compare),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Duration.compare),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Duration.compare.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/calendar-dateadd-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/calendar-dateadd-called-with-options-undefined.js
new file mode 100644
index 0000000000..6fe3fdb040
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/calendar-dateadd-called-with-options-undefined.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.duration.compare
+description: >
+ BuiltinTimeZoneGetInstantFor calls Calendar.dateAdd with undefined as the
+ options value
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarDateAddUndefinedOptions();
+const timeZone = TemporalHelpers.oneShiftTimeZone(new Temporal.Instant(0n), 3600e9);
+const relativeTo = new Temporal.ZonedDateTime(0n, timeZone, calendar);
+
+const duration1 = new Temporal.Duration(0, 0, 1);
+const duration2 = new Temporal.Duration(0, 0, 1, 1);
+Temporal.Duration.compare(duration1, duration2, { relativeTo });
+assert.sameValue(calendar.dateAddCallCount, 2);
+// one call for each duration argument to add it to relativeTo
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/calendar-dateadd-called-with-plaindate-instance.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/calendar-dateadd-called-with-plaindate-instance.js
new file mode 100644
index 0000000000..c30d608e8f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/calendar-dateadd-called-with-plaindate-instance.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.duration.compare
+description: >
+ relativeTo parameters that are not ZonedDateTime or undefined, are always
+ converted to PlainDate for observable calendar calls
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarDateAddPlainDateInstance();
+const relativeTo = new Temporal.PlainDate(2000, 1, 1, calendar);
+calendar.specificPlainDate = relativeTo;
+Temporal.Duration.compare(new Temporal.Duration(1, 1, 1, 1), new Temporal.Duration(1, 1, 1), { relativeTo });
+assert(calendar.dateAddCallCount > 0, "assertions in calendar.dateAdd() should have been tested");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/calendar-fields-iterable.js
new file mode 100644
index 0000000000..13daa88c87
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/calendar-fields-iterable.js
@@ -0,0 +1,35 @@
+// |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.duration.compare
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.duration.compare step 4:
+ 4. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_).
+ sec-temporal-torelativetemporalobject step 4.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+const duration1 = new Temporal.Duration(1);
+const duration2 = new Temporal.Duration(0, 12);
+Temporal.Duration.compare(duration1, duration2, { relativeTo: { year: 2000, month: 1, day: 1, calendar } });
+
+assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/calendar-possibly-required.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/calendar-possibly-required.js
new file mode 100644
index 0000000000..dd4082ef02
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/calendar-possibly-required.js
@@ -0,0 +1,55 @@
+// |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.duration.compare
+description: relativeTo argument needed if days = 0 but years/months/weeks non-zero
+features: [Temporal]
+---*/
+const duration1a = new Temporal.Duration(1);
+const duration1b = new Temporal.Duration(1, 0, 0, 0, 0, 0, 0, 0, 0, 1);
+const duration2a = new Temporal.Duration(0, 12);
+const duration2b = new Temporal.Duration(0, 12, 0, 0, 0, 0, 0, 0, 0, 1);
+const duration3a = new Temporal.Duration(0, 0, 5);
+const duration3b = new Temporal.Duration(0, 0, 5, 0, 0, 0, 0, 0, 0, 1);
+const duration4a = new Temporal.Duration(0, 0, 0, 42);
+const duration4b = new Temporal.Duration(0, 0, 0, 42, 0, 0, 0, 0, 0, 1);
+const relativeTo = new Temporal.PlainDate(2021, 12, 15);
+assert.throws(
+ RangeError,
+ () => { Temporal.Duration.compare(duration1a, duration1b); },
+ "cannot compare Duration values without relativeTo if year is non-zero"
+);
+assert.sameValue(-1,
+ Temporal.Duration.compare(duration1a, duration1b, { relativeTo }),
+ "compare succeeds for year-only Duration provided relativeTo is supplied");
+assert.throws(
+ RangeError,
+ () => { Temporal.Duration.compare(duration2a, duration2b); },
+ "cannot compare Duration values without relativeTo if month is non-zero"
+);
+assert.sameValue(-1,
+ Temporal.Duration.compare(duration2a, duration2b, { relativeTo }),
+ "compare succeeds for year-and-month Duration provided relativeTo is supplied");
+assert.throws(
+ RangeError,
+ () => { Temporal.Duration.compare(duration3a, duration3b); },
+ "cannot compare Duration values without relativeTo if week is non-zero"
+);
+assert.sameValue(-1,
+ Temporal.Duration.compare(duration3a, duration3b, { relativeTo }),
+ "compare succeeds for year-and-month-and-week Duration provided relativeTo is supplied"
+);
+
+assert.sameValue(-1,
+ Temporal.Duration.compare(duration4a, duration4b),
+ "compare succeeds for zero year-month-week non-zero day Duration even without relativeTo");
+
+// Double-check that the comparison also works with a relative-to argument
+assert.sameValue(-1,
+ Temporal.Duration.compare(duration4a, duration4b, { relativeTo }),
+ "compare succeeds for zero year-month-week non-zero day Duration with relativeTo"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/calendar-temporal-object.js
new file mode 100644
index 0000000000..e2d4441a74
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/calendar-temporal-object.js
@@ -0,0 +1,30 @@
+// |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.duration.compare
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.duration.compare step 4:
+ 4. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_).
+ sec-temporal-torelativetemporalobject step 4.b:
+ b. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const duration1 = new Temporal.Duration(1);
+ const duration2 = new Temporal.Duration(0, 12);
+ Temporal.Duration.compare(duration1, duration2, { relativeTo: { year: 2000, month: 1, day: 1, calendar: temporalObject } });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..a6fa93b44a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/constructor-in-calendar-fields.js
@@ -0,0 +1,18 @@
+// |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.duration.prototype.compare
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const relativeTo = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar, timeZone: 'Europe/Paris' };
+const duration1 = new Temporal.Duration(1);
+const duration2 = new Temporal.Duration(0, 12);
+
+assert.throws(RangeError, () => Temporal.Duration.compare(duration1, duration2, {relativeTo: relativeTo}));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/duplicate-calendar-fields.js
new file mode 100644
index 0000000000..a54ae06f86
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/duplicate-calendar-fields.js
@@ -0,0 +1,21 @@
+// |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.duration.prototype.compare
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+
+for (const extra_fields of [['foo', 'foo'], ['day'], ['month'], ['monthCode'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const relativeTo = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar, timeZone: 'Europe/Paris' };
+ const duration1 = new Temporal.Duration(1);
+ const duration2 = new Temporal.Duration(0, 12);
+
+ assert.throws(RangeError, () => Temporal.Duration.compare(duration1, duration2, {relativeTo: relativeTo}));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/exhaustive.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/exhaustive.js
new file mode 100644
index 0000000000..a38fa79320
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/exhaustive.js
@@ -0,0 +1,516 @@
+// |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.duration.compare
+description: Tests for compare() with each possible outcome
+features: [Temporal]
+---*/
+
+const plainDate = new Temporal.PlainDate(2000, 1, 1);
+
+assert.sameValue(
+ Temporal.Duration.compare(
+ new Temporal.Duration(6),
+ new Temporal.Duration(5),
+ { relativeTo: plainDate }
+ ),
+ 1,
+ "years >, relativeTo PlainDate"
+);
+assert.sameValue(
+ Temporal.Duration.compare(
+ new Temporal.Duration(3),
+ new Temporal.Duration(4),
+ { relativeTo: plainDate }
+ ),
+ -1,
+ "years <, relativeTo PlainDate"
+);
+assert.sameValue(
+ Temporal.Duration.compare(
+ new Temporal.Duration(2, 6),
+ new Temporal.Duration(2, 5),
+ { relativeTo: plainDate }
+ ),
+ 1,
+ "months >, relativeTo PlainDate"
+);
+assert.sameValue(
+ Temporal.Duration.compare(
+ new Temporal.Duration(4, 3),
+ new Temporal.Duration(4, 4),
+ { relativeTo: plainDate }
+ ),
+ -1,
+ "months <, relativeTo PlainDate"
+);
+assert.sameValue(
+ Temporal.Duration.compare(
+ new Temporal.Duration(2, 1, 6),
+ new Temporal.Duration(2, 1, 5),
+ { relativeTo: plainDate }
+ ),
+ 1,
+ "weeks >, relativeTo PlainDate"
+);
+assert.sameValue(
+ Temporal.Duration.compare(
+ new Temporal.Duration(4, 7, 3),
+ new Temporal.Duration(4, 7, 4),
+ { relativeTo: plainDate }
+ ),
+ -1,
+ "weeks <, relativeTo PlainDate"
+);
+assert.sameValue(
+ Temporal.Duration.compare(
+ new Temporal.Duration(2, 1, 3, 6),
+ new Temporal.Duration(2, 1, 3, 5),
+ { relativeTo: plainDate }
+ ),
+ 1,
+ "days >, relativeTo PlainDate"
+);
+assert.sameValue(
+ Temporal.Duration.compare(
+ new Temporal.Duration(4, 7, 2, 3),
+ new Temporal.Duration(4, 7, 2, 4),
+ { relativeTo: plainDate }
+ ),
+ -1,
+ "days <, relativeTo PlainDate"
+);
+assert.sameValue(
+ Temporal.Duration.compare(
+ new Temporal.Duration(2, 1, 3, 12, 6),
+ new Temporal.Duration(2, 1, 3, 12, 5),
+ { relativeTo: plainDate }
+ ),
+ 1,
+ "hours >, relativeTo PlainDate"
+);
+assert.sameValue(
+ Temporal.Duration.compare(
+ new Temporal.Duration(4, 7, 2, 40, 3),
+ new Temporal.Duration(4, 7, 2, 40, 4),
+ { relativeTo: plainDate }
+ ),
+ -1,
+ "hours <, relativeTo PlainDate"
+);
+assert.sameValue(
+ Temporal.Duration.compare(
+ new Temporal.Duration(2, 1, 3, 12, 6, 6),
+ new Temporal.Duration(2, 1, 3, 12, 6, 5),
+ { relativeTo: plainDate }
+ ),
+ 1,
+ "minutes >, relativeTo PlainDate"
+);
+assert.sameValue(
+ Temporal.Duration.compare(
+ new Temporal.Duration(4, 7, 2, 40, 12, 3),
+ new Temporal.Duration(4, 7, 2, 40, 12, 4),
+ { relativeTo: plainDate }
+ ),
+ -1,
+ "minutes <, relativeTo PlainDate"
+);
+assert.sameValue(
+ Temporal.Duration.compare(
+ new Temporal.Duration(2, 1, 3, 12, 6, 30, 6),
+ new Temporal.Duration(2, 1, 3, 12, 6, 30, 5),
+ { relativeTo: plainDate }
+ ),
+ 1,
+ "seconds >, relativeTo PlainDate"
+);
+assert.sameValue(
+ Temporal.Duration.compare(
+ new Temporal.Duration(4, 7, 2, 40, 12, 15, 3),
+ new Temporal.Duration(4, 7, 2, 40, 12, 15, 4),
+ { relativeTo: plainDate }
+ ),
+ -1,
+ "seconds <, relativeTo PlainDate"
+);
+assert.sameValue(
+ Temporal.Duration.compare(
+ new Temporal.Duration(2, 1, 3, 12, 6, 30, 15, 6),
+ new Temporal.Duration(2, 1, 3, 12, 6, 30, 15, 5),
+ { relativeTo: plainDate }
+ ),
+ 1,
+ "milliseconds >, relativeTo PlainDate"
+);
+assert.sameValue(
+ Temporal.Duration.compare(
+ new Temporal.Duration(4, 7, 2, 40, 12, 15, 45, 3),
+ new Temporal.Duration(4, 7, 2, 40, 12, 15, 45, 4),
+ { relativeTo: plainDate }
+ ),
+ -1,
+ "milliseconds <, relativeTo PlainDate"
+);
+assert.sameValue(
+ Temporal.Duration.compare(
+ new Temporal.Duration(2, 1, 3, 12, 6, 30, 15, 222, 6),
+ new Temporal.Duration(2, 1, 3, 12, 6, 30, 15, 222, 5),
+ { relativeTo: plainDate }
+ ),
+ 1,
+ "microseconds >, relativeTo PlainDate"
+);
+assert.sameValue(
+ Temporal.Duration.compare(
+ new Temporal.Duration(4, 7, 2, 40, 12, 15, 45, 333, 3),
+ new Temporal.Duration(4, 7, 2, 40, 12, 15, 45, 333, 4),
+ { relativeTo: plainDate }
+ ),
+ -1,
+ "microseconds <, relativeTo PlainDate"
+);
+assert.sameValue(
+ Temporal.Duration.compare(
+ new Temporal.Duration(2, 1, 3, 12, 6, 30, 15, 222, 444, 6),
+ new Temporal.Duration(2, 1, 3, 12, 6, 30, 15, 222, 444, 5),
+ { relativeTo: plainDate }
+ ),
+ 1,
+ "nanoseconds >, relativeTo PlainDate"
+);
+assert.sameValue(
+ Temporal.Duration.compare(
+ new Temporal.Duration(4, 7, 2, 40, 12, 15, 45, 333, 777, 3),
+ new Temporal.Duration(4, 7, 2, 40, 12, 15, 45, 333, 777, 4),
+ { relativeTo: plainDate }
+ ),
+ -1,
+ "nanoseconds <, relativeTo PlainDate"
+);
+assert.sameValue(
+ Temporal.Duration.compare(
+ new Temporal.Duration(4, 7, 2, 40, 12, 15, 45, 333, 777, 111),
+ new Temporal.Duration(4, 7, 2, 40, 12, 15, 45, 333, 777, 111),
+ { relativeTo: plainDate }
+ ),
+ 0,
+ "equal, relativeTo PlainDate"
+);
+
+const zonedDateTime = new Temporal.ZonedDateTime(0n, "UTC");
+
+assert.sameValue(
+ Temporal.Duration.compare(
+ new Temporal.Duration(6),
+ new Temporal.Duration(5),
+ { relativeTo: zonedDateTime }
+ ),
+ 1,
+ "years >, relativeTo ZonedDateTime"
+);
+assert.sameValue(
+ Temporal.Duration.compare(
+ new Temporal.Duration(3),
+ new Temporal.Duration(4),
+ { relativeTo: zonedDateTime }
+ ),
+ -1,
+ "years <, relativeTo ZonedDateTime"
+);
+assert.sameValue(
+ Temporal.Duration.compare(
+ new Temporal.Duration(2, 6),
+ new Temporal.Duration(2, 5),
+ { relativeTo: zonedDateTime }
+ ),
+ 1,
+ "months >, relativeTo ZonedDateTime"
+);
+assert.sameValue(
+ Temporal.Duration.compare(
+ new Temporal.Duration(4, 3),
+ new Temporal.Duration(4, 4),
+ { relativeTo: zonedDateTime }
+ ),
+ -1,
+ "months <, relativeTo ZonedDateTime"
+);
+assert.sameValue(
+ Temporal.Duration.compare(
+ new Temporal.Duration(2, 1, 6),
+ new Temporal.Duration(2, 1, 5),
+ { relativeTo: zonedDateTime }
+ ),
+ 1,
+ "weeks >, relativeTo ZonedDateTime"
+);
+assert.sameValue(
+ Temporal.Duration.compare(
+ new Temporal.Duration(4, 7, 3),
+ new Temporal.Duration(4, 7, 4),
+ { relativeTo: zonedDateTime }
+ ),
+ -1,
+ "weeks <, relativeTo ZonedDateTime"
+);
+assert.sameValue(
+ Temporal.Duration.compare(
+ new Temporal.Duration(2, 1, 3, 6),
+ new Temporal.Duration(2, 1, 3, 5),
+ { relativeTo: zonedDateTime }
+ ),
+ 1,
+ "days >, relativeTo ZonedDateTime"
+);
+assert.sameValue(
+ Temporal.Duration.compare(
+ new Temporal.Duration(4, 7, 2, 3),
+ new Temporal.Duration(4, 7, 2, 4),
+ { relativeTo: zonedDateTime }
+ ),
+ -1,
+ "days <, relativeTo ZonedDateTime"
+);
+assert.sameValue(
+ Temporal.Duration.compare(
+ new Temporal.Duration(2, 1, 3, 12, 6),
+ new Temporal.Duration(2, 1, 3, 12, 5),
+ { relativeTo: zonedDateTime }
+ ),
+ 1,
+ "hours >, relativeTo ZonedDateTime"
+);
+assert.sameValue(
+ Temporal.Duration.compare(
+ new Temporal.Duration(4, 7, 2, 40, 3),
+ new Temporal.Duration(4, 7, 2, 40, 4),
+ { relativeTo: zonedDateTime }
+ ),
+ -1,
+ "hours <, relativeTo ZonedDateTime"
+);
+assert.sameValue(
+ Temporal.Duration.compare(
+ new Temporal.Duration(2, 1, 3, 12, 6, 6),
+ new Temporal.Duration(2, 1, 3, 12, 6, 5),
+ { relativeTo: zonedDateTime }
+ ),
+ 1,
+ "minutes >, relativeTo ZonedDateTime"
+);
+assert.sameValue(
+ Temporal.Duration.compare(
+ new Temporal.Duration(4, 7, 2, 40, 12, 3),
+ new Temporal.Duration(4, 7, 2, 40, 12, 4),
+ { relativeTo: zonedDateTime }
+ ),
+ -1,
+ "minutes <, relativeTo ZonedDateTime"
+);
+assert.sameValue(
+ Temporal.Duration.compare(
+ new Temporal.Duration(2, 1, 3, 12, 6, 30, 6),
+ new Temporal.Duration(2, 1, 3, 12, 6, 30, 5),
+ { relativeTo: zonedDateTime }
+ ),
+ 1,
+ "seconds >, relativeTo ZonedDateTime"
+);
+assert.sameValue(
+ Temporal.Duration.compare(
+ new Temporal.Duration(4, 7, 2, 40, 12, 15, 3),
+ new Temporal.Duration(4, 7, 2, 40, 12, 15, 4),
+ { relativeTo: zonedDateTime }
+ ),
+ -1,
+ "seconds <, relativeTo ZonedDateTime"
+);
+assert.sameValue(
+ Temporal.Duration.compare(
+ new Temporal.Duration(2, 1, 3, 12, 6, 30, 15, 6),
+ new Temporal.Duration(2, 1, 3, 12, 6, 30, 15, 5),
+ { relativeTo: zonedDateTime }
+ ),
+ 1,
+ "milliseconds >, relativeTo ZonedDateTime"
+);
+assert.sameValue(
+ Temporal.Duration.compare(
+ new Temporal.Duration(4, 7, 2, 40, 12, 15, 45, 3),
+ new Temporal.Duration(4, 7, 2, 40, 12, 15, 45, 4),
+ { relativeTo: zonedDateTime }
+ ),
+ -1,
+ "milliseconds <, relativeTo ZonedDateTime"
+);
+assert.sameValue(
+ Temporal.Duration.compare(
+ new Temporal.Duration(2, 1, 3, 12, 6, 30, 15, 222, 6),
+ new Temporal.Duration(2, 1, 3, 12, 6, 30, 15, 222, 5),
+ { relativeTo: zonedDateTime }
+ ),
+ 1,
+ "microseconds >, relativeTo ZonedDateTime"
+);
+assert.sameValue(
+ Temporal.Duration.compare(
+ new Temporal.Duration(4, 7, 2, 40, 12, 15, 45, 333, 3),
+ new Temporal.Duration(4, 7, 2, 40, 12, 15, 45, 333, 4),
+ { relativeTo: zonedDateTime }
+ ),
+ -1,
+ "microseconds <, relativeTo ZonedDateTime"
+);
+assert.sameValue(
+ Temporal.Duration.compare(
+ new Temporal.Duration(2, 1, 3, 12, 6, 30, 15, 222, 444, 6),
+ new Temporal.Duration(2, 1, 3, 12, 6, 30, 15, 222, 444, 5),
+ { relativeTo: zonedDateTime }
+ ),
+ 1,
+ "nanoseconds >, relativeTo ZonedDateTime"
+);
+assert.sameValue(
+ Temporal.Duration.compare(
+ new Temporal.Duration(4, 7, 2, 40, 12, 15, 45, 333, 777, 3),
+ new Temporal.Duration(4, 7, 2, 40, 12, 15, 45, 333, 777, 4),
+ { relativeTo: zonedDateTime }
+ ),
+ -1,
+ "nanoseconds <, relativeTo ZonedDateTime"
+);
+assert.sameValue(
+ Temporal.Duration.compare(
+ new Temporal.Duration(4, 7, 2, 40, 12, 15, 45, 333, 777, 111),
+ new Temporal.Duration(4, 7, 2, 40, 12, 15, 45, 333, 777, 111),
+ { relativeTo: zonedDateTime }
+ ),
+ 0,
+ "equal, relativeTo ZonedDateTime"
+);
+
+assert.sameValue(
+ Temporal.Duration.compare(
+ new Temporal.Duration(0, 0, 0, 6),
+ new Temporal.Duration(0, 0, 0, 5)
+ ),
+ 1,
+ "days >, relativeTo nothing"
+);
+assert.sameValue(
+ Temporal.Duration.compare(
+ new Temporal.Duration(0, 0, 0, 3),
+ new Temporal.Duration(0, 0, 0, 4)
+ ),
+ -1,
+ "days <, relativeTo nothing"
+);
+assert.sameValue(
+ Temporal.Duration.compare(
+ new Temporal.Duration(0, 0, 0, 12, 6),
+ new Temporal.Duration(0, 0, 0, 12, 5)
+ ),
+ 1,
+ "hours >, relativeTo nothing"
+);
+assert.sameValue(
+ Temporal.Duration.compare(
+ new Temporal.Duration(0, 0, 0, 40, 3),
+ new Temporal.Duration(0, 0, 0, 40, 4)
+ ),
+ -1,
+ "hours <, relativeTo nothing"
+);
+assert.sameValue(
+ Temporal.Duration.compare(
+ new Temporal.Duration(0, 0, 0, 12, 6, 6),
+ new Temporal.Duration(0, 0, 0, 12, 6, 5)
+ ),
+ 1,
+ "minutes >, relativeTo nothing"
+);
+assert.sameValue(
+ Temporal.Duration.compare(
+ new Temporal.Duration(0, 0, 0, 40, 12, 3),
+ new Temporal.Duration(0, 0, 0, 40, 12, 4)
+ ),
+ -1,
+ "minutes <, relativeTo nothing"
+);
+assert.sameValue(
+ Temporal.Duration.compare(
+ new Temporal.Duration(0, 0, 0, 12, 6, 30, 6),
+ new Temporal.Duration(0, 0, 0, 12, 6, 30, 5)
+ ),
+ 1,
+ "seconds >, relativeTo nothing"
+);
+assert.sameValue(
+ Temporal.Duration.compare(
+ new Temporal.Duration(0, 0, 0, 40, 12, 15, 3),
+ new Temporal.Duration(0, 0, 0, 40, 12, 15, 4)
+ ),
+ -1,
+ "seconds <, relativeTo nothing"
+);
+assert.sameValue(
+ Temporal.Duration.compare(
+ new Temporal.Duration(0, 0, 0, 12, 6, 30, 15, 6),
+ new Temporal.Duration(0, 0, 0, 12, 6, 30, 15, 5)
+ ),
+ 1,
+ "milliseconds >, relativeTo nothing"
+);
+assert.sameValue(
+ Temporal.Duration.compare(
+ new Temporal.Duration(0, 0, 0, 40, 12, 15, 45, 3),
+ new Temporal.Duration(0, 0, 0, 40, 12, 15, 45, 4)
+ ),
+ -1,
+ "milliseconds <, relativeTo nothing"
+);
+assert.sameValue(
+ Temporal.Duration.compare(
+ new Temporal.Duration(0, 0, 0, 12, 6, 30, 15, 222, 6),
+ new Temporal.Duration(0, 0, 0, 12, 6, 30, 15, 222, 5)
+ ),
+ 1,
+ "microseconds >, relativeTo nothing"
+);
+assert.sameValue(
+ Temporal.Duration.compare(
+ new Temporal.Duration(0, 0, 0, 40, 12, 15, 45, 333, 3),
+ new Temporal.Duration(0, 0, 0, 40, 12, 15, 45, 333, 4)
+ ),
+ -1,
+ "microseconds <, relativeTo nothing"
+);
+assert.sameValue(
+ Temporal.Duration.compare(
+ new Temporal.Duration(0, 0, 0, 12, 6, 30, 15, 222, 444, 6),
+ new Temporal.Duration(0, 0, 0, 12, 6, 30, 15, 222, 444, 5)
+ ),
+ 1,
+ "nanoseconds >, relativeTo nothing"
+);
+assert.sameValue(
+ Temporal.Duration.compare(
+ new Temporal.Duration(0, 0, 0, 40, 12, 15, 45, 333, 777, 3),
+ new Temporal.Duration(0, 0, 0, 40, 12, 15, 45, 333, 777, 4)
+ ),
+ -1,
+ "nanoseconds <, relativeTo nothing"
+);
+assert.sameValue(
+ Temporal.Duration.compare(
+ new Temporal.Duration(0, 0, 0, 40, 12, 15, 45, 333, 777, 111),
+ new Temporal.Duration(0, 0, 0, 40, 12, 15, 45, 333, 777, 111)
+ ),
+ 0,
+ "equal, relativeTo nothing"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/instances-identical.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/instances-identical.js
new file mode 100644
index 0000000000..8e2f95d88a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/instances-identical.js
@@ -0,0 +1,47 @@
+// |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.duration.compare
+description: >
+ Shortcut return with no observable user code calls when two Temporal.Duration
+ have identical internal slots
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const duration1 = new Temporal.Duration(0, 0, 0, 5, 5, 5, 5, 5, 5, 5);
+const duration2 = new Temporal.Duration(0, 0, 0, 5, 5, 5, 5, 5, 5, 5);
+assert.sameValue(Temporal.Duration.compare(duration1, duration2), 0, "identical Duration instances should be equal");
+
+const dateDuration1 = new Temporal.Duration(5, 5, 5, 5, 5, 5, 5, 5, 5, 5);
+const dateDuration2 = new Temporal.Duration(5, 5, 5, 5, 5, 5, 5, 5, 5, 5);
+assert.sameValue(
+ Temporal.Duration.compare(dateDuration1, dateDuration2),
+ 0,
+ "relativeTo is not required if two distinct Duration instances are identical"
+);
+
+const calendar = TemporalHelpers.calendarThrowEverything();
+const relativeTo = new Temporal.PlainDate(2000, 1, 1, calendar);
+assert.sameValue(
+ Temporal.Duration.compare(dateDuration1, dateDuration2, { relativeTo }),
+ 0,
+ "no calendar methods are called if two distinct Duration instances are identical"
+);
+
+const dateDuration3 = new Temporal.Duration(5, 5, 5, 5, 4, 65, 5, 5, 5, 5);
+assert.throws(
+ RangeError,
+ () => Temporal.Duration.compare(dateDuration1, dateDuration3),
+ "relativeTo is required if two Duration instances are the same length but not identical"
+);
+
+assert.throws(
+ Test262Error,
+ () => Temporal.Duration.compare(dateDuration1, dateDuration3, { relativeTo }),
+ "calendar methods are called if two Duration instances are the same length but not identical"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/length.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/length.js
new file mode 100644
index 0000000000..9a8e1d5697
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/length.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.duration.compare
+description: Temporal.Duration.compare.length is 2
+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.Duration.compare, "length", {
+ value: 2,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/name.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/name.js
new file mode 100644
index 0000000000..240758f60e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/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.duration.compare
+description: Temporal.Duration.compare.name is "compare".
+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.Duration.compare, "name", {
+ value: "compare",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/not-a-constructor.js
new file mode 100644
index 0000000000..2c4dadec93
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/not-a-constructor.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.duration.compare
+description: Temporal.Duration.compare 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.Duration.compare();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Duration.compare), false,
+ "isConstructor(Temporal.Duration.compare)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/options-object.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/options-object.js
new file mode 100644
index 0000000000..b2a08779e8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/options-object.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.duration.prototype.compare
+description: Empty object may be used as options
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ Temporal.Duration.compare({ hours: 1 }, { minutes: 60 }, {}), 0,
+ "options may be an empty plain object"
+);
+
+assert.sameValue(
+ Temporal.Duration.compare({ hours: 1 }, { minutes:60 }, () => {}), 0,
+ "options may be an empty function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/options-undefined.js
new file mode 100644
index 0000000000..214a16df84
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/options-undefined.js
@@ -0,0 +1,16 @@
+// |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.duration.compare
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const duration1 = new Temporal.Duration(1);
+const duration2 = new Temporal.Duration(0, 12);
+assert.throws(RangeError, () => Temporal.Duration.compare(duration1, duration2), "default relativeTo is undefined");
+assert.throws(RangeError, () => Temporal.Duration.compare(duration1, duration2, undefined), "default relativeTo is undefined");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/options-wrong-type.js
new file mode 100644
index 0000000000..dad31eab09
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/options-wrong-type.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.duration.compare
+description: TypeError thrown when options argument is a primitive
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const badOptions = [
+ null,
+ true,
+ "some string",
+ Symbol(),
+ 1,
+ 2n,
+];
+
+for (const value of badOptions) {
+ assert.throws(TypeError, () => Temporal.Duration.compare({ hours: 1 }, { minutes: 60 }, value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/order-of-operations.js
new file mode 100644
index 0000000000..ad9c408f2f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/order-of-operations.js
@@ -0,0 +1,310 @@
+// |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.duration.compare
+description: Properties on objects passed to compare() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ // ToTemporalDuration on first argument
+ "get one.days",
+ "get one.days.valueOf",
+ "call one.days.valueOf",
+ "get one.hours",
+ "get one.hours.valueOf",
+ "call one.hours.valueOf",
+ "get one.microseconds",
+ "get one.microseconds.valueOf",
+ "call one.microseconds.valueOf",
+ "get one.milliseconds",
+ "get one.milliseconds.valueOf",
+ "call one.milliseconds.valueOf",
+ "get one.minutes",
+ "get one.minutes.valueOf",
+ "call one.minutes.valueOf",
+ "get one.months",
+ "get one.months.valueOf",
+ "call one.months.valueOf",
+ "get one.nanoseconds",
+ "get one.nanoseconds.valueOf",
+ "call one.nanoseconds.valueOf",
+ "get one.seconds",
+ "get one.seconds.valueOf",
+ "call one.seconds.valueOf",
+ "get one.weeks",
+ "get one.weeks.valueOf",
+ "call one.weeks.valueOf",
+ "get one.years",
+ "get one.years.valueOf",
+ "call one.years.valueOf",
+ // ToTemporalDuration on second argument
+ "get two.days",
+ "get two.days.valueOf",
+ "call two.days.valueOf",
+ "get two.hours",
+ "get two.hours.valueOf",
+ "call two.hours.valueOf",
+ "get two.microseconds",
+ "get two.microseconds.valueOf",
+ "call two.microseconds.valueOf",
+ "get two.milliseconds",
+ "get two.milliseconds.valueOf",
+ "call two.milliseconds.valueOf",
+ "get two.minutes",
+ "get two.minutes.valueOf",
+ "call two.minutes.valueOf",
+ "get two.months",
+ "get two.months.valueOf",
+ "call two.months.valueOf",
+ "get two.nanoseconds",
+ "get two.nanoseconds.valueOf",
+ "call two.nanoseconds.valueOf",
+ "get two.seconds",
+ "get two.seconds.valueOf",
+ "call two.seconds.valueOf",
+ "get two.weeks",
+ "get two.weeks.valueOf",
+ "call two.weeks.valueOf",
+ "get two.years",
+ "get two.years.valueOf",
+ "call two.years.valueOf",
+ // ToRelativeTemporalObject
+ "get options.relativeTo",
+];
+const actual = [];
+
+// basic order of observable operations with no relativeTo
+Temporal.Duration.compare(
+ createDurationPropertyBagObserver("one", 0, 0, 0, 7),
+ createDurationPropertyBagObserver("two", 0, 0, 0, 6),
+ createOptionsObserver(undefined)
+);
+assert.compareArray(actual, expected, "order of operations");
+actual.splice(0); // clear
+
+const baseExpectedOpsWithRelativeTo = expected.concat([
+ // ToRelativeTemporalObject
+ "get options.relativeTo.calendar",
+ "has options.relativeTo.calendar.dateAdd",
+ "has options.relativeTo.calendar.dateFromFields",
+ "has options.relativeTo.calendar.dateUntil",
+ "has options.relativeTo.calendar.day",
+ "has options.relativeTo.calendar.dayOfWeek",
+ "has options.relativeTo.calendar.dayOfYear",
+ "has options.relativeTo.calendar.daysInMonth",
+ "has options.relativeTo.calendar.daysInWeek",
+ "has options.relativeTo.calendar.daysInYear",
+ "has options.relativeTo.calendar.fields",
+ "has options.relativeTo.calendar.id",
+ "has options.relativeTo.calendar.inLeapYear",
+ "has options.relativeTo.calendar.mergeFields",
+ "has options.relativeTo.calendar.month",
+ "has options.relativeTo.calendar.monthCode",
+ "has options.relativeTo.calendar.monthDayFromFields",
+ "has options.relativeTo.calendar.monthsInYear",
+ "has options.relativeTo.calendar.weekOfYear",
+ "has options.relativeTo.calendar.year",
+ "has options.relativeTo.calendar.yearMonthFromFields",
+ "has options.relativeTo.calendar.yearOfWeek",
+ "get options.relativeTo.calendar.dateFromFields",
+ "get options.relativeTo.calendar.fields",
+ "call options.relativeTo.calendar.fields",
+ "get options.relativeTo.day",
+ "get options.relativeTo.day.valueOf",
+ "call options.relativeTo.day.valueOf",
+ "get options.relativeTo.hour",
+]);
+
+const expectedOpsForPlainRelativeTo = baseExpectedOpsWithRelativeTo.concat([
+ // ToRelativeTemporalObject, continued
+ "get options.relativeTo.microsecond",
+ "get options.relativeTo.millisecond",
+ "get options.relativeTo.minute",
+ "get options.relativeTo.month",
+ "get options.relativeTo.month.valueOf",
+ "call options.relativeTo.month.valueOf",
+ "get options.relativeTo.monthCode",
+ "get options.relativeTo.monthCode.toString",
+ "call options.relativeTo.monthCode.toString",
+ "get options.relativeTo.nanosecond",
+ "get options.relativeTo.offset",
+ "get options.relativeTo.second",
+ "get options.relativeTo.timeZone",
+ "get options.relativeTo.year",
+ "get options.relativeTo.year.valueOf",
+ "call options.relativeTo.year.valueOf",
+ "call options.relativeTo.calendar.dateFromFields",
+ // lookup in Duration.compare
+ "get options.relativeTo.calendar.dateAdd",
+]);
+
+const plainRelativeTo = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2001,
+ month: 5,
+ monthCode: "M05",
+ day: 2,
+ calendar: TemporalHelpers.calendarObserver(actual, "options.relativeTo.calendar"),
+}, "options.relativeTo");
+
+function createOptionsObserver(relativeTo = undefined) {
+ return TemporalHelpers.propertyBagObserver(actual, { relativeTo }, "options");
+}
+
+function createDurationPropertyBagObserver(name, y = 0, mon = 0, w = 0, d = 0, h = 0, min = 0, s = 0, ms = 0, µs = 0, ns = 0) {
+ return TemporalHelpers.propertyBagObserver(actual, {
+ years: y,
+ months: mon,
+ weeks: w,
+ days: d,
+ hours: h,
+ minutes: min,
+ seconds: s,
+ milliseconds: ms,
+ microseconds: µs,
+ nanoseconds: ns,
+ }, name);
+}
+
+// order of observable operations with plain relativeTo and without calendar units
+Temporal.Duration.compare(
+ createDurationPropertyBagObserver("one", 0, 0, 0, 7),
+ createDurationPropertyBagObserver("two", 0, 0, 0, 6),
+ createOptionsObserver(plainRelativeTo)
+);
+assert.compareArray(actual, expectedOpsForPlainRelativeTo, "order of operations with PlainDate relativeTo and no calendar units");
+actual.splice(0); // clear
+
+// code path through UnbalanceDurationRelative that balances higher units down
+// to days:
+const expectedOpsForPlainDayBalancing = expectedOpsForPlainRelativeTo.concat(
+ [
+ "call options.relativeTo.calendar.dateAdd", // UnbalanceDateDurationRelative on 1st argument
+ "call options.relativeTo.calendar.dateAdd", // UnbalanceDateDurationRelative on 2nd argument
+ ]
+);
+Temporal.Duration.compare(
+ createDurationPropertyBagObserver("one", 1, 1, 1),
+ createDurationPropertyBagObserver("two", 1, 1, 1, 1),
+ createOptionsObserver(plainRelativeTo)
+);
+assert.compareArray(actual, expectedOpsForPlainDayBalancing, "order of operations with PlainDate relativeTo and calendar units");
+actual.splice(0); // clear
+
+const expectedOpsForZonedRelativeTo = baseExpectedOpsWithRelativeTo.concat([
+ // ToRelativeTemporalObject, continued
+ "get options.relativeTo.hour.valueOf",
+ "call options.relativeTo.hour.valueOf",
+ "get options.relativeTo.microsecond",
+ "get options.relativeTo.microsecond.valueOf",
+ "call options.relativeTo.microsecond.valueOf",
+ "get options.relativeTo.millisecond",
+ "get options.relativeTo.millisecond.valueOf",
+ "call options.relativeTo.millisecond.valueOf",
+ "get options.relativeTo.minute",
+ "get options.relativeTo.minute.valueOf",
+ "call options.relativeTo.minute.valueOf",
+ "get options.relativeTo.month",
+ "get options.relativeTo.month.valueOf",
+ "call options.relativeTo.month.valueOf",
+ "get options.relativeTo.monthCode",
+ "get options.relativeTo.monthCode.toString",
+ "call options.relativeTo.monthCode.toString",
+ "get options.relativeTo.nanosecond",
+ "get options.relativeTo.nanosecond.valueOf",
+ "call options.relativeTo.nanosecond.valueOf",
+ "get options.relativeTo.offset",
+ "get options.relativeTo.offset.toString",
+ "call options.relativeTo.offset.toString",
+ "get options.relativeTo.second",
+ "get options.relativeTo.second.valueOf",
+ "call options.relativeTo.second.valueOf",
+ "get options.relativeTo.timeZone",
+ "get options.relativeTo.year",
+ "get options.relativeTo.year.valueOf",
+ "call options.relativeTo.year.valueOf",
+ "call options.relativeTo.calendar.dateFromFields",
+ "has options.relativeTo.timeZone.getOffsetNanosecondsFor",
+ "has options.relativeTo.timeZone.getPossibleInstantsFor",
+ "has options.relativeTo.timeZone.id",
+ "get options.relativeTo.timeZone.getOffsetNanosecondsFor",
+ "get options.relativeTo.timeZone.getPossibleInstantsFor",
+ "call options.relativeTo.timeZone.getPossibleInstantsFor",
+ "call options.relativeTo.timeZone.getOffsetNanosecondsFor",
+ // lookup in Duration.compare
+ "get options.relativeTo.calendar.dateAdd",
+]);
+
+const zonedRelativeTo = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2001,
+ month: 5,
+ monthCode: "M05",
+ day: 2,
+ hour: 6,
+ minute: 54,
+ second: 32,
+ millisecond: 987,
+ microsecond: 654,
+ nanosecond: 321,
+ offset: "+00:00",
+ calendar: TemporalHelpers.calendarObserver(actual, "options.relativeTo.calendar"),
+ timeZone: TemporalHelpers.timeZoneObserver(actual, "options.relativeTo.timeZone"),
+}, "options.relativeTo");
+
+// order of observable operations with zoned relativeTo and without calendar units except days
+Temporal.Duration.compare(
+ createDurationPropertyBagObserver("one", 0, 0, 0, 7),
+ createDurationPropertyBagObserver("two", 0, 0, 0, 6),
+ createOptionsObserver(zonedRelativeTo)
+);
+assert.compareArray(
+ actual,
+ expectedOpsForZonedRelativeTo.concat([
+ "call options.relativeTo.timeZone.getOffsetNanosecondsFor",
+ // AddDaysToZonedDateTime on first argument
+ "call options.relativeTo.timeZone.getPossibleInstantsFor",
+ // AddDaysToZonedDateTime on second argument
+ "call options.relativeTo.timeZone.getPossibleInstantsFor",
+ ]),
+ "order of operations with ZonedDateTime relativeTo and no calendar units except days"
+);
+actual.splice(0); // clear
+
+// order of observable operations with zoned relativeTo and with only time units
+Temporal.Duration.compare(
+ createDurationPropertyBagObserver("one", 0, 0, 0, 0, 7),
+ createDurationPropertyBagObserver("two", 0, 0, 0, 0, 6),
+ createOptionsObserver(zonedRelativeTo)
+);
+assert.compareArray(
+ actual,
+ expectedOpsForZonedRelativeTo,
+ "order of operations with ZonedDateTime relativeTo and only time units"
+);
+actual.splice(0); // clear
+
+// order of observable operations with zoned relativeTo and calendar units
+Temporal.Duration.compare(
+ createDurationPropertyBagObserver("one", 1, 1, 1),
+ createDurationPropertyBagObserver("two", 1, 1, 1, 1),
+ createOptionsObserver(zonedRelativeTo)
+);
+assert.compareArray(
+ actual,
+ expectedOpsForZonedRelativeTo.concat([
+ "call options.relativeTo.timeZone.getOffsetNanosecondsFor",
+ // AddZonedDateTime on first argument
+ "call options.relativeTo.calendar.dateAdd",
+ "call options.relativeTo.timeZone.getPossibleInstantsFor",
+ // AddZonedDateTime on second argument
+ "call options.relativeTo.calendar.dateAdd",
+ "call options.relativeTo.timeZone.getPossibleInstantsFor",
+ ]),
+ "order of operations with ZonedDateTime relativeTo and calendar units"
+);
+actual.splice(0); // clear
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/prop-desc.js
new file mode 100644
index 0000000000..1a290efaff
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/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.duration.compare
+description: The "compare" property of Temporal.Duration
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Duration.compare,
+ "function",
+ "`typeof Duration.compare` is `function`"
+);
+
+verifyProperty(Temporal.Duration, "compare", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/proto-in-calendar-fields.js
new file mode 100644
index 0000000000..7b7201144f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/proto-in-calendar-fields.js
@@ -0,0 +1,18 @@
+// |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.duration.prototype.compare
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const relativeTo = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar, timeZone: 'Europe/Paris' };
+const duration1 = new Temporal.Duration(1);
+const duration2 = new Temporal.Duration(0, 12);
+
+assert.throws(RangeError, () => Temporal.Duration.compare(duration1, duration2, {relativeTo: relativeTo}));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/read-time-fields-before-datefromfields.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/read-time-fields-before-datefromfields.js
new file mode 100644
index 0000000000..e86754a77d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/read-time-fields-before-datefromfields.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.duration.compare
+description: The time fields are read from the object before being passed to dateFromFields().
+info: |
+ sec-temporal.duration.compare step 4:
+ 4. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_).
+ sec-temporal-torelativetemporalobject step 4.g:
+ g. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+ sec-temporal-interprettemporaldatetimefields steps 1–2:
+ 1. Let _timeResult_ be ? ToTemporalTimeRecord(_fields_).
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarMakeInvalidGettersTime();
+const duration1 = new Temporal.Duration(1);
+const duration2 = new Temporal.Duration(0, 12);
+Temporal.Duration.compare(duration1, duration2, { relativeTo: { year: 2000, month: 1, day: 1, calendar } });
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-hour.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-hour.js
new file mode 100644
index 0000000000..3f3ab60ce2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-hour.js
@@ -0,0 +1,44 @@
+// |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.duration.compare
+description: relativeTo with hours.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const oneDay = new Temporal.Duration(0, 0, 0, 1);
+const hours24 = new Temporal.Duration(0, 0, 0, 0, 24);
+assert.sameValue(Temporal.Duration.compare(oneDay, hours24),
+ 0,
+ "relativeTo not required for days");
+assert.sameValue(
+ Temporal.Duration.compare(oneDay, hours24, { relativeTo: Temporal.PlainDate.from("2017-01-01") }),
+ 0,
+ "relativeTo does not affect days if PlainDate");
+assert.sameValue(Temporal.Duration.compare(oneDay, hours24, { relativeTo: "2019-11-03" }),
+ 0,
+ "casts relativeTo to PlainDate from string");
+assert.sameValue(Temporal.Duration.compare(oneDay, hours24, { relativeTo: { year: 2019, month: 11, day: 3 } }),
+ 0,
+ "casts relativeTo to PlainDate from object");
+
+const timeZone = TemporalHelpers.springForwardFallBackTimeZone();
+assert.sameValue(
+ Temporal.Duration.compare(oneDay, hours24, { relativeTo: new Temporal.ZonedDateTime(0n, timeZone) }),
+ 0,
+ 'relativeTo does not affect days if ZonedDateTime, and duration encompasses no DST change');
+assert.sameValue(
+ Temporal.Duration.compare(oneDay, hours24, { relativeTo: new Temporal.ZonedDateTime(972802800_000_000_000n, timeZone) }),
+ 1,
+ 'relativeTo does affect days if ZonedDateTime, and duration encompasses DST change');
+assert.sameValue(
+ Temporal.Duration.compare(oneDay, hours24, {
+ relativeTo: { year: 2000, month: 10, day: 29, timeZone }
+ }),
+ 1,
+ 'casts relativeTo to ZonedDateTime from object');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-month.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-month.js
new file mode 100644
index 0000000000..c8b1e036cd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-month.js
@@ -0,0 +1,20 @@
+// |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.duration.compare
+description: relativeTo with months.
+features: [Temporal]
+---*/
+
+const oneMonth = new Temporal.Duration(0, 1);
+const days30 = new Temporal.Duration(0, 0, 0, 30);
+assert.sameValue(
+ Temporal.Duration.compare(oneMonth, days30, { relativeTo: Temporal.PlainDate.from("2018-04-01") }), 0);
+assert.sameValue(
+ Temporal.Duration.compare(oneMonth, days30, { relativeTo: Temporal.PlainDate.from("2018-03-01") }), 1);
+assert.sameValue(
+ Temporal.Duration.compare(oneMonth, days30, { relativeTo: Temporal.PlainDate.from("2018-02-01") }), -1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-plaindate-add24hourdaystonormalizedtimeduration-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-plaindate-add24hourdaystonormalizedtimeduration-out-of-range.js
new file mode 100644
index 0000000000..9a7a0d6ef4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-plaindate-add24hourdaystonormalizedtimeduration-out-of-range.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2024 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.compare
+description: RangeError thrown if adding the duration to the relativeTo date would result in anout-of-range date-time
+features: [Temporal]
+---*/
+
+let duration1 = Temporal.Duration.from({
+ years: 1,
+ seconds: 2**53 - 1,
+});
+let duration2 = Temporal.Duration.from({
+ years: 2,
+});
+let relativeTo = new Temporal.PlainDate(2000, 1, 1);
+
+assert.throws(RangeError, () => Temporal.Duration.compare(duration1, duration2, { relativeTo }));
+assert.throws(RangeError, () => Temporal.Duration.compare(duration2, duration1, { relativeTo }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-ambiguous-wall-clock-time.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-ambiguous-wall-clock-time.js
new file mode 100644
index 0000000000..d39faa6fe5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-ambiguous-wall-clock-time.js
@@ -0,0 +1,93 @@
+// |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.duration.compare
+description: >
+ Correct time zone calls are made when converting a ZonedDateTime-like
+ relativeTo property bag denoting an ambiguous wall-clock time
+includes: [temporalHelpers.js, compareArray.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+
+const duration1 = new Temporal.Duration(0, 0, 0, 1);
+const duration2 = new Temporal.Duration(0, 0, 0, 2);
+
+const dstTimeZone = TemporalHelpers.springForwardFallBackTimeZone();
+const dstTimeZoneObserver = TemporalHelpers.timeZoneObserver(actual, "timeZone", {
+ getOffsetNanosecondsFor: dstTimeZone.getOffsetNanosecondsFor.bind(dstTimeZone),
+ getPossibleInstantsFor: dstTimeZone.getPossibleInstantsFor.bind(dstTimeZone),
+});
+const calendar = TemporalHelpers.calendarObserver(actual, "calendar");
+
+let relativeTo = { year: 2000, month: 4, day: 2, hour: 2, minute: 30, timeZone: dstTimeZoneObserver, calendar };
+Temporal.Duration.compare(duration1, duration2, {relativeTo: relativeTo});
+
+const expected = [
+ // GetTemporalCalendarSlotValueWithISODefault
+ "has calendar.dateAdd",
+ "has calendar.dateFromFields",
+ "has calendar.dateUntil",
+ "has calendar.day",
+ "has calendar.dayOfWeek",
+ "has calendar.dayOfYear",
+ "has calendar.daysInMonth",
+ "has calendar.daysInWeek",
+ "has calendar.daysInYear",
+ "has calendar.fields",
+ "has calendar.id",
+ "has calendar.inLeapYear",
+ "has calendar.mergeFields",
+ "has calendar.month",
+ "has calendar.monthCode",
+ "has calendar.monthDayFromFields",
+ "has calendar.monthsInYear",
+ "has calendar.weekOfYear",
+ "has calendar.year",
+ "has calendar.yearMonthFromFields",
+ "has calendar.yearOfWeek",
+ // lookup
+ "get calendar.dateFromFields",
+ "get calendar.fields",
+ // CalendarFields
+ "call calendar.fields",
+ // InterpretTemporalDateTimeFields
+ "call calendar.dateFromFields",
+ // ToTemporalTimeZoneSlotValue
+ "has timeZone.getOffsetNanosecondsFor",
+ "has timeZone.getPossibleInstantsFor",
+ "has timeZone.id",
+ // lookup
+ "get timeZone.getOffsetNanosecondsFor",
+ "get timeZone.getPossibleInstantsFor",
+ // InterpretISODateTimeOffset
+ "call timeZone.getPossibleInstantsFor",
+];
+
+const expectedSpringForward = expected.concat([
+ // DisambiguatePossibleInstants
+ "call timeZone.getOffsetNanosecondsFor",
+ "call timeZone.getOffsetNanosecondsFor",
+ "call timeZone.getPossibleInstantsFor",
+]);
+assert.compareArray(
+ actual.slice(0, expectedSpringForward.length), // ignore operations after ToRelativeTemporalObject
+ expectedSpringForward,
+ "order of operations converting property bag at skipped wall-clock time"
+);
+actual.splice(0); // clear
+
+relativeTo = { year: 2000, month: 10, day: 29, hour: 1, minute: 30, timeZone: dstTimeZoneObserver, calendar };
+Temporal.Duration.compare(duration1, duration2, {relativeTo: relativeTo});
+
+assert.compareArray(
+ actual.slice(0, expected.length), // ignore operations after ToRelativeTemporalObject
+ expected,
+ "order of operations converting property bag at repeated wall-clock time"
+);
+actual.splice(0); // clear
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..896a5947bb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-builtin-calendar-no-array-iteration.js
@@ -0,0 +1,26 @@
+// |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.duration.compare
+description: >
+ Calling the method with a relativeTo property bag with a builtin calendar
+ causes no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const timeZone = "UTC";
+const relativeTo = { year: 2000, month: 5, day: 2, hour: 21, minute: 43, second: 5, timeZone, calendar: "iso8601" };
+const duration1 = new Temporal.Duration(0, 0, 1);
+const duration2 = new Temporal.Duration(0, 0, 0, 7);
+Temporal.Duration.compare(duration1, duration2, { relativeTo });
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-calendar-string.js
new file mode 100644
index 0000000000..ae4913a997
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-calendar-string.js
@@ -0,0 +1,28 @@
+// |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.duration.compare
+description: >
+ Builtin dateFromFields method is not observably called when the property bag
+ has a string-valued calendar property
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dateFromFieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dateFromFields");
+Object.defineProperty(Temporal.Calendar.prototype, "dateFromFields", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("dateFromFields should not be looked up");
+ },
+});
+
+const relativeTo = { year: 2000, month: 5, day: 2, calendar: "iso8601" };
+Temporal.Duration.compare(new Temporal.Duration(), new Temporal.Duration(), { relativeTo });
+
+Object.defineProperty(Temporal.Calendar.prototype, "dateFromFields", dateFromFieldsOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-getpossibleinstantsfor-called-with-iso8601-calendar.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-getpossibleinstantsfor-called-with-iso8601-calendar.js
new file mode 100644
index 0000000000..ff04cbc3f2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-getpossibleinstantsfor-called-with-iso8601-calendar.js
@@ -0,0 +1,58 @@
+// |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.duration.prototype.compare
+description: >
+ Time zone's getPossibleInstantsFor is called with a PlainDateTime with the
+ built-in ISO 8601 calendar
+features: [Temporal]
+info: |
+ DisambiguatePossibleInstants:
+ 2. Let _n_ be _possibleInstants_'s length.
+ ...
+ 5. Assert: _n_ = 0.
+ ...
+ 19. If _disambiguation_ is *"earlier"*, then
+ ...
+ c. Let _earlierDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*).
+ d. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _earlierDateTime_).
+ ...
+ 20. Assert: _disambiguation_ is *"compatible"* or *"later"*.
+ ...
+ 23. Let _laterDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*).
+ 24. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _laterDateTime_).
+---*/
+
+class SkippedDateTime extends Temporal.TimeZone {
+ constructor() {
+ super("UTC");
+ this.calls = 0;
+ }
+
+ getPossibleInstantsFor(dateTime) {
+ // Calls occur in pairs. For the first one return no possible instants so
+ // that DisambiguatePossibleInstants will call it again
+ if (this.calls++ % 2 == 0) {
+ return [];
+ }
+
+ assert.sameValue(
+ dateTime.getISOFields().calendar,
+ "iso8601",
+ "getPossibleInstantsFor called with dateTime with built-in ISO 8601 calendar"
+ );
+ return super.getPossibleInstantsFor(dateTime);
+ }
+}
+
+const nonBuiltinISOCalendar = new Temporal.Calendar("iso8601");
+const timeZone = new SkippedDateTime();
+const relativeTo = { year: 2000, month: 5, day: 2, timeZone, calendar: nonBuiltinISOCalendar };
+
+Temporal.Duration.compare(new Temporal.Duration(1), new Temporal.Duration(2), { relativeTo });
+
+assert.sameValue(timeZone.calls, 6, "getPossibleInstantsFor should have been called 6 times");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..e5d0dc51ab
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-infinity-throws-rangeerror.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.
+
+/*---
+description: Throws if any value in the relativeTo property bag is Infinity or -Infinity
+esid: sec-temporal.duration.compare
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const duration1 = new Temporal.Duration(0, 0, 0, 1);
+const duration2 = new Temporal.Duration(0, 0, 0, 0, 24);
+const base = { year: 2000, month: 5, day: 2, hour: 15, minute: 30, second: 45, millisecond: 987, microsecond: 654, nanosecond: 321 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((prop) => {
+ assert.throws(RangeError, () => Temporal.Duration.compare(duration1, duration2, { relativeTo: { ...base, [prop]: inf } }), `${prop} property cannot be ${inf} in relativeTo`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => Temporal.Duration.compare(duration1, duration2, { relativeTo: { ...base, [prop]: obj } }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-invalid-offset-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-invalid-offset-string.js
new file mode 100644
index 0000000000..d4850eede2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-invalid-offset-string.js
@@ -0,0 +1,33 @@
+// |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.duration.compare
+description: relativeTo property bag with offset property is rejected if offset is in the wrong format
+features: [Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const d1 = new Temporal.Duration(0, 1, 0, 280);
+const d2 = new Temporal.Duration(0, 1, 0, 281);
+
+const badOffsets = [
+ "00:00", // missing sign
+ "+0", // too short
+ "-000:00", // too long
+ 1000, // must be a string
+ null, // must be a string
+ true, // must be a string
+ 1000n, // must be a string
+];
+badOffsets.forEach((offset) => {
+ const relativeTo = { year: 2021, month: 10, day: 28, offset, timeZone };
+ assert.throws(
+ typeof(offset) === 'string' ? RangeError : TypeError,
+ () => Temporal.Duration.compare(d1, d2, { relativeTo }),
+ `"${offset} is not a valid offset string`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-invalid.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-invalid.js
new file mode 100644
index 0000000000..1432fa1d8f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-invalid.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.duration.compare
+description: Throws if a value in the relativeTo property bag is missing.
+features: [Temporal]
+---*/
+
+const oneDay = new Temporal.Duration(0, 0, 0, 1);
+const hours24 = new Temporal.Duration(0, 0, 0, 0, 24);
+assert.throws(TypeError, () => Temporal.Duration.compare(oneDay, hours24, { relativeTo: { month: 11, day: 3 } }), "missing year");
+assert.throws(TypeError, () => Temporal.Duration.compare(oneDay, hours24, { relativeTo: { year: 2019, month: 11 } }), "missing day");
+assert.throws(TypeError, () => Temporal.Duration.compare(oneDay, hours24, { relativeTo: { year: 2019, day: 3 } }), "missing month");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..328fdc33db
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-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.duration.compare
+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].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const duration1 = new Temporal.Duration(1);
+ const duration2 = new Temporal.Duration(1, 1);
+ assert.throws(RangeError, () => Temporal.Duration.compare(duration1, duration2, { relativeTo: { year: 2000, month: 5, day: 2, hour: 12, timeZone } }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..34e11e3d24
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-getoffsetnanosecondsfor-not-callable.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.duration.compare
+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 duration1 = new Temporal.Duration(1);
+ const duration2 = new Temporal.Duration(1, 1);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => Temporal.Duration.compare(duration1, duration2, { relativeTo: { year: 2000, month: 5, day: 2, hour: 12, timeZone } }),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..b7383a8351
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-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.duration.compare
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const duration1 = new Temporal.Duration(1);
+ const duration2 = new Temporal.Duration(1, 1);
+ assert.throws(RangeError, () => Temporal.Duration.compare(duration1, duration2, { relativeTo: { year: 2000, month: 5, day: 2, hour: 12, timeZone } }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..6da6385ecf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-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.duration.compare
+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 duration1 = new Temporal.Duration(1);
+ const duration2 = new Temporal.Duration(1, 1);
+ assert.throws(TypeError, () => Temporal.Duration.compare(duration1, duration2, { relativeTo: { year: 2000, month: 5, day: 2, hour: 12, timeZone } }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-string-datetime.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-string-datetime.js
new file mode 100644
index 0000000000..75f2ae0020
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-string-datetime.js
@@ -0,0 +1,64 @@
+// |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.duration.compare
+description: Conversion of ISO date-time strings to Temporal.TimeZone instances
+features: [Temporal]
+---*/
+
+let timeZone = "2021-08-19T17:30";
+assert.throws(RangeError, () => Temporal.Duration.compare(new Temporal.Duration(1), new Temporal.Duration(), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } }), "bare date-time string is not a time zone");
+
+[
+ "2021-08-19T17:30-07:00:01",
+ "2021-08-19T17:30-07:00:00",
+ "2021-08-19T17:30-07:00:00.1",
+ "2021-08-19T17:30-07:00:00.0",
+ "2021-08-19T17:30-07:00:00.01",
+ "2021-08-19T17:30-07:00:00.00",
+ "2021-08-19T17:30-07:00:00.001",
+ "2021-08-19T17:30-07:00:00.000",
+ "2021-08-19T17:30-07:00:00.0001",
+ "2021-08-19T17:30-07:00:00.0000",
+ "2021-08-19T17:30-07:00:00.00001",
+ "2021-08-19T17:30-07:00:00.00000",
+ "2021-08-19T17:30-07:00:00.000001",
+ "2021-08-19T17:30-07:00:00.000000",
+ "2021-08-19T17:30-07:00:00.0000001",
+ "2021-08-19T17:30-07:00:00.0000000",
+ "2021-08-19T17:30-07:00:00.00000001",
+ "2021-08-19T17:30-07:00:00.00000000",
+ "2021-08-19T17:30-07:00:00.000000001",
+ "2021-08-19T17:30-07:00:00.000000000",
+].forEach((timeZone) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.Duration.compare(new Temporal.Duration(1), new Temporal.Duration(), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } }),
+ `ISO string ${timeZone} with a sub-minute offset is not a valid time zone`
+ );
+});
+
+// The following are all valid strings so should not throw:
+
+[
+ "2021-08-19T17:30Z",
+ "2021-08-19T1730Z",
+ "2021-08-19T17:30-07:00",
+ "2021-08-19T1730-07:00",
+ "2021-08-19T17:30-0700",
+ "2021-08-19T1730-0700",
+ "2021-08-19T17:30[UTC]",
+ "2021-08-19T1730[UTC]",
+ "2021-08-19T17:30Z[UTC]",
+ "2021-08-19T1730Z[UTC]",
+ "2021-08-19T17:30-07:00[UTC]",
+ "2021-08-19T1730-07:00[UTC]",
+ "2021-08-19T17:30-0700[UTC]",
+ "2021-08-19T1730-0700[UTC]",
+].forEach((timeZone) => {
+ Temporal.Duration.compare(new Temporal.Duration(1), new Temporal.Duration(), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-string-leap-second.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-string-leap-second.js
new file mode 100644
index 0000000000..5afbc2c745
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-string-leap-second.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.duration.compare
+description: Leap second is a valid ISO string for TimeZone
+features: [Temporal]
+---*/
+
+let timeZone = "2016-12-31T23:59:60+00:00[UTC]";
+
+// A string with a leap second is a valid ISO string, so the following
+// operation should not throw
+
+Temporal.Duration.compare(new Temporal.Duration(1), new Temporal.Duration(), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } });
+
+timeZone = "2021-08-19T17:30:45.123456789+23:59[+23:59:60]";
+assert.throws(RangeError, () => Temporal.Duration.compare(new Temporal.Duration(1), new Temporal.Duration(), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } }), "leap second in time zone name not valid");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-string-year-zero.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-string-year-zero.js
new file mode 100644
index 0000000000..57d48a7c3a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-string-year-zero.js
@@ -0,0 +1,23 @@
+// |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.duration.compare
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+invalidStrings.forEach((timeZone) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.Duration.compare(new Temporal.Duration(1), new Temporal.Duration(), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } }),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-string.js
new file mode 100644
index 0000000000..5da1a97928
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-string.js
@@ -0,0 +1,38 @@
+// |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.duration.compare
+description: Time zone IDs are valid input for a time zone
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getPossibleInstantsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getPossibleInstantsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be looked up");
+ },
+});
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+
+// The following are all valid strings so should not throw:
+
+["UTC", "+01:00"].forEach((timeZone) => {
+ Temporal.Duration.compare(new Temporal.Duration(1), new Temporal.Duration(), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } });
+});
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", getPossibleInstantsForOriginal);
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-wrong-type.js
new file mode 100644
index 0000000000..20db4448c4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-wrong-type.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.duration.compare
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or object for TimeZone
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [19761118, "number that would convert to a valid ISO string in other contexts"],
+ [1n, "bigint"],
+];
+
+for (const [timeZone, description] of primitiveTests) {
+ assert.throws(
+ typeof timeZone === 'string' ? RangeError : TypeError,
+ () => Temporal.Duration.compare(new Temporal.Duration(1), new Temporal.Duration(), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } }),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "object not implementing time zone protocol"],
+ [new Temporal.Calendar("iso8601"), "calendar instance"],
+];
+
+for (const [timeZone, description] of typeErrorTests) {
+ assert.throws(TypeError, () => Temporal.Duration.compare(new Temporal.Duration(1), new Temporal.Duration(), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } }), `${description} is not a valid object and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-string-invalid.js
new file mode 100644
index 0000000000..c6cd41b71e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-string-invalid.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.duration.compare
+description: RangeError thrown if relativeTo is a string with the wrong format
+features: [Temporal]
+---*/
+
+['bad string', '15:30:45.123456', 'iso8601', 'UTC', 'P1YT1H'].forEach((relativeTo) => {
+ const duration1 = new Temporal.Duration(0, 0, 0, 31);
+ const duration2 = new Temporal.Duration(0, 1);
+ assert.throws(RangeError, () => Temporal.Duration.compare(duration1, duration2, { relativeTo }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-string-plaindatetime.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-string-plaindatetime.js
new file mode 100644
index 0000000000..8ce80dffe1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-string-plaindatetime.js
@@ -0,0 +1,18 @@
+// |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.duration.compare
+description: The relativeTo option accepts a PlainDateTime-like ISO 8601 string
+features: [Temporal]
+---*/
+
+['2000-01-01', '2000-01-01T00:00', '2000-01-01T00:00[u-ca=iso8601]'].forEach((relativeTo) => {
+ const duration1 = new Temporal.Duration(0, 0, 0, 31);
+ const duration2 = new Temporal.Duration(0, 1);
+ const result = Temporal.Duration.compare(duration1, duration2, { relativeTo });
+ assert.sameValue(result, 0);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-string-zoneddatetime-wrong-offset.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-string-zoneddatetime-wrong-offset.js
new file mode 100644
index 0000000000..b31007bb5b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-string-zoneddatetime-wrong-offset.js
@@ -0,0 +1,16 @@
+// |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.duration.compare
+description: Throws if a ZonedDateTime-like relativeTo string has the wrong UTC offset
+features: [Temporal]
+---*/
+
+const duration1 = new Temporal.Duration(0, 0, 0, 31);
+const duration2 = new Temporal.Duration(0, 1);
+const relativeTo = "2000-01-01T00:00+05:30[UTC]";
+assert.throws(RangeError, () => Temporal.Duration.compare(duration1, duration2, { relativeTo }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-string-zoneddatetime.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-string-zoneddatetime.js
new file mode 100644
index 0000000000..f48784d3b5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-string-zoneddatetime.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.duration.compare
+description: The relativeTo option accepts a ZonedDateTime-like ISO 8601 string
+features: [Temporal]
+---*/
+
+[
+ '2000-01-01[UTC]',
+ '2000-01-01T00:00[UTC]',
+ '2000-01-01T00:00+00:00[UTC]',
+ '2000-01-01T00:00+00:00[UTC][u-ca=iso8601]',
+].forEach((relativeTo) => {
+ const duration1 = new Temporal.Duration(0, 0, 0, 31);
+ const duration2 = new Temporal.Duration(0, 1);
+ const result = Temporal.Duration.compare(duration1, duration2, { relativeTo });
+ assert.sameValue(result, 0);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-sub-minute-offset.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-sub-minute-offset.js
new file mode 100644
index 0000000000..a11edd6177
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-sub-minute-offset.js
@@ -0,0 +1,30 @@
+// |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.duration.compare
+description: relativeTo string accepts trailing zeroes in sub-minute UTC offset
+features: [Temporal]
+---*/
+
+const duration1 = new Temporal.Duration(0, 0, 0, 31);
+const duration2 = new Temporal.Duration(0, 1);
+
+let result;
+let relativeTo;
+
+const action = (relativeTo) => Temporal.Duration.compare(duration1, duration2, { relativeTo });
+
+relativeTo = "1970-01-01T00:00-00:45:00[-00:45]";
+result = action(relativeTo);
+assert.sameValue(result, 0, "ISO string offset accepted with zero seconds (string)");
+
+relativeTo = { year: 1970, month: 1, day: 1, offset: "+00:45:00.000000000", timeZone: "+00:45" };
+result = action(relativeTo);
+assert.sameValue(result, 0, "ISO string offset accepted with zero seconds (property bag)");
+
+relativeTo = "1970-01-01T00:00+00:44:30.123456789[+00:45]";
+assert.throws(RangeError, () => action(relativeTo), "rounding is not accepted between ISO offset and time zone");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-undefined-throw-on-calendar-units.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-undefined-throw-on-calendar-units.js
new file mode 100644
index 0000000000..6072bd795e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-undefined-throw-on-calendar-units.js
@@ -0,0 +1,30 @@
+// |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.duration.compare
+description: >
+ The relativeTo option is required when either Duration contains years,
+ months, or weeks
+features: [Temporal]
+---*/
+
+const oneYear = new Temporal.Duration(1);
+const oneMonth = new Temporal.Duration(0, 1);
+const oneWeek = new Temporal.Duration(0, 0, 1);
+const oneDay = new Temporal.Duration(0, 0, 0, 1);
+const twoDays = new Temporal.Duration(0, 0, 0, 2);
+
+assert.sameValue(Temporal.Duration.compare(oneDay, twoDays), -1, "days do not require relativeTo");
+
+assert.throws(RangeError, () => Temporal.Duration.compare(oneWeek, oneDay), "weeks in left operand require relativeTo");
+assert.throws(RangeError, () => Temporal.Duration.compare(oneDay, oneWeek), "weeks in right operand require relativeTo");
+
+assert.throws(RangeError, () => Temporal.Duration.compare(oneMonth, oneDay), "months in left operand require relativeTo");
+assert.throws(RangeError, () => Temporal.Duration.compare(oneDay, oneMonth), "months in right operand require relativeTo");
+
+assert.throws(RangeError, () => Temporal.Duration.compare(oneYear, oneDay), "years in left operand require relativeTo");
+assert.throws(RangeError, () => Temporal.Duration.compare(oneDay, oneYear), "years in right operand require relativeTo");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-year.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-year.js
new file mode 100644
index 0000000000..8bb23e2da6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-year.js
@@ -0,0 +1,18 @@
+// |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.duration.compare
+description: relativeTo with years.
+features: [Temporal]
+---*/
+
+const oneYear = new Temporal.Duration(1);
+const days365 = new Temporal.Duration(0, 0, 0, 365);
+assert.sameValue(
+ Temporal.Duration.compare(oneYear, days365, { relativeTo: Temporal.PlainDate.from("2017-01-01") }), 0);
+assert.sameValue(
+ Temporal.Duration.compare(oneYear, days365, { relativeTo: Temporal.PlainDate.from("2016-01-01") }), 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-zoneddatetime-negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-zoneddatetime-negative-epochnanoseconds.js
new file mode 100644
index 0000000000..7aa811c9eb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-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.duration.compare
+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]
+---*/
+
+const relativeTo = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC");
+const duration = new Temporal.Duration(0, 0, 0, 1);
+
+// This code path shows up anywhere we convert an exact time, before the Unix
+// epoch, with nonzero microseconds or nanoseconds, into a wall time; in this
+// case via relativeTo.
+
+const result = Temporal.Duration.compare(duration, duration, { relativeTo });
+assert.sameValue(result, 0);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..714d1aab7e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.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.duration.compare
+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].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const relativeTo = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const duration1 = new Temporal.Duration(1);
+ const duration2 = new Temporal.Duration(1, 1);
+ assert.throws(RangeError, () => Temporal.Duration.compare(duration1, duration2, { relativeTo }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..246fbe2b69
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-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.duration.compare
+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 relativeTo = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const duration1 = new Temporal.Duration(1);
+ const duration2 = new Temporal.Duration(1, 1);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => Temporal.Duration.compare(duration1, duration2, { relativeTo }),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..bbdb6604a4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.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.duration.compare
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const relativeTo = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const duration1 = new Temporal.Duration(1);
+ const duration2 = new Temporal.Duration(1, 1);
+ assert.throws(RangeError, () => Temporal.Duration.compare(duration1, duration2, { relativeTo }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..182f428a38
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,29 @@
+// |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.duration.compare
+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 relativeTo = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const duration1 = new Temporal.Duration(1);
+ const duration2 = new Temporal.Duration(1, 1);
+ assert.throws(TypeError, () => Temporal.Duration.compare(duration1, duration2, { relativeTo }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/shell.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/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/Duration/compare/timezone-getpossibleinstantsfor-iterable.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/timezone-getpossibleinstantsfor-iterable.js
new file mode 100644
index 0000000000..d5ab422897
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/timezone-getpossibleinstantsfor-iterable.js
@@ -0,0 +1,41 @@
+// |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.duration.compare
+description: An iterable returned from timeZone.getPossibleInstantsFor is consumed after each call
+info: |
+ sec-temporal.duration.compare steps 4–6:
+ 4. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_).
+ 5. Let _shift1_ be ! CalculateOffsetShift(_relativeTo_, _one_.[[Years]], [...], _one_.[[Nanoseconds]]).
+ 6. Let _shift2_ be ! CalculateOffsetShift(_relativeTo_, _two_.[[Years]], [...], _two_.[[Nanoseconds]]).
+ sec-temporal-torelativetemporalobject step 6.d:
+ d. Let _epochNanoseconds_ be ? InterpretISODateTimeOffset(_result_.[[Year]], [...], _result_.[[Nanosecond]], _offsetNs_, _timeZone_, *"compatible"*, *"reject"*).
+ sec-temporal-interpretisodatetimeoffset step 7:
+ 7. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_).
+ sec-temporal-calculateoffsetshift step 4:
+ 4. Let _after_ be ? AddZonedDateTime(_relativeTo_.[[Nanoseconds]], _relativeTo_.[[TimeZone]], _relativeTo_.[[Calendar]], _y_, [...], _ns_).
+ sec-temporal-addzoneddatetime step 8:
+ 8. Let _intermediateInstant_ be ? BuiltinTimeZoneGetInstantFor(_timeZone_, _intermediateDateTime_, *"compatible"*).
+ sec-temporal-builtintimezonegetinstantfor step 1:
+ 1. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_).
+ sec-temporal-getpossibleinstantsfor step 2:
+ 2. Let _list_ be ? IterableToList(_possibleInstants_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "2000-01-01T00:00:00", // called once on the input relativeTo object
+ "2001-01-01T00:00:00", // called once on relativeTo plus the first operand
+ "2001-02-01T00:00:00", // called once on relativeTo plus the second operand
+];
+
+TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => {
+ const duration1 = new Temporal.Duration(1);
+ const duration2 = new Temporal.Duration(0, 13);
+ Temporal.Duration.compare(duration1, duration2, { relativeTo: { year: 2000, month: 1, day: 1, timeZone } });
+}, expected);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/twenty-five-hour-day.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/twenty-five-hour-day.js
new file mode 100644
index 0000000000..1b846a9e60
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/twenty-five-hour-day.js
@@ -0,0 +1,36 @@
+// |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.duration.compare
+description: Unbalancing handles DST days with more than 24 hours
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tz = TemporalHelpers.springForwardFallBackTimeZone();
+
+// 2000-10-29 is a 25-hour day according to this time zone...
+
+const relativeTo = new Temporal.ZonedDateTime(941187600_000_000_000n, tz);
+
+// confirm that we have rewound one year and one day:
+assert.sameValue('1999-10-29T01:00:00-08:00[Custom/Spring_Fall]', relativeTo.toString());
+
+const d1 = new Temporal.Duration(1, 0, 0, 1);
+const d2 = new Temporal.Duration(1, 0, 0, 0, 25);
+
+// ...so the durations should be equal relative to relativeTo:
+
+assert.sameValue(0,
+ Temporal.Duration.compare(d1, d2, { relativeTo }),
+ "2000-10-29 is a 25-hour day"
+);
+
+assert.sameValue(1,
+ Temporal.Duration.compare(d1, { years: 1, hours: 24 }, { relativeTo }),
+ "2020-10-29 has more than 24 hours"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/year-zero.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/year-zero.js
new file mode 100644
index 0000000000..4adbc80ddb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/year-zero.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.duration.compare
+description: Negative zero, as an extended year, fails
+features: [Temporal]
+---*/
+
+const duration1 = new Temporal.Duration(1);
+const duration2 = new Temporal.Duration(2);
+const bad = "-000000-11-01";
+
+assert.throws(
+ RangeError,
+ () => Temporal.Duration.compare(duration1, duration2, { relativeTo: bad }),
+ "Cannot use negative zero as extended year"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/constructor.js b/js/src/tests/test262/built-ins/Temporal/Duration/constructor.js
new file mode 100644
index 0000000000..9d2e7e676c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/constructor.js
@@ -0,0 +1,15 @@
+// |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.duration
+description: Temporal.Duration constructor cannot be called as a function
+info: |
+ 1. If NewTarget is undefined, throw a TypeError exception.
+features: [Temporal]
+---*/
+
+assert.throws(TypeError, () => Temporal.Duration());
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/days-undefined.js b/js/src/tests/test262/built-ins/Temporal/Duration/days-undefined.js
new file mode 100644
index 0000000000..8e79041131
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/days-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.duration
+description: Undefined arguments should be treated as zero.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const args = [1, 1, 1];
+
+const explicit = new Temporal.Duration(...args, undefined);
+TemporalHelpers.assertDuration(explicit, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, "explicit");
+
+const implicit = new Temporal.Duration(...args);
+TemporalHelpers.assertDuration(implicit, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, "implicit");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/fractional-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/Duration/fractional-throws-rangeerror.js
new file mode 100644
index 0000000000..b13c7ead70
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/fractional-throws-rangeerror.js
@@ -0,0 +1,35 @@
+// |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.
+
+/*---
+description: Temporal.Duration throws a RangeError if any value is fractional
+esid: sec-temporal.duration
+features: [Temporal]
+---*/
+
+const descriptions = [
+ 'years',
+ 'months',
+ 'weeks',
+ 'days',
+ 'hours',
+ 'minutes',
+ 'seconds',
+ 'milliseconds',
+ 'microseconds',
+ 'nanoseconds'
+].map((time) => `Duration constructor throws RangeError with fractional value in the ${time} position`);
+
+assert.throws(RangeError, () => new Temporal.Duration(1.1), descriptions[0]);
+assert.throws(RangeError, () => new Temporal.Duration(0, 1.1), descriptions[1]);
+assert.throws(RangeError, () => new Temporal.Duration(0, 0, 1.1), descriptions[2]);
+assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 1.1), descriptions[3]);
+assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 1.1), descriptions[4]);
+assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 1.1), descriptions[5]);
+assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 0, 1.1), descriptions[6]);
+assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 1.1), descriptions[7]);
+assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 1.1), descriptions[8]);
+assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, 1.1), descriptions[9]);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-duration-max.js b/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-duration-max.js
new file mode 100644
index 0000000000..0a4043d391
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-duration-max.js
@@ -0,0 +1,55 @@
+// |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.duration.from
+description: Maximum allowed duration
+features: [Temporal]
+---*/
+
+const maxCases = [
+ ["P4294967295Y104249991374DT7H36M31.999999999S", "string with max years"],
+ [{ years: 4294967295, days: 104249991374, nanoseconds: 27391999999999 }, "property bag with max years"],
+ ["P4294967295M104249991374DT7H36M31.999999999S", "string with max weeks"],
+ [{ months: 4294967295, days: 104249991374, nanoseconds: 27391999999999 }, "property bag with max months"],
+ ["P4294967295W104249991374DT7H36M31.999999999S", "string with max weeks"],
+ [{ weeks: 4294967295, days: 104249991374, nanoseconds: 27391999999999 }, "property bag with max weeks"],
+ ["P104249991374DT7H36M31.999999999S", "string with max days"],
+ [{ days: 104249991374, nanoseconds: 27391999999999 }, "property bag with max days"],
+ ["PT2501999792983H36M31.999999999S", "string with max hours"],
+ [{ hours: 2501999792983, nanoseconds: 2191999999999 }, "property bag with max hours"],
+ ["PT150119987579016M31.999999999S", "string with max minutes"],
+ [{ minutes: 150119987579016, nanoseconds: 31999999999 }, "property bag with max minutes"],
+ ["PT9007199254740991.999999999S", "string with max seconds"],
+ [{ seconds: 9007199254740991, nanoseconds: 999999999 }, "property bag with max seconds"],
+];
+
+for (const [arg, descr] of maxCases) {
+ const result = Temporal.Duration.from(arg);
+ assert.sameValue(result.with({ years: 0, months: 0, weeks: 0 }).total("seconds"), 9007199254740991.999999999, `operation succeeds with ${descr}`);
+}
+
+const minCases = [
+ ["-P4294967295Y104249991374DT7H36M31.999999999S", "string with min years"],
+ [{ years: -4294967295, days: -104249991374, nanoseconds: -27391999999999 }, "property bag with min years"],
+ ["-P4294967295M104249991374DT7H36M31.999999999S", "string with min months"],
+ [{ months: -4294967295, days: -104249991374, nanoseconds: -27391999999999 }, "property bag with min months"],
+ ["-P4294967295W104249991374DT7H36M31.999999999S", "string with min weeks"],
+ [{ weeks: -4294967295, days: -104249991374, nanoseconds: -27391999999999 }, "property bag with min weeks"],
+ ["-P104249991374DT7H36M31.999999999S", "string with min days"],
+ [{ days: -104249991374, nanoseconds: -27391999999999 }, "property bag with min days"],
+ ["-PT2501999792983H36M31.999999999S", "string with min hours"],
+ [{ hours: -2501999792983, nanoseconds: -2191999999999 }, "property bag with min hours"],
+ ["-PT150119987579016M31.999999999S", "string with min minutes"],
+ [{ minutes: -150119987579016, nanoseconds: -31999999999 }, "property bag with min minutes"],
+ ["-PT9007199254740991.999999999S", "string with min seconds"],
+ [{ seconds: -9007199254740991, nanoseconds: -999999999 }, "property bag with min seconds"],
+];
+
+for (const [arg, descr] of minCases) {
+ const result = Temporal.Duration.from(arg);
+ assert.sameValue(result.with({ years: 0, months: 0, weeks: 0 }).total("seconds"), -9007199254740991.999999999, `operation succeeds with ${descr}`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-duration-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-duration-out-of-range.js
new file mode 100644
index 0000000000..77fbdc9f7a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-duration-out-of-range.js
@@ -0,0 +1,59 @@
+// |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.duration.from
+description: Duration-like argument that is out of range
+features: [Temporal]
+---*/
+
+const cases = [
+ // ceil(max safe integer / 86400) = 104249991375
+ ["P104249991375D", "string with days > max"],
+ [{ days: 104249991375 }, "property bag with days > max"],
+ ["P104249991374DT24H", "string where hours balance into days > max"],
+ [{ days: 104249991374, hours: 24 }, "property bag where hours balance into days > max"],
+ ["-P104249991375D", "string with days < min"],
+ [{ days: -104249991375 }, "property bag with days < min"],
+ ["-P104249991374DT24H", "string where hours balance into days < min"],
+ [{ days: -104249991374, hours: -24 }, "property bag where hours balance into days < min"],
+
+ // ceil(max safe integer / 3600) = 2501999792984
+ ["PT2501999792984H", "string with hours > max"],
+ [{ hours: 2501999792984 }, "property bag with hours > max"],
+ ["PT2501999792983H60M", "string where minutes balance into hours > max"],
+ [{ hours: 2501999792983, minutes: 60 }, "property bag where minutes balance into hours > max"],
+ ["-PT2501999792984H", "string with hours < min"],
+ [{ hours: -2501999792984 }, "property bag with hours < min"],
+ ["-PT2501999792983H60M", "string where minutes balance into hours < min"],
+ [{ hours: -2501999792983, minutes: -60 }, "property bag where minutes balance into hours < min"],
+
+ // ceil(max safe integer / 60) = 150119987579017
+ ["PT150119987579017M", "string with minutes > max"],
+ [{ minutes: 150119987579017 }, "property bag with minutes > max"],
+ ["PT150119987579016M60S", "string where seconds balance into minutes > max"],
+ [{ minutes: 150119987579016, seconds: 60 }, "property bag where seconds balance into minutes > max"],
+ ["-PT150119987579017M", "string with minutes < min"],
+ [{ minutes: -150119987579017 }, "property bag with minutes < min"],
+ ["-PT150119987579016M60S", "string where seconds balance into minutes < min"],
+ [{ minutes: -150119987579016, seconds: -60 }, "property bag where seconds balance into minutes < min"],
+
+ // 2^53 = 9007199254740992
+ ["PT9007199254740992S", "string with seconds > max"],
+ [{ seconds: 9007199254740992 }, "property bag with seconds > max"],
+ [{ seconds: 9007199254740991, milliseconds: 1000 }, "property bag where milliseconds balance into seconds > max"],
+ [{ seconds: 9007199254740991, microseconds: 1000000 }, "property bag where microseconds balance into seconds > max"],
+ [{ seconds: 9007199254740991, nanoseconds: 1000000000 }, "property bag where nanoseconds balance into seconds > max"],
+ ["-PT9007199254740992S", "string with seconds < min"],
+ [{ seconds: -9007199254740992 }, "property bag with seconds < min"],
+ [{ seconds: -9007199254740991, milliseconds: -1000 }, "property bag where milliseconds balance into seconds < min"],
+ [{ seconds: -9007199254740991, microseconds: -1000000 }, "property bag where microseconds balance into seconds < min"],
+ [{ seconds: -9007199254740991, nanoseconds: -1000000000 }, "property bag where nanoseconds balance into seconds < min"],
+];
+
+for (const [arg, descr] of cases) {
+ assert.throws(RangeError, () => Temporal.Duration.from(arg), `${descr} is out of range`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-duration-precision-exact-numerical-values.js b/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-duration-precision-exact-numerical-values.js
new file mode 100644
index 0000000000..2bece633b8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-duration-precision-exact-numerical-values.js
@@ -0,0 +1,41 @@
+// |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.duration.from
+description: >
+ Duration-like argument performs the range check with minimal floating point
+ precision loss
+features: [Temporal]
+---*/
+
+// Based on a test case by André Bargull
+
+const cases = [
+ [
+ {
+ milliseconds: 4503599627370497_000, // ℝ(𝔽(4503599627370497000)) = 4503599627370497024
+ microseconds: 4503599627370495_000000, // ℝ(𝔽(4503599627370495000000)) = 4503599627370494951424
+ },
+ // 4503599627370497024 / 1000 + 4503599627370494951424 / 1000000 is
+ // 9007199254740991.975424, which is below the limit of 2**53
+ "PT9007199254740991.975424S",
+ "case where floating point inaccuracy brings total below limit, positive"
+ ],
+ [
+ {
+ milliseconds: -4503599627370497_000,
+ microseconds: -4503599627370495_000000,
+ },
+ "-PT9007199254740991.975424S",
+ "case where floating point inaccuracy brings total below limit, negative"
+ ],
+];
+
+for (const [arg, string, descr] of cases) {
+ const instance = Temporal.Duration.from(arg); // should not throw
+ assert.sameValue(instance.toString(), string, descr);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-duration.js b/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-duration.js
new file mode 100644
index 0000000000..59eaffb223
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-duration.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.duration.from
+description: A Duration object is copied, not returned directly
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const orig = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+const result = Temporal.Duration.from(orig);
+
+TemporalHelpers.assertDuration(
+ result,
+ 1, 2, 3, 4, 5, 6, 7, 987, 654, 321,
+ "Duration is copied"
+);
+
+assert.notSameValue(
+ result,
+ orig,
+ "When a Duration is given, the returned value is not the original Duration"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-existing-object.js b/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-existing-object.js
new file mode 100644
index 0000000000..6f44cd0c03
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-existing-object.js
@@ -0,0 +1,20 @@
+// |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.duration.from
+description: Property bag is converted to Duration; Duration is copied
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const d1 = Temporal.Duration.from({ milliseconds: 1000, month: 1 });
+TemporalHelpers.assertDuration(d1, 0, 0, 0, 0, 0, 0, 0, 1000, 0, 0);
+
+const d2 = Temporal.Duration.from(d1);
+assert.notSameValue(d1, d2);
+TemporalHelpers.assertDuration(d1, 0, 0, 0, 0, 0, 0, 0, 1000, 0, 0);
+TemporalHelpers.assertDuration(d2, 0, 0, 0, 0, 0, 0, 0, 1000, 0, 0);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-non-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-non-string.js
new file mode 100644
index 0000000000..3d53993e4e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-non-string.js
@@ -0,0 +1,18 @@
+// |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.duration.from
+description: Appropriate error thrown if primitive input cannot convert to a valid string
+features: [Temporal]
+---*/
+
+assert.throws(TypeError, () => Temporal.Duration.from(undefined), "undefined");
+assert.throws(TypeError, () => Temporal.Duration.from(null), "null");
+assert.throws(TypeError, () => Temporal.Duration.from(true), "boolean");
+assert.throws(TypeError, () => Temporal.Duration.from(Symbol()), "Symbol");
+assert.throws(TypeError, () => Temporal.Duration.from(5), "number");
+assert.throws(TypeError, () => Temporal.Duration.from(5n), "bigint");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-object-invalid.js b/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-object-invalid.js
new file mode 100644
index 0000000000..bc378f0368
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-object-invalid.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.duration.from
+description: Invalid object arguments.
+features: [Temporal]
+---*/
+
+const tests = [
+ { years: 0.5 },
+ { months: 0.5 },
+ { weeks: 0.5 },
+ { days: 0.5 },
+ { hours: 0.5, minutes: 20 },
+ { hours: 0.5, seconds: 15 },
+ { minutes: 10.7, nanoseconds: 400 },
+ { hours: 1, minutes: -30 },
+];
+
+for (const input of tests) {
+ assert.throws(RangeError, () => Temporal.Duration.from(input));
+}
+
+assert.throws(TypeError, () => Temporal.Duration.from({}));
+assert.throws(TypeError, () => Temporal.Duration.from({ month: 12 }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-string-fractional-precision.js b/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-string-fractional-precision.js
new file mode 100644
index 0000000000..459fd26c37
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-string-fractional-precision.js
@@ -0,0 +1,59 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.from
+description: >
+ Fractional parts are computed using exact mathematical values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const tests = {
+ "PT0.999999999H": Temporal.Duration.from({
+ minutes: 59,
+ seconds: 59,
+ milliseconds: 999,
+ microseconds: 996,
+ nanoseconds: 400,
+ }),
+ "PT0.000000011H": Temporal.Duration.from({
+ minutes: 0,
+ seconds: 0,
+ milliseconds: 0,
+ microseconds: 39,
+ nanoseconds: 600,
+ }),
+
+ "PT0.999999999M": Temporal.Duration.from({
+ seconds: 59,
+ milliseconds: 999,
+ microseconds: 999,
+ nanoseconds: 940,
+ }),
+ "PT0.000000011M": Temporal.Duration.from({
+ seconds: 0,
+ milliseconds: 0,
+ microseconds: 0,
+ nanoseconds: 660,
+ }),
+
+ "PT0.999999999S": Temporal.Duration.from({
+ milliseconds: 999,
+ microseconds: 999,
+ nanoseconds: 999,
+ }),
+ "PT0.000000011S": Temporal.Duration.from({
+ milliseconds: 0,
+ microseconds: 0,
+ nanoseconds: 11,
+ }),
+};
+
+for (let [str, expected] of Object.entries(tests)) {
+ let actual = Temporal.Duration.from(str);
+ TemporalHelpers.assertDurationsEqual(actual, expected, str);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-string-fractional-units-rounding-mode.js b/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-string-fractional-units-rounding-mode.js
new file mode 100644
index 0000000000..65b257b355
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-string-fractional-units-rounding-mode.js
@@ -0,0 +1,31 @@
+// |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.duration.from
+description: Strings with fractional duration units are rounded with the correct rounding mode
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const resultPosHours = Temporal.Duration.from("PT1.03125H");
+TemporalHelpers.assertDuration(resultPosHours, 0, 0, 0, 0, 1, 1, 52, 500, 0, 0,
+ "positive fractional hours rounded with correct rounding mode");
+
+const resultNegHours = Temporal.Duration.from("-PT1.03125H");
+TemporalHelpers.assertDuration(resultNegHours, 0, 0, 0, 0, -1, -1, -52, -500, 0, 0,
+ "negative fractional hours rounded with correct rounding mode");
+
+// The following input should not round, but may fail if an implementation does
+// floating point arithmetic too early:
+
+const resultPosSeconds = Temporal.Duration.from("PT46H66M71.50040904S");
+TemporalHelpers.assertDuration(resultPosSeconds, 0, 0, 0, 0, 46, 66, 71, 500, 409, 40,
+ "positive fractional seconds not rounded");
+
+const resultNegSeconds = Temporal.Duration.from("-PT46H66M71.50040904S");
+TemporalHelpers.assertDuration(resultNegSeconds, 0, 0, 0, 0, -46, -66, -71, -500, -409, -40,
+ "negative fractional seconds not rounded");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-string-fractional-with-zero-subparts.js b/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-string-fractional-with-zero-subparts.js
new file mode 100644
index 0000000000..292dc1b712
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-string-fractional-with-zero-subparts.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.from
+description: >
+ Throws when a fractional unit is present and a sub-part is zero.
+features: [Temporal]
+---*/
+
+const invalid = [
+ // Hours fraction with whole minutes.
+ "PT0.1H0M",
+
+ // Hours fraction with fractional minutes.
+ "PT0.1H0.0M",
+
+ // Hours fraction with whole seconds.
+ "PT0.1H0S",
+
+ // Hours fraction with fractional seconds.
+ "PT0.1H0.0S",
+
+ // Minutes fraction with whole seconds.
+ "PT0.1M0S",
+
+ // Minutes fraction with fractional seconds.
+ "PT0.1M0.0S",
+];
+
+for (let string of invalid) {
+ assert.throws(RangeError, () => Temporal.Duration.from(string));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-string-invalid.js
new file mode 100644
index 0000000000..963c72f4af
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-string-invalid.js
@@ -0,0 +1,35 @@
+// |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.duration.from
+description: Invalid string arguments.
+features: [Temporal]
+---*/
+
+const tests = [
+ "P1Y1M1W1DT1H1M1.123456789123S",
+ "P0.5Y",
+ "P1Y0,5M",
+ "P1Y1M0.5W",
+ "P1Y1M1W0,5D",
+ "P1Y1M1W1DT0.5H5S",
+ "P1Y1M1W1DT1.5H0,5M",
+ "P1Y1M1W1DT1H0.5M0.5S",
+ "P",
+ "PT",
+ "-P",
+ "-PT",
+ "+P",
+ "+PT",
+ "P1Y1M1W1DT1H1M1.01Sjunk",
+ "P-1Y1M",
+ "P1Y-1M"
+];
+
+for (const input of tests) {
+ assert.throws(RangeError, () => Temporal.Duration.from(input));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-string-negative-fractional-units.js b/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-string-negative-fractional-units.js
new file mode 100644
index 0000000000..3caf5fc2eb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-string-negative-fractional-units.js
@@ -0,0 +1,18 @@
+// |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.duration.prototype.add
+description: Strings with fractional duration units are treated with the correct sign
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const resultHours = Temporal.Duration.from("-PT24.567890123H");
+TemporalHelpers.assertDuration(resultHours, 0, 0, 0, 0, -24, -34, -4, -404, -442, -800, "negative fractional hours");
+
+const resultMinutes = Temporal.Duration.from("-PT1440.567890123M");
+TemporalHelpers.assertDuration(resultMinutes, 0, 0, 0, 0, 0, -1440, -34, -73, -407, -380, "negative fractional minutes");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-string.js
new file mode 100644
index 0000000000..7382b24e08
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-string.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.duration.from
+description: Basic string arguments.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.assertDuration(Temporal.Duration.from("P1D"),
+ 0, 0, 0, 1, 0, 0, 0, 0, 0, 0);
+TemporalHelpers.assertDuration(Temporal.Duration.from("p1y1m1dt1h1m1s"),
+ 1, 1, 0, 1, 1, 1, 1, 0, 0, 0);
+TemporalHelpers.assertDuration(Temporal.Duration.from("P1Y1M1W1DT1H1M1.1S"),
+ 1, 1, 1, 1, 1, 1, 1, 100, 0, 0);
+TemporalHelpers.assertDuration(Temporal.Duration.from("P1Y1M1W1DT1H1M1.12S"),
+ 1, 1, 1, 1, 1, 1, 1, 120, 0, 0);
+TemporalHelpers.assertDuration(Temporal.Duration.from("P1Y1M1W1DT1H1M1.123S"),
+ 1, 1, 1, 1, 1, 1, 1, 123, 0, 0);
+TemporalHelpers.assertDuration(Temporal.Duration.from("P1Y1M1W1DT1H1M1.1234S"),
+ 1, 1, 1, 1, 1, 1, 1, 123, 400, 0);
+TemporalHelpers.assertDuration(Temporal.Duration.from("P1Y1M1W1DT1H1M1.12345S"),
+ 1, 1, 1, 1, 1, 1, 1, 123, 450, 0);
+TemporalHelpers.assertDuration(Temporal.Duration.from("P1Y1M1W1DT1H1M1.123456S"),
+ 1, 1, 1, 1, 1, 1, 1, 123, 456, 0);
+TemporalHelpers.assertDuration(Temporal.Duration.from("P1Y1M1W1DT1H1M1.1234567S"),
+ 1, 1, 1, 1, 1, 1, 1, 123, 456, 700);
+TemporalHelpers.assertDuration(Temporal.Duration.from("P1Y1M1W1DT1H1M1.12345678S"),
+ 1, 1, 1, 1, 1, 1, 1, 123, 456, 780);
+TemporalHelpers.assertDuration(Temporal.Duration.from("P1Y1M1W1DT1H1M1.123456789S"),
+ 1, 1, 1, 1, 1, 1, 1, 123, 456, 789);
+TemporalHelpers.assertDuration(Temporal.Duration.from("P1Y1M1W1DT1H1M1,12S"),
+ 1, 1, 1, 1, 1, 1, 1, 120, 0, 0);
+TemporalHelpers.assertDuration(Temporal.Duration.from("P1DT0.5M"),
+ 0, 0, 0, 1, 0, 0, 30, 0, 0, 0);
+TemporalHelpers.assertDuration(Temporal.Duration.from("P1DT0,5H"),
+ 0, 0, 0, 1, 0, 30, 0, 0, 0, 0);
+TemporalHelpers.assertDuration(Temporal.Duration.from("+P1D"),
+ 0, 0, 0, 1, 0, 0, 0, 0, 0, 0);
+TemporalHelpers.assertDuration(Temporal.Duration.from("-P1D"),
+ 0, 0, 0, -1, 0, 0, 0, 0, 0, 0);
+TemporalHelpers.assertDuration(Temporal.Duration.from("\u2212P1D"),
+ 0, 0, 0, -1, 0, 0, 0, 0, 0, 0);
+TemporalHelpers.assertDuration(Temporal.Duration.from("-P1Y1M1W1DT1H1M1.123456789S"),
+ -1, -1, -1, -1, -1, -1, -1, -123, -456, -789);
+TemporalHelpers.assertDuration(Temporal.Duration.from("PT100M"),
+ 0, 0, 0, 0, 0, 100, 0, 0, 0, 0);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/from/browser.js b/js/src/tests/test262/built-ins/Temporal/Duration/from/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/from/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/from/builtin.js b/js/src/tests/test262/built-ins/Temporal/Duration/from/builtin.js
new file mode 100644
index 0000000000..5a67e03f52
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/from/builtin.js
@@ -0,0 +1,33 @@
+// |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.duration.from
+description: Tests that Temporal.Duration.from meets the requirements for built-in objects
+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.Duration.from),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Duration.from),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Duration.from),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Duration.from.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/from/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/Duration/from/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..465f40b9c7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/from/infinity-throws-rangeerror.js
@@ -0,0 +1,31 @@
+// |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.
+
+/*---
+description: Temporal.Duration.from handles a property bag if any value is Infinity
+esid: sec-temporal.duration.from
+features: [Temporal]
+---*/
+
+const fields = ['years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds', 'milliseconds', 'microseconds', 'nanoseconds'];
+
+fields.forEach((field) => {
+ assert.throws(RangeError, () => Temporal.Duration.from({ [field]: Infinity }));
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return Infinity;
+ }
+};
+
+fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => Temporal.Duration.from({ [field]: obj }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/from/length.js b/js/src/tests/test262/built-ins/Temporal/Duration/from/length.js
new file mode 100644
index 0000000000..c01e3aa8a5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/from/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.duration.from
+description: Temporal.Duration.from.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.Duration.from, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/from/name.js b/js/src/tests/test262/built-ins/Temporal/Duration/from/name.js
new file mode 100644
index 0000000000..eb84fdc568
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/from/name.js
@@ -0,0 +1,26 @@
+// |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.duration.from
+description: Temporal.Duration.from.name is "from"
+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.Duration.from, "name", {
+ value: "from",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/from/negative-inifinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/Duration/from/negative-inifinity-throws-rangeerror.js
new file mode 100644
index 0000000000..dec4380531
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/from/negative-inifinity-throws-rangeerror.js
@@ -0,0 +1,31 @@
+// |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.
+
+/*---
+description: Temporal.Duration.from handles a property bag if any value is -Infinity
+esid: sec-temporal.duration.from
+features: [Temporal]
+---*/
+
+const fields = ['years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds', 'milliseconds', 'microseconds', 'nanoseconds'];
+
+fields.forEach((field) => {
+ assert.throws(RangeError, () => Temporal.Duration.from({ [field]: -Infinity }));
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return -Infinity;
+ }
+};
+
+fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => Temporal.Duration.from({ [field]: obj }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/from/non-integer-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/Duration/from/non-integer-throws-rangeerror.js
new file mode 100644
index 0000000000..1cf31580ff
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/from/non-integer-throws-rangeerror.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.duration.prototype.with
+description: A non-integer value for any recognized property in the property bag, throws a RangeError
+features: [Temporal]
+---*/
+
+const fields = [
+ "years",
+ "months",
+ "weeks",
+ "days",
+ "hours",
+ "minutes",
+ "seconds",
+ "milliseconds",
+ "microseconds",
+ "nanoseconds",
+];
+fields.forEach((field) => {
+ assert.throws(RangeError, () => Temporal.Duration.from({ [field]: 1.5 }));
+ assert.throws(RangeError, () => Temporal.Duration.from({ [field]: -1.5 }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/from/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Duration/from/not-a-constructor.js
new file mode 100644
index 0000000000..e9c2a5d655
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/from/not-a-constructor.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.duration.from
+description: Temporal.Duration.from 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.Duration.from();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Duration.from), false,
+ "isConstructor(Temporal.Duration.from)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/from/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/Duration/from/order-of-operations.js
new file mode 100644
index 0000000000..a17d63c60f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/from/order-of-operations.js
@@ -0,0 +1,61 @@
+// |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.duration.from
+description: Properties on an object passed to from() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "get fields.days",
+ "get fields.days.valueOf",
+ "call fields.days.valueOf",
+ "get fields.hours",
+ "get fields.hours.valueOf",
+ "call fields.hours.valueOf",
+ "get fields.microseconds",
+ "get fields.microseconds.valueOf",
+ "call fields.microseconds.valueOf",
+ "get fields.milliseconds",
+ "get fields.milliseconds.valueOf",
+ "call fields.milliseconds.valueOf",
+ "get fields.minutes",
+ "get fields.minutes.valueOf",
+ "call fields.minutes.valueOf",
+ "get fields.months",
+ "get fields.months.valueOf",
+ "call fields.months.valueOf",
+ "get fields.nanoseconds",
+ "get fields.nanoseconds.valueOf",
+ "call fields.nanoseconds.valueOf",
+ "get fields.seconds",
+ "get fields.seconds.valueOf",
+ "call fields.seconds.valueOf",
+ "get fields.weeks",
+ "get fields.weeks.valueOf",
+ "call fields.weeks.valueOf",
+ "get fields.years",
+ "get fields.years.valueOf",
+ "call fields.years.valueOf",
+];
+const actual = [];
+const fields = TemporalHelpers.propertyBagObserver(actual, {
+ years: 1,
+ months: 1,
+ weeks: 1,
+ days: 1,
+ hours: 1,
+ minutes: 1,
+ seconds: 1,
+ milliseconds: 1,
+ microseconds: 1,
+ nanoseconds: 1,
+}, "fields");
+const result = Temporal.Duration.from(fields);
+TemporalHelpers.assertDuration(result, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1);
+assert.compareArray(actual, expected, "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/from/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Duration/from/prop-desc.js
new file mode 100644
index 0000000000..f3e34fb557
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/from/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.duration.from
+description: The "from" property of Temporal.Duration
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Duration.from,
+ "function",
+ "`typeof Duration.from` is `function`"
+);
+
+verifyProperty(Temporal.Duration, "from", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/from/shell.js b/js/src/tests/test262/built-ins/Temporal/Duration/from/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/from/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/Duration/from/string-with-skipped-units.js b/js/src/tests/test262/built-ins/Temporal/Duration/from/string-with-skipped-units.js
new file mode 100644
index 0000000000..b6e79b60eb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/from/string-with-skipped-units.js
@@ -0,0 +1,34 @@
+// |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.duration.from
+description: |
+ Creating a Duration from an ISO 8601 string with an absent designator between
+ two other designators
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+// Date designators: years-weeks (months missing)
+
+const d1 = Temporal.Duration.from("P3Y4W");
+TemporalHelpers.assertDuration(d1, 3, 0, 4, 0, 0, 0, 0, 0, 0, 0, "years-weeks string");
+
+// Date designators: years-days (months and weeks missing)
+
+const d2 = Temporal.Duration.from("P3Y4D");
+TemporalHelpers.assertDuration(d2, 3, 0, 0, 4, 0, 0, 0, 0, 0, 0, "years-days string");
+
+// Date designators: months-days (weeks missing)
+
+const d3 = Temporal.Duration.from("P3M4D");
+TemporalHelpers.assertDuration(d3, 0, 3, 0, 4, 0, 0, 0, 0, 0, 0, "months-days string");
+
+// Time designators: hours-seconds (minutes missing)
+
+const d4 = Temporal.Duration.from("PT3H4.123456789S");
+TemporalHelpers.assertDuration(d4, 0, 0, 0, 0, 3, 0, 4, 123, 456, 789, "hours-seconds string");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/from/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/Duration/from/subclassing-ignored.js
new file mode 100644
index 0000000000..a6802d3938
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/from/subclassing-ignored.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.duration.from
+description: The receiver is never called when calling from()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnoredStatic(
+ Temporal.Duration,
+ "from",
+ ["P1Y2M3W4DT5H6M7.987654321S"],
+ (result) => TemporalHelpers.assertDuration(result, 1, 2, 3, 4, 5, 6, 7, 987, 654, 321),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/hours-undefined.js b/js/src/tests/test262/built-ins/Temporal/Duration/hours-undefined.js
new file mode 100644
index 0000000000..820c75d622
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/hours-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.duration
+description: Undefined arguments should be treated as zero.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const args = [1, 1, 1, 1];
+
+const explicit = new Temporal.Duration(...args, undefined);
+TemporalHelpers.assertDuration(explicit, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, "explicit");
+
+const implicit = new Temporal.Duration(...args);
+TemporalHelpers.assertDuration(implicit, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, "implicit");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/Duration/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..0df7594441
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/infinity-throws-rangeerror.js
@@ -0,0 +1,84 @@
+// |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.
+
+/*---
+description: Temporal.Duration throws a RangeError if any value is Infinity
+esid: sec-temporal.duration
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+assert.throws(RangeError, () => new Temporal.Duration(Infinity));
+assert.throws(RangeError, () => new Temporal.Duration(0, Infinity));
+assert.throws(RangeError, () => new Temporal.Duration(0, 0, Infinity));
+assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, Infinity));
+assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, Infinity));
+assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, Infinity));
+assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 0, Infinity));
+assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, Infinity));
+assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, Infinity));
+assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, Infinity));
+
+const O = (primitiveValue, propertyName) => (calls) => TemporalHelpers.toPrimitiveObserver(calls, primitiveValue, propertyName);
+const tests = [
+ [
+ "infinite years",
+ [O(Infinity, "years"), O(0, "months"), O(0, "weeks"), O(0, "days"), O(0, "hours"), O(0, "minutes"), O(0, "seconds"), O(0, "milliseconds"), O(0, "microseconds"), O(0, "nanoseconds")],
+ ["get years.valueOf", "call years.valueOf"]
+ ],
+ [
+ "infinite months",
+ [O(0, "years"), O(Infinity, "months"), O(0, "weeks"), O(0, "days"), O(0, "hours"), O(0, "minutes"), O(0, "seconds"), O(0, "milliseconds"), O(0, "microseconds"), O(0, "nanoseconds")],
+ ["get years.valueOf", "call years.valueOf", "get months.valueOf", "call months.valueOf"]
+ ],
+ [
+ "infinite weeks",
+ [O(0, "years"), O(0, "months"), O(Infinity, "weeks"), O(0, "days"), O(0, "hours"), O(0, "minutes"), O(0, "seconds"), O(0, "milliseconds"), O(0, "microseconds"), O(0, "nanoseconds")],
+ ["get years.valueOf", "call years.valueOf", "get months.valueOf", "call months.valueOf", "get weeks.valueOf", "call weeks.valueOf"]
+ ],
+ [
+ "infinite days",
+ [O(0, "years"), O(0, "months"), O(0, "weeks"), O(Infinity, "days"), O(0, "hours"), O(0, "minutes"), O(0, "seconds"), O(0, "milliseconds"), O(0, "microseconds"), O(0, "nanoseconds")],
+ ["get years.valueOf", "call years.valueOf", "get months.valueOf", "call months.valueOf", "get weeks.valueOf", "call weeks.valueOf", "get days.valueOf", "call days.valueOf"]
+ ],
+ [
+ "infinite hours",
+ [O(0, "years"), O(0, "months"), O(0, "weeks"), O(0, "days"), O(Infinity, "hours"), O(0, "minutes"), O(0, "seconds"), O(0, "milliseconds"), O(0, "microseconds"), O(0, "nanoseconds")],
+ ["get years.valueOf", "call years.valueOf", "get months.valueOf", "call months.valueOf", "get weeks.valueOf", "call weeks.valueOf", "get days.valueOf", "call days.valueOf", "get hours.valueOf", "call hours.valueOf"]
+ ],
+ [
+ "infinite minutes",
+ [O(0, "years"), O(0, "months"), O(0, "weeks"), O(0, "days"), O(0, "hours"), O(Infinity, "minutes"), O(0, "seconds"), O(0, "milliseconds"), O(0, "microseconds"), O(0, "nanoseconds")],
+ ["get years.valueOf", "call years.valueOf", "get months.valueOf", "call months.valueOf", "get weeks.valueOf", "call weeks.valueOf", "get days.valueOf", "call days.valueOf", "get hours.valueOf", "call hours.valueOf", "get minutes.valueOf", "call minutes.valueOf"]
+ ],
+ [
+ "infinite seconds",
+ [O(0, "years"), O(0, "months"), O(0, "weeks"), O(0, "days"), O(0, "hours"), O(0, "minutes"), O(Infinity, "seconds"), O(0, "milliseconds"), O(0, "microseconds"), O(0, "nanoseconds")],
+ ["get years.valueOf", "call years.valueOf", "get months.valueOf", "call months.valueOf", "get weeks.valueOf", "call weeks.valueOf", "get days.valueOf", "call days.valueOf", "get hours.valueOf", "call hours.valueOf", "get minutes.valueOf", "call minutes.valueOf", "get seconds.valueOf", "call seconds.valueOf"]
+ ],
+ [
+ "infinite milliseconds",
+ [O(0, "years"), O(0, "months"), O(0, "weeks"), O(0, "days"), O(0, "hours"), O(0, "minutes"), O(0, "seconds"), O(Infinity, "milliseconds"), O(0, "microseconds"), O(0, "nanoseconds")],
+ ["get years.valueOf", "call years.valueOf", "get months.valueOf", "call months.valueOf", "get weeks.valueOf", "call weeks.valueOf", "get days.valueOf", "call days.valueOf", "get hours.valueOf", "call hours.valueOf", "get minutes.valueOf", "call minutes.valueOf", "get seconds.valueOf", "call seconds.valueOf", "get milliseconds.valueOf", "call milliseconds.valueOf"]
+ ],
+ [
+ "infinite microseconds",
+ [O(0, "years"), O(0, "months"), O(0, "weeks"), O(0, "days"), O(0, "hours"), O(0, "minutes"), O(0, "seconds"), O(0, "milliseconds"), O(Infinity, "microseconds"), O(0, "nanoseconds")],
+ ["get years.valueOf", "call years.valueOf", "get months.valueOf", "call months.valueOf", "get weeks.valueOf", "call weeks.valueOf", "get days.valueOf", "call days.valueOf", "get hours.valueOf", "call hours.valueOf", "get minutes.valueOf", "call minutes.valueOf", "get seconds.valueOf", "call seconds.valueOf", "get milliseconds.valueOf", "call milliseconds.valueOf", "get microseconds.valueOf", "call microseconds.valueOf"]
+ ],
+ [
+ "infinite nanoseconds",
+ [O(0, "years"), O(0, "months"), O(0, "weeks"), O(0, "days"), O(0, "hours"), O(0, "minutes"), O(0, "seconds"), O(0, "milliseconds"), O(0, "microseconds"), O(Infinity, "nanoseconds")],
+ ["get years.valueOf", "call years.valueOf", "get months.valueOf", "call months.valueOf", "get weeks.valueOf", "call weeks.valueOf", "get days.valueOf", "call days.valueOf", "get hours.valueOf", "call hours.valueOf", "get minutes.valueOf", "call minutes.valueOf", "get seconds.valueOf", "call seconds.valueOf", "get milliseconds.valueOf", "call milliseconds.valueOf", "get microseconds.valueOf", "call microseconds.valueOf", "get nanoseconds.valueOf", "call nanoseconds.valueOf"]
+ ],
+];
+
+for (const [description, args, expected] of tests) {
+ const actual = [];
+ const args_ = args.map((o) => o(actual));
+ assert.throws(RangeError, () => new Temporal.Duration(...args_), description);
+ assert.compareArray(actual, expected, `${description} order of operations`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/length.js b/js/src/tests/test262/built-ins/Temporal/Duration/length.js
new file mode 100644
index 0000000000..0e0748acd0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/length.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.duration
+description: Temporal.Duration.length is 0
+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.Duration, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/max.js b/js/src/tests/test262/built-ins/Temporal/Duration/max.js
new file mode 100644
index 0000000000..8cc866fa69
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/max.js
@@ -0,0 +1,33 @@
+// |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.duration
+description: Maximum values of arguments to the Duration constructor
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const cases = [
+ [new Temporal.Duration(0, 0, 0, 104249991374, 7, 36, 31, 999, 999, 999), "max days", 9007199254740991.999999999],
+ [new Temporal.Duration(0, 0, 0, 0, 2501999792983, 36, 31, 999, 999, 999), "max hours", 9007199254740991.999999999],
+ [new Temporal.Duration(0, 0, 0, 0, 0, 150119987579016, 31, 999, 999, 999), "max minutes", 9007199254740991.999999999],
+ [new Temporal.Duration(0, 0, 0, 0, 0, 0, 9007199254740991, 999, 999, 999), "max seconds", 9007199254740991.999999999],
+ [new Temporal.Duration(0, 0, 0, -104249991374, -7, -36, -31, -999, -999, -999), "min days", -9007199254740991.999999999],
+ [new Temporal.Duration(0, 0, 0, 0, -2501999792983, -36, -31, -999, -999, -999), "min hours", -9007199254740991.999999999],
+ [new Temporal.Duration(0, 0, 0, 0, 0, -150119987579016, -31, -999, -999, -999), "min minutes", -9007199254740991.999999999],
+ [new Temporal.Duration(0, 0, 0, 0, 0, 0, -9007199254740991, -999, -999, -999), "min seconds", -9007199254740991.999999999],
+];
+
+for (const [d, descr, expected] of cases) {
+ assert.sameValue(d.total("seconds"), expected, descr);
+}
+
+// 2^32 - 1 = 4294967295
+const max = new Temporal.Duration(4294967295, 4294967295, 4294967295, 104249991374, 7, 36, 31, 999, 999, 999);
+TemporalHelpers.assertDuration(max, 4294967295, 4294967295, 4294967295, 104249991374, 7, 36, 31, 999, 999, 999);
+const min = new Temporal.Duration(-4294967295, -4294967295, -4294967295, -104249991374, -7, -36, -31, -999, -999, -999);
+TemporalHelpers.assertDuration(min, -4294967295, -4294967295, -4294967295, -104249991374, -7, -36, -31, -999, -999, -999);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/microseconds-undefined.js b/js/src/tests/test262/built-ins/Temporal/Duration/microseconds-undefined.js
new file mode 100644
index 0000000000..42ff2a962a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/microseconds-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.duration
+description: Undefined arguments should be treated as zero.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const args = [1, 1, 1, 1, 1, 1, 1, 1];
+
+const explicit = new Temporal.Duration(...args, undefined);
+TemporalHelpers.assertDuration(explicit, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, "explicit");
+
+const implicit = new Temporal.Duration(...args);
+TemporalHelpers.assertDuration(implicit, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, "implicit");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/milliseconds-undefined.js b/js/src/tests/test262/built-ins/Temporal/Duration/milliseconds-undefined.js
new file mode 100644
index 0000000000..cddc1ee873
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/milliseconds-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.duration
+description: Undefined arguments should be treated as zero.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const args = [1, 1, 1, 1, 1, 1, 1];
+
+const explicit = new Temporal.Duration(...args, undefined);
+TemporalHelpers.assertDuration(explicit, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, "explicit");
+
+const implicit = new Temporal.Duration(...args);
+TemporalHelpers.assertDuration(implicit, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, "implicit");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/minutes-undefined.js b/js/src/tests/test262/built-ins/Temporal/Duration/minutes-undefined.js
new file mode 100644
index 0000000000..993f76b296
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/minutes-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.duration
+description: Undefined arguments should be treated as zero.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const args = [1, 1, 1, 1, 1];
+
+const explicit = new Temporal.Duration(...args, undefined);
+TemporalHelpers.assertDuration(explicit, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, "explicit");
+
+const implicit = new Temporal.Duration(...args);
+TemporalHelpers.assertDuration(implicit, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, "implicit");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/mixed.js b/js/src/tests/test262/built-ins/Temporal/Duration/mixed.js
new file mode 100644
index 0000000000..ae7f0c4069
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/mixed.js
@@ -0,0 +1,22 @@
+// |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.duration
+description: Constructor with mixed signs.
+features: [Temporal]
+---*/
+
+assert.throws(RangeError, () => new Temporal.Duration(-1, 1, 1, 1, 1, 1, 1, 1, 1, 1));
+assert.throws(RangeError, () => new Temporal.Duration(1, -1, 1, 1, 1, 1, 1, 1, 1, 1));
+assert.throws(RangeError, () => new Temporal.Duration(1, 1, -1, 1, 1, 1, 1, 1, 1, 1));
+assert.throws(RangeError, () => new Temporal.Duration(1, 1, 1, -1, 1, 1, 1, 1, 1, 1));
+assert.throws(RangeError, () => new Temporal.Duration(1, 1, 1, 1, -1, 1, 1, 1, 1, 1));
+assert.throws(RangeError, () => new Temporal.Duration(1, 1, 1, 1, 1, -1, 1, 1, 1, 1));
+assert.throws(RangeError, () => new Temporal.Duration(1, 1, 1, 1, 1, 1, -1, 1, 1, 1));
+assert.throws(RangeError, () => new Temporal.Duration(1, 1, 1, 1, 1, 1, 1, -1, 1, 1));
+assert.throws(RangeError, () => new Temporal.Duration(1, 1, 1, 1, 1, 1, 1, 1, -1, 1));
+assert.throws(RangeError, () => new Temporal.Duration(1, 1, 1, 1, 1, 1, 1, 1, 1, -1));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/months-undefined.js b/js/src/tests/test262/built-ins/Temporal/Duration/months-undefined.js
new file mode 100644
index 0000000000..526c523f59
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/months-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.duration
+description: Undefined arguments should be treated as zero.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const args = [1];
+
+const explicit = new Temporal.Duration(...args, undefined);
+TemporalHelpers.assertDuration(explicit, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "explicit");
+
+const implicit = new Temporal.Duration(...args);
+TemporalHelpers.assertDuration(implicit, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "implicit");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/name.js b/js/src/tests/test262/built-ins/Temporal/Duration/name.js
new file mode 100644
index 0000000000..b35da70bde
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/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.duration
+description: Temporal.Duration.name is "Duration"
+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.Duration, "name", {
+ value: "Duration",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/nanoseconds-undefined.js b/js/src/tests/test262/built-ins/Temporal/Duration/nanoseconds-undefined.js
new file mode 100644
index 0000000000..3d186ed646
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/nanoseconds-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.duration
+description: Undefined arguments should be treated as zero.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const args = [1, 1, 1, 1, 1, 1, 1, 1, 1];
+
+const explicit = new Temporal.Duration(...args, undefined);
+TemporalHelpers.assertDuration(explicit, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, "explicit");
+
+const implicit = new Temporal.Duration(...args);
+TemporalHelpers.assertDuration(implicit, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, "implicit");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/negative-infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/Duration/negative-infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..cb25820ff4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/negative-infinity-throws-rangeerror.js
@@ -0,0 +1,84 @@
+// |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.
+
+/*---
+description: Temporal.Duration throws a RangeError if any value is -Infinity
+esid: sec-temporal.duration
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+assert.throws(RangeError, () => new Temporal.Duration(-Infinity));
+assert.throws(RangeError, () => new Temporal.Duration(0, -Infinity));
+assert.throws(RangeError, () => new Temporal.Duration(0, 0, -Infinity));
+assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, -Infinity));
+assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, -Infinity));
+assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, -Infinity));
+assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 0, -Infinity));
+assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, -Infinity));
+assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, -Infinity));
+assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, -Infinity));
+
+const O = (primitiveValue, propertyName) => (calls) => TemporalHelpers.toPrimitiveObserver(calls, primitiveValue, propertyName);
+const tests = [
+ [
+ "infinite years",
+ [O(-Infinity, "years"), O(0, "months"), O(0, "weeks"), O(0, "days"), O(0, "hours"), O(0, "minutes"), O(0, "seconds"), O(0, "milliseconds"), O(0, "microseconds"), O(0, "nanoseconds")],
+ ["get years.valueOf", "call years.valueOf"]
+ ],
+ [
+ "infinite months",
+ [O(0, "years"), O(-Infinity, "months"), O(0, "weeks"), O(0, "days"), O(0, "hours"), O(0, "minutes"), O(0, "seconds"), O(0, "milliseconds"), O(0, "microseconds"), O(0, "nanoseconds")],
+ ["get years.valueOf", "call years.valueOf", "get months.valueOf", "call months.valueOf"]
+ ],
+ [
+ "infinite weeks",
+ [O(0, "years"), O(0, "months"), O(-Infinity, "weeks"), O(0, "days"), O(0, "hours"), O(0, "minutes"), O(0, "seconds"), O(0, "milliseconds"), O(0, "microseconds"), O(0, "nanoseconds")],
+ ["get years.valueOf", "call years.valueOf", "get months.valueOf", "call months.valueOf", "get weeks.valueOf", "call weeks.valueOf"]
+ ],
+ [
+ "infinite days",
+ [O(0, "years"), O(0, "months"), O(0, "weeks"), O(-Infinity, "days"), O(0, "hours"), O(0, "minutes"), O(0, "seconds"), O(0, "milliseconds"), O(0, "microseconds"), O(0, "nanoseconds")],
+ ["get years.valueOf", "call years.valueOf", "get months.valueOf", "call months.valueOf", "get weeks.valueOf", "call weeks.valueOf", "get days.valueOf", "call days.valueOf"]
+ ],
+ [
+ "infinite hours",
+ [O(0, "years"), O(0, "months"), O(0, "weeks"), O(0, "days"), O(-Infinity, "hours"), O(0, "minutes"), O(0, "seconds"), O(0, "milliseconds"), O(0, "microseconds"), O(0, "nanoseconds")],
+ ["get years.valueOf", "call years.valueOf", "get months.valueOf", "call months.valueOf", "get weeks.valueOf", "call weeks.valueOf", "get days.valueOf", "call days.valueOf", "get hours.valueOf", "call hours.valueOf"]
+ ],
+ [
+ "infinite minutes",
+ [O(0, "years"), O(0, "months"), O(0, "weeks"), O(0, "days"), O(0, "hours"), O(-Infinity, "minutes"), O(0, "seconds"), O(0, "milliseconds"), O(0, "microseconds"), O(0, "nanoseconds")],
+ ["get years.valueOf", "call years.valueOf", "get months.valueOf", "call months.valueOf", "get weeks.valueOf", "call weeks.valueOf", "get days.valueOf", "call days.valueOf", "get hours.valueOf", "call hours.valueOf", "get minutes.valueOf", "call minutes.valueOf"]
+ ],
+ [
+ "infinite seconds",
+ [O(0, "years"), O(0, "months"), O(0, "weeks"), O(0, "days"), O(0, "hours"), O(0, "minutes"), O(-Infinity, "seconds"), O(0, "milliseconds"), O(0, "microseconds"), O(0, "nanoseconds")],
+ ["get years.valueOf", "call years.valueOf", "get months.valueOf", "call months.valueOf", "get weeks.valueOf", "call weeks.valueOf", "get days.valueOf", "call days.valueOf", "get hours.valueOf", "call hours.valueOf", "get minutes.valueOf", "call minutes.valueOf", "get seconds.valueOf", "call seconds.valueOf"]
+ ],
+ [
+ "infinite milliseconds",
+ [O(0, "years"), O(0, "months"), O(0, "weeks"), O(0, "days"), O(0, "hours"), O(0, "minutes"), O(0, "seconds"), O(-Infinity, "milliseconds"), O(0, "microseconds"), O(0, "nanoseconds")],
+ ["get years.valueOf", "call years.valueOf", "get months.valueOf", "call months.valueOf", "get weeks.valueOf", "call weeks.valueOf", "get days.valueOf", "call days.valueOf", "get hours.valueOf", "call hours.valueOf", "get minutes.valueOf", "call minutes.valueOf", "get seconds.valueOf", "call seconds.valueOf", "get milliseconds.valueOf", "call milliseconds.valueOf"]
+ ],
+ [
+ "infinite microseconds",
+ [O(0, "years"), O(0, "months"), O(0, "weeks"), O(0, "days"), O(0, "hours"), O(0, "minutes"), O(0, "seconds"), O(0, "milliseconds"), O(-Infinity, "microseconds"), O(0, "nanoseconds")],
+ ["get years.valueOf", "call years.valueOf", "get months.valueOf", "call months.valueOf", "get weeks.valueOf", "call weeks.valueOf", "get days.valueOf", "call days.valueOf", "get hours.valueOf", "call hours.valueOf", "get minutes.valueOf", "call minutes.valueOf", "get seconds.valueOf", "call seconds.valueOf", "get milliseconds.valueOf", "call milliseconds.valueOf", "get microseconds.valueOf", "call microseconds.valueOf"]
+ ],
+ [
+ "infinite nanoseconds",
+ [O(0, "years"), O(0, "months"), O(0, "weeks"), O(0, "days"), O(0, "hours"), O(0, "minutes"), O(0, "seconds"), O(0, "milliseconds"), O(0, "microseconds"), O(-Infinity, "nanoseconds")],
+ ["get years.valueOf", "call years.valueOf", "get months.valueOf", "call months.valueOf", "get weeks.valueOf", "call weeks.valueOf", "get days.valueOf", "call days.valueOf", "get hours.valueOf", "call hours.valueOf", "get minutes.valueOf", "call minutes.valueOf", "get seconds.valueOf", "call seconds.valueOf", "get milliseconds.valueOf", "call milliseconds.valueOf", "get microseconds.valueOf", "call microseconds.valueOf", "get nanoseconds.valueOf", "call nanoseconds.valueOf"]
+ ],
+];
+
+for (const [description, args, expected] of tests) {
+ const actual = [];
+ const args_ = args.map((o) => o(actual));
+ assert.throws(RangeError, () => new Temporal.Duration(...args_), description);
+ assert.compareArray(actual, expected, `${description} order of operations`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/out-of-range.js b/js/src/tests/test262/built-ins/Temporal/Duration/out-of-range.js
new file mode 100644
index 0000000000..970148ead7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/out-of-range.js
@@ -0,0 +1,47 @@
+// |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.duration
+description: Various arguments to the Duration constructor that are out of range
+features: [Temporal]
+---*/
+
+// 2^32 = 4294967296
+assert.throws(RangeError, () => new Temporal.Duration(4294967296), "years > max");
+assert.throws(RangeError, () => new Temporal.Duration(-4294967296), "years < min");
+assert.throws(RangeError, () => new Temporal.Duration(0, 4294967296), "months > max");
+assert.throws(RangeError, () => new Temporal.Duration(0, -4294967296), "months < min");
+assert.throws(RangeError, () => new Temporal.Duration(0, 0, 4294967296), "days > max");
+assert.throws(RangeError, () => new Temporal.Duration(0, 0, -4294967296), "days < min");
+
+// ceil(max safe integer / 86400) = 104249991375
+assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 104249991375), "days > max");
+assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 104249991374, 24), "hours balance into days > max");
+assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, -104249991375), "days < min");
+assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, -104249991374, -24), "hours balance into days < min");
+
+// ceil(max safe integer / 3600) = 2501999792984
+assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 2501999792984), "hours > max");
+assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 2501999792983, 60), "minutes balance into hours > max");
+assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, -2501999792984), "hours < min");
+assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, -2501999792983, -60), "minutes balance into hours < min");
+
+// ceil(max safe integer / 60) = 150119987579017
+assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 150119987579017), "minutes > max");
+assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 150119987579016, 60), "seconds balance into minutes > max");
+assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, -150119987579017), "minutes < min");
+assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, -150119987579016, -60), "seconds balance into minutes < min");
+
+// 2^53 = 9007199254740992
+assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 0, 9007199254740992), "seconds > max");
+assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 0, 9007199254740991, 1000), "ms balance into seconds > max");
+assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 0, 9007199254740991, 999, 1000), "µs balance into seconds > max");
+assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 0, 9007199254740991, 999, 999, 1000), "ns balance into seconds > max");
+assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 0, -9007199254740992), "seconds < min");
+assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 0, -9007199254740991, -1000), "ms balance into seconds < min");
+assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 0, -9007199254740991, -999, -1000), "µs balance into seconds < min");
+assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 0, -9007199254740991, -999, -999, -1000), "ns balance into seconds < min");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Duration/prop-desc.js
new file mode 100644
index 0000000000..e6d24f4b51
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/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.duration
+description: The "Duration" property of Temporal
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Duration,
+ "function",
+ "`typeof Duration` is `function`"
+);
+
+verifyProperty(Temporal, "Duration", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/basic.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/basic.js
new file mode 100644
index 0000000000..24057b87ea
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/basic.js
@@ -0,0 +1,42 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.abs
+description: Temporal.Duration.prototype.abs will return absolute value of the input duration.
+info: |
+ 1. Let duration be the this value.
+ 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]).
+ 3. Return ? CreateTemporalDuration(abs(duration.[[Years]]), abs(duration.[[Months]]), abs(duration.[[Weeks]]), abs(duration.[[Days]]), abs(duration.[[Hours]]), abs(duration.[[Minutes]]), abs(duration.[[Seconds]]), abs(duration.[[Milliseconds]]), abs(duration.[[Microseconds]]), abs(duration.[[Nanoseconds]])).
+
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+let d1 = new Temporal.Duration();
+TemporalHelpers.assertDuration(
+ d1.abs(), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "empty");
+
+let d2 = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
+TemporalHelpers.assertDuration(
+ d2.abs(), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, "positive");
+
+let d3 = new Temporal.Duration(1e5, 2e5, 3e5, 4e5, 5e5, 6e5, 7e5, 8e5, 9e5, 10e5);
+TemporalHelpers.assertDuration(
+ d3.abs(), 1e5, 2e5, 3e5, 4e5, 5e5, 6e5, 7e5, 8e5, 9e5, 10e5, "large positive");
+
+let d4 = new Temporal.Duration(-1, -2, -3, -4, -5, -6, -7, -8, -9, -10);
+TemporalHelpers.assertDuration(
+ d4.abs(), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, "negative");
+
+// Test with some zeros
+let d5 = new Temporal.Duration(1, 0, 3, 0, 5, 0, 7, 0, 9, 0);
+TemporalHelpers.assertDuration(
+ d5.abs(), 1, 0, 3, 0, 5, 0, 7, 0, 9, 0, "some zeros");
+
+let d6 = new Temporal.Duration(0, 2, 0, 4, 0, 6, 0, 8, 0, 10);
+TemporalHelpers.assertDuration(
+ d6.abs(), 0, 2, 0, 4, 0, 6, 0, 8, 0, 10, "other zeros");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/branding.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/branding.js
new file mode 100644
index 0000000000..2e341fd15b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/branding.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.duration.prototype.abs
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const abs = Temporal.Duration.prototype.abs;
+
+assert.sameValue(typeof abs, "function");
+
+assert.throws(TypeError, () => abs.call(undefined), "undefined");
+assert.throws(TypeError, () => abs.call(null), "null");
+assert.throws(TypeError, () => abs.call(true), "true");
+assert.throws(TypeError, () => abs.call(""), "empty string");
+assert.throws(TypeError, () => abs.call(Symbol()), "symbol");
+assert.throws(TypeError, () => abs.call(1), "1");
+assert.throws(TypeError, () => abs.call({}), "plain object");
+assert.throws(TypeError, () => abs.call(Temporal.Duration), "Temporal.Duration");
+assert.throws(TypeError, () => abs.call(Temporal.Duration.prototype), "Temporal.Duration.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/browser.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/builtin.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/builtin.js
new file mode 100644
index 0000000000..e699e3e0e6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/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.duration.prototype.abs
+description: >
+ Tests that Temporal.Duration.prototype.abs
+ 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.Duration.prototype.abs),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Duration.prototype.abs),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Duration.prototype.abs),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Duration.prototype.abs.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/length.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/length.js
new file mode 100644
index 0000000000..6349fa1b8c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/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.duration.prototype.abs
+description: Temporal.Duration.prototype.abs.length is 0
+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.Duration.prototype.abs, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/name.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/name.js
new file mode 100644
index 0000000000..fe8fd6fe2b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/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.duration.prototype.abs
+description: Temporal.Duration.prototype.abs.name is "abs".
+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.Duration.prototype.abs, "name", {
+ value: "abs",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/new-object.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/new-object.js
new file mode 100644
index 0000000000..3210711449
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/new-object.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.duration.prototype.abs
+description: Temporal.Duration.prototype.abs returns a new object.
+features: [Temporal]
+---*/
+
+let d1 = new Temporal.Duration();
+assert.notSameValue(d1.abs(), d1);
+
+let d2 = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
+assert.notSameValue(d2.abs(), d2);
+
+let d3 = new Temporal.Duration(1e5, 2e5, 3e5, 4e5, 5e5, 6e5, 7e5, 8e5, 9e5, 10e5);
+assert.notSameValue(d3.abs(), d3);
+
+let d4 = new Temporal.Duration(-1, -2, -3, -4, -5, -6, -7, -8, -9, -10);
+assert.notSameValue(d4.abs(), d4);
+
+// Test with some zeros
+let d5 = new Temporal.Duration(1, 0, 3, 0, 5, 0, 7, 0, 9, 0);
+assert.notSameValue(d5.abs(), d5);
+
+let d6 = new Temporal.Duration(0, 2, 0, 4, 0, 6, 0, 8, 0, 10);
+assert.notSameValue(d6.abs(), d6);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/not-a-constructor.js
new file mode 100644
index 0000000000..338939d33e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/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.duration.prototype.abs
+description: >
+ Temporal.Duration.prototype.abs 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.Duration.prototype.abs();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Duration.prototype.abs), false,
+ "isConstructor(Temporal.Duration.prototype.abs)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/prop-desc.js
new file mode 100644
index 0000000000..49ed01dd2b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/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.duration.prototype.abs
+description: The "abs" property of Temporal.Duration.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Duration.prototype.abs,
+ "function",
+ "`typeof Duration.prototype.abs` is `function`"
+);
+
+verifyProperty(Temporal.Duration.prototype, "abs", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/shell.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/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/Duration/prototype/abs/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/subclassing-ignored.js
new file mode 100644
index 0000000000..b29ed06990
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/subclassing-ignored.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.duration.prototype.abs
+description: Objects of a subclass are never created as return values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.Duration,
+ [0, 0, 0, -4, -5, -6, -7, -987, -654, -321],
+ "abs",
+ [],
+ (result) => TemporalHelpers.assertDuration(result, 0, 0, 0, 4, 5, 6, 7, 987, 654, 321),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/argument-duration-max.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/argument-duration-max.js
new file mode 100644
index 0000000000..2e3d009987
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/argument-duration-max.js
@@ -0,0 +1,45 @@
+// |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.duration.prototype.add
+description: Maximum allowed duration
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration();
+
+const maxCases = [
+ ["P104249991374DT7H36M31.999999999S", "string with max days"],
+ [{ days: 104249991374, nanoseconds: 27391999999999 }, "property bag with max days"],
+ ["PT2501999792983H36M31.999999999S", "string with max hours"],
+ [{ hours: 2501999792983, nanoseconds: 2191999999999 }, "property bag with max hours"],
+ ["PT150119987579016M31.999999999S", "string with max minutes"],
+ [{ minutes: 150119987579016, nanoseconds: 31999999999 }, "property bag with max minutes"],
+ ["PT9007199254740991.999999999S", "string with max seconds"],
+ [{ seconds: 9007199254740991, nanoseconds: 999999999 }, "property bag with max seconds"],
+];
+
+for (const [arg, descr] of maxCases) {
+ const result = instance.add(arg);
+ assert.sameValue(result.total("seconds"), 9007199254740991.999999999, `operation succeeds with ${descr}`);
+}
+
+const minCases = [
+ ["-P104249991374DT7H36M31.999999999S", "string with min days"],
+ [{ days: -104249991374, nanoseconds: -27391999999999 }, "property bag with min days"],
+ ["-PT2501999792983H36M31.999999999S", "string with min hours"],
+ [{ hours: -2501999792983, nanoseconds: -2191999999999 }, "property bag with min hours"],
+ ["-PT150119987579016M31.999999999S", "string with min minutes"],
+ [{ minutes: -150119987579016, nanoseconds: -31999999999 }, "property bag with min minutes"],
+ ["-PT9007199254740991.999999999S", "string with min seconds"],
+ [{ seconds: -9007199254740991, nanoseconds: -999999999 }, "property bag with min seconds"],
+];
+
+for (const [arg, descr] of minCases) {
+ const result = instance.add(arg);
+ assert.sameValue(result.total("seconds"), -9007199254740991.999999999, `operation succeeds with ${descr}`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/argument-duration-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/argument-duration-out-of-range.js
new file mode 100644
index 0000000000..0e386030e3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/argument-duration-out-of-range.js
@@ -0,0 +1,75 @@
+// |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.duration.prototype.add
+description: Duration-like argument that is out of range
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration();
+
+const cases = [
+ // 2^32 = 4294967296
+ ["P4294967296Y", "string with years > max"],
+ [{ years: 4294967296 }, "property bag with years > max"],
+ ["-P4294967296Y", "string with years < min"],
+ [{ years: -4294967296 }, "property bag with years < min"],
+ ["P4294967296M", "string with months > max"],
+ [{ months: 4294967296 }, "property bag with months > max"],
+ ["-P4294967296M", "string with months < min"],
+ [{ months: -4294967296 }, "property bag with months < min"],
+ ["P4294967296W", "string with weeks > max"],
+ [{ weeks: 4294967296 }, "property bag with weeks > max"],
+ ["-P4294967296W", "string with weeks < min"],
+ [{ weeks: -4294967296 }, "property bag with weeks < min"],
+
+ // ceil(max safe integer / 86400) = 104249991375
+ ["P104249991375D", "string with days > max"],
+ [{ days: 104249991375 }, "property bag with days > max"],
+ ["P104249991374DT24H", "string where hours balance into days > max"],
+ [{ days: 104249991374, hours: 24 }, "property bag where hours balance into days > max"],
+ ["-P104249991375D", "string with days < min"],
+ [{ days: -104249991375 }, "property bag with days < min"],
+ ["-P104249991374DT24H", "string where hours balance into days < min"],
+ [{ days: -104249991374, hours: -24 }, "property bag where hours balance into days < min"],
+
+ // ceil(max safe integer / 3600) = 2501999792984
+ ["PT2501999792984H", "string with hours > max"],
+ [{ hours: 2501999792984 }, "property bag with hours > max"],
+ ["PT2501999792983H60M", "string where minutes balance into hours > max"],
+ [{ hours: 2501999792983, minutes: 60 }, "property bag where minutes balance into hours > max"],
+ ["-PT2501999792984H", "string with hours < min"],
+ [{ hours: -2501999792984 }, "property bag with hours < min"],
+ ["-PT2501999792983H60M", "string where minutes balance into hours < min"],
+ [{ hours: -2501999792983, minutes: -60 }, "property bag where minutes balance into hours < min"],
+
+ // ceil(max safe integer / 60) = 150119987579017
+ ["PT150119987579017M", "string with minutes > max"],
+ [{ minutes: 150119987579017 }, "property bag with minutes > max"],
+ ["PT150119987579016M60S", "string where seconds balance into minutes > max"],
+ [{ minutes: 150119987579016, seconds: 60 }, "property bag where seconds balance into minutes > max"],
+ ["-PT150119987579017M", "string with minutes < min"],
+ [{ minutes: -150119987579017 }, "property bag with minutes < min"],
+ ["-PT150119987579016M60S", "string where seconds balance into minutes < min"],
+ [{ minutes: -150119987579016, seconds: -60 }, "property bag where seconds balance into minutes < min"],
+
+ // 2^53 = 9007199254740992
+ ["PT9007199254740992S", "string with seconds > max"],
+ [{ seconds: 9007199254740992 }, "property bag with seconds > max"],
+ [{ seconds: 9007199254740991, milliseconds: 1000 }, "property bag where milliseconds balance into seconds > max"],
+ [{ seconds: 9007199254740991, microseconds: 1000000 }, "property bag where microseconds balance into seconds > max"],
+ [{ seconds: 9007199254740991, nanoseconds: 1000000000 }, "property bag where nanoseconds balance into seconds > max"],
+ ["-PT9007199254740992S", "string with seconds < min"],
+ [{ seconds: -9007199254740992 }, "property bag with seconds < min"],
+ [{ seconds: -9007199254740991, milliseconds: -1000 }, "property bag where milliseconds balance into seconds < min"],
+ [{ seconds: -9007199254740991, microseconds: -1000000 }, "property bag where microseconds balance into seconds < min"],
+ [{ seconds: -9007199254740991, nanoseconds: -1000000000 }, "property bag where nanoseconds balance into seconds < min"],
+];
+
+for (const [arg, descr] of cases) {
+ assert.throws(RangeError, () => instance.add(arg), `${descr} is out of range`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/argument-duration-precision-exact-numerical-values.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/argument-duration-precision-exact-numerical-values.js
new file mode 100644
index 0000000000..3810427fee
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/argument-duration-precision-exact-numerical-values.js
@@ -0,0 +1,79 @@
+// |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.duration.prototype.add
+description: >
+ Duration-like argument performs the range check with minimal floating point
+ precision loss
+features: [Temporal]
+---*/
+
+// Based on a test case by André Bargull
+
+const instance = new Temporal.Duration();
+
+const balanceFailCases = [
+ [
+ {
+ milliseconds: 4503599627370497_000, // ℝ(𝔽(4503599627370497000)) = 4503599627370497024
+ microseconds: 4503599627370495_000000, // ℝ(𝔽(4503599627370495000000)) = 4503599627370494951424
+ },
+ // 4503599627370497024 / 1000 + 4503599627370494951424 / 1000000 is
+ // 9007199254740991.975424, which is below the limit of 2**53
+ "case where floating point inaccuracy brings total below limit, positive"
+ ],
+ [
+ {
+ milliseconds: -4503599627370497_000,
+ microseconds: -4503599627370495_000000,
+ },
+ "case where floating point inaccuracy brings total below limit, negative"
+ ],
+];
+
+// Adding a duration, even to a zero duration, causes rebalancing to the current
+// largestUnit. These cases will not fail when converting the property bag to a
+// duration, but they will fail during balancing after the addition when storing
+// the resulting duration, because:
+// 9007199254740991.975424 seconds balances into 9007199254740991975 ms, 424 µs
+// ℝ(𝔽(9007199254740991975)) ms = 9007199254740992000 ms
+// which is once again above the limit due to floating point inaccuracy.
+
+for (const [arg, descr] of balanceFailCases) {
+ assert.throws(RangeError, () => instance.add(arg), descr + ': ℝ(𝔽(x)) operation after balancing brings total over limit')
+}
+
+// These cases will balance to a largestUnit of seconds, which will not be
+// inaccurate.
+
+const balanceSuccessCases = [
+ [
+ {
+ seconds: 2,
+ milliseconds: 4503599627370496_500, // ℝ(𝔽(4503599627370496500)) = 4503599627370496512
+ microseconds: 4503599627370493_500000, // ℝ(𝔽(4503599627370493500000)) = 4503599627370493378560
+ },
+ // 1 + 4503599627370496512 / 1000 + 4503599627370493378560 / 1000000 is
+ // 9007199254740991.89056, which is below the limit of 2**53
+ "PT9007199254740991.89056S",
+ "case where floating point inaccuracy brings total below limit, positive"
+ ],
+ [
+ {
+ seconds: -2,
+ milliseconds: -4503599627370496_500,
+ microseconds: -4503599627370493_500000,
+ },
+ "-PT9007199254740991.89056S",
+ "case where floating point inaccuracy brings total below limit, negative"
+ ],
+];
+
+for (const [arg, string, descr] of balanceSuccessCases) {
+ const result = instance.add(arg);
+ assert.sameValue(result.toString(), string, descr);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/argument-invalid-property.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/argument-invalid-property.js
new file mode 100644
index 0000000000..77b16745d2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/argument-invalid-property.js
@@ -0,0 +1,31 @@
+// |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.duration.prototype.add
+description: temporalDurationLike object must contain at least one correctly spelled property
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(0, 0, 0, 1, 2, 3, 4, 987, 654, 321);
+
+assert.throws(
+ TypeError,
+ () => instance.add({}),
+ "Throws TypeError if no property is present"
+);
+
+assert.throws(
+ TypeError,
+ () => instance.add({ nonsense: true }),
+ "Throws TypeError if no recognized property is present"
+);
+
+assert.throws(
+ TypeError,
+ () => instance.add({ sign: 1 }),
+ "Sign property is not recognized"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/argument-mixed-sign.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/argument-mixed-sign.js
new file mode 100644
index 0000000000..26e6a13da3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/argument-mixed-sign.js
@@ -0,0 +1,20 @@
+// |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.duration.prototype.add
+description: Positive and negative values in the temporalDurationLike argument are not acceptable
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(0, 0, 0, 1, 2, 3, 4, 987, 654, 321);
+
+assert.throws(
+ RangeError,
+ () => instance.add({ hours: 1, minutes: -30 }),
+ `mixed positive and negative values always throw`
+);
+
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/argument-not-object.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/argument-not-object.js
new file mode 100644
index 0000000000..a4c30e70ad
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/argument-not-object.js
@@ -0,0 +1,22 @@
+// |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.duration.prototype.add
+description: Passing a primitive other than string to add() throws
+features: [Symbol, Temporal]
+---*/
+
+const instance = new Temporal.Duration(0, 0, 0, 1, 2, 3, 4, 987, 654, 321);
+assert.throws(TypeError, () => instance.add(undefined), "undefined");
+assert.throws(TypeError, () => instance.add(null), "null");
+assert.throws(TypeError, () => instance.add(true), "boolean");
+assert.throws(RangeError, () => instance.add(""), "empty string");
+assert.throws(TypeError, () => instance.add(Symbol()), "Symbol");
+assert.throws(TypeError, () => instance.add(7), "number");
+assert.throws(TypeError, () => instance.add(7n), "bigint");
+assert.throws(TypeError, () => instance.add([]), "array");
+assert.throws(TypeError, () => instance.add(() => {}), "function");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/argument-singular-properties.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/argument-singular-properties.js
new file mode 100644
index 0000000000..78ac4c2a0c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/argument-singular-properties.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.duration.prototype.add
+description: Singular properties in the property bag are always ignored
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(0, 0, 0, 1, 2, 3, 4, 987, 654, 321);
+
+[
+ { year: 1 },
+ { month: 2 },
+ { week: 3 },
+ { day: 4 },
+ { hour: 5 },
+ { minute: 6 },
+ { second: 7 },
+ { millisecond: 8 },
+ { microsecond: 9 },
+ { nanosecond: 10 },
+].forEach((badObject) => {
+ assert.throws(TypeError, () => instance.add(badObject),
+ "Throw TypeError if temporalDurationLike is not valid");
+});
+
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/argument-string-fractional-units-rounding-mode.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/argument-string-fractional-units-rounding-mode.js
new file mode 100644
index 0000000000..f7456fea97
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/argument-string-fractional-units-rounding-mode.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.duration.prototype.add
+description: Strings with fractional duration units are rounded with the correct rounding mode
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const blank = new Temporal.Duration();
+
+TemporalHelpers.assertDuration(blank.add("PT1.03125H"), 0, 0, 0, 0, 1, 1, 52, 500, 0, 0,
+ "positive fractional units rounded with correct rounding mode");
+TemporalHelpers.assertDuration(blank.add("-PT1.03125H"), 0, 0, 0, 0, -1, -1, -52, -500, 0, 0,
+ "negative fractional units rounded with correct rounding mode");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/argument-string-negative-fractional-units.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/argument-string-negative-fractional-units.js
new file mode 100644
index 0000000000..4dbd149c5e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/argument-string-negative-fractional-units.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.duration.prototype.add
+description: Strings with fractional duration units are treated with the correct sign
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration();
+
+const resultHours = instance.add("-PT24.567890123H");
+TemporalHelpers.assertDuration(resultHours, 0, 0, 0, 0, -24, -34, -4, -404, -442, -800, "negative fractional hours");
+
+const resultMinutes = instance.add("-PT1440.567890123M");
+TemporalHelpers.assertDuration(resultMinutes, 0, 0, 0, 0, 0, -1440, -34, -73, -407, -380, "negative fractional minutes");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/argument-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/argument-string.js
new file mode 100644
index 0000000000..cb043be3ba
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/argument-string.js
@@ -0,0 +1,17 @@
+// |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.duration.prototype.add
+description: String arguments are supported.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const duration = Temporal.Duration.from({ days: 1, minutes: 5 });
+const result = duration.add("P2DT5M");
+TemporalHelpers.assertDuration(result, 0, 0, 0, 3, 0, 10, 0, 0, 0, 0, "String argument should be supported");
+assert.throws(RangeError, () => duration.add("2DT5M"), "Invalid string argument should throw");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/balance-negative-result.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/balance-negative-result.js
new file mode 100644
index 0000000000..e9166f2594
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/balance-negative-result.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.duration.prototype.add
+description: A negative duration result is balanced correctly by the modulo operation in NanosecondsToDays
+info: |
+ sec-temporal-nanosecondstodays step 6:
+ 6. If Type(_relativeTo_) is not Object or _relativeTo_ does not have an [[InitializedTemporalZonedDateTime]] internal slot, then
+ a. Return the new Record { ..., [[Nanoseconds]]: abs(_nanoseconds_) modulo _dayLengthNs_ × _sign_, ... }.
+ sec-temporal-balanceduration step 4:
+ 4. If _largestUnit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*, then
+ a. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _relativeTo_).
+ sec-temporal-addduration steps 5–6:
+ 5. If _relativeTo_ is *undefined*, then
+ ...
+ b. Let _result_ be ! BalanceDuration(_d1_ + _d2_, _h1_ + _h2_, _min1_ + _min2_, _s1_ + _s2_, _ms1_ + _ms2_, _mus1_ + _mus2_, _ns1_ + _ns2_, _largestUnit_).
+ ...
+ 6. Else if _relativeTo_ has an [[InitializedTemporalPlainDateTime]] internal slot, then
+ ...
+ n. Let _result_ be ! BalanceDuration(_dateDifference_.[[Days]], _h1_ + _h2_, _min1_ + _min2_, _s1_ + _s2_, _ms1_ + _ms2_, _mus1_ + _mus2_, _ns1_ + _ns2_, _largestUnit_).
+ sec-temporal.duration.prototype.add step 6:
+ 6. Let _result_ be ? AddDuration(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], _other_.[[Years]], _other_.[[Months]], _other_.[[Weeks]], _other_.[[Days]], _other_.[[Hours]], _other_.[[Minutes]], _other_.[[Seconds]], _other_.[[Milliseconds]], _other_.[[Microseconds]], _other_.[[Nanoseconds]], _relativeTo_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const duration1 = new Temporal.Duration(0, 0, 0, 0, -60);
+const duration2 = new Temporal.Duration(0, 0, 0, -1);
+
+const resultNotRelative = duration1.add(duration2);
+TemporalHelpers.assertDuration(resultNotRelative, 0, 0, 0, -3, -12, 0, 0, 0, 0, 0);
+
+const relativeTo = new Temporal.PlainDateTime(2000, 1, 1);
+const resultRelative = duration1.add(duration2, { relativeTo });
+TemporalHelpers.assertDuration(resultRelative, 0, 0, 0, -3, -12, 0, 0, 0, 0, 0);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/balance-negative-time-units.js
new file mode 100644
index 0000000000..55a61f206e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/balance-negative-time-units.js
@@ -0,0 +1,63 @@
+// |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.duration.prototype.add
+description: Negative time fields in relativeTo 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-differenceisodatetime step 2:
+ 2. Let _timeDifference_ be ? DifferenceTime(_h1_, _min1_, _s1_, _ms1_, _mus1_, _ns1_, _h2_, _min2_, _s2_, _ms2_, _mus2_, _ns2_).
+ sec-temporal-differencezoneddatetime step 7:
+ 7. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _calendar_, _largestUnit_, _options_).
+ sec-temporal-addduration step 7.g.i:
+ i. Let _result_ be ? DifferenceZonedDateTime(_relativeTo_.[[Nanoseconds]], _endNs_, _timeZone_, _calendar_, _largestUnit_).
+ sec-temporal.duration.prototype.add step 6:
+ 6. Let _result_ be ? AddDuration(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], _other_.[[Years]], _other_.[[Months]], _other_.[[Weeks]], _other_.[[Days]], _other_.[[Hours]], _other_.[[Minutes]], _other_.[[Seconds]], _other_.[[Milliseconds]], _other_.[[Microseconds]], _other_.[[Nanoseconds]], _relativeTo_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(0, 0, 0, 0, 1, 1, 1, 1, 1, 1);
+
+const timeZone = new Temporal.TimeZone("UTC");
+const relativeTo = new Temporal.ZonedDateTime(830998861_000_000_000n, timeZone);
+// This code path is encountered if largestUnit is years, months, weeks, or days
+// and relativeTo is a ZonedDateTime
+const options = { largestUnit: "days", relativeTo };
+
+const result1 = duration.add(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, -2), options);
+TemporalHelpers.assertDuration(result1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 999, "nanoseconds balance");
+
+const result2 = duration.add(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, -2), options);
+TemporalHelpers.assertDuration(result2, 0, 0, 0, 0, 1, 1, 1, 0, 999, 1, "microseconds balance");
+
+const result3 = duration.add(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, -2), options);
+TemporalHelpers.assertDuration(result3, 0, 0, 0, 0, 1, 1, 0, 999, 1, 1, "milliseconds balance");
+
+const result4 = duration.add(new Temporal.Duration(0, 0, 0, 0, 0, 0, -2), options);
+TemporalHelpers.assertDuration(result4, 0, 0, 0, 0, 1, 0, 59, 1, 1, 1, "seconds balance");
+
+const result5 = duration.add(new Temporal.Duration(0, 0, 0, 0, 0, -2), options);
+TemporalHelpers.assertDuration(result5, 0, 0, 0, 0, 0, 59, 1, 1, 1, 1, "minutes balance");
+
+// This one is different because hours are later balanced again in BalanceDuration
+const result6 = duration.add(new Temporal.Duration(0, 0, 0, 0, -2), options);
+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/Duration/prototype/add/basic.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/basic.js
new file mode 100644
index 0000000000..220437bd6f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/basic.js
@@ -0,0 +1,34 @@
+// |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.duration.prototype.add
+description: Basic behavior
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const duration1 = Temporal.Duration.from({ days: 1, minutes: 5 });
+TemporalHelpers.assertDuration(duration1.add({ days: 2, minutes: 5 }),
+ 0, 0, 0, 3, 0, 10, 0, 0, 0, 0, "positive same units");
+TemporalHelpers.assertDuration(duration1.add({ hours: 12, seconds: 30 }),
+ 0, 0, 0, 1, 12, 5, 30, 0, 0, 0, "positive different units");
+TemporalHelpers.assertDuration(Temporal.Duration.from("P3DT10M").add({ days: -2, minutes: -5 }),
+ 0, 0, 0, 1, 0, 5, 0, 0, 0, 0, "negative same units");
+TemporalHelpers.assertDuration(Temporal.Duration.from("P1DT12H5M30S").add({ hours: -12, seconds: -30 }),
+ 0, 0, 0, 1, 0, 5, 0, 0, 0, 0, "negative different units");
+const duration2 = Temporal.Duration.from("P50DT50H50M50.500500500S");
+TemporalHelpers.assertDuration(duration2.add(duration2),
+ 0, 0, 0, 104, 5, 41, 41, 1, 1, 0, "balancing positive");
+const duration3 = Temporal.Duration.from({ hours: -1, seconds: -60 });
+TemporalHelpers.assertDuration(duration3.add({ minutes: 122 }),
+ 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, "balancing flipped sign 1");
+const duration4 = Temporal.Duration.from({ hours: -1, seconds: -3721 });
+TemporalHelpers.assertDuration(duration4.add({ minutes: 61, nanoseconds: 3722000000001 }),
+ 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, "balancing flipped sign 2");
+TemporalHelpers.assertDuration(duration1.add({ month: 1, days: 1 }),
+ 0, 0, 0, 2, 0, 5, 0, 0, 0, 0,
+ "incorrectly-spelled properties are ignored");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/branding.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/branding.js
new file mode 100644
index 0000000000..8171aaa3dc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/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.duration.prototype.add
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const add = Temporal.Duration.prototype.add;
+
+assert.sameValue(typeof add, "function");
+
+const args = [new Temporal.Duration(0, 0, 0, 0, 5)];
+
+assert.throws(TypeError, () => add.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => add.apply(null, args), "null");
+assert.throws(TypeError, () => add.apply(true, args), "true");
+assert.throws(TypeError, () => add.apply("", args), "empty string");
+assert.throws(TypeError, () => add.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => add.apply(1, args), "1");
+assert.throws(TypeError, () => add.apply({}, args), "plain object");
+assert.throws(TypeError, () => add.apply(Temporal.Duration, args), "Temporal.Duration");
+assert.throws(TypeError, () => add.apply(Temporal.Duration.prototype, args), "Temporal.Duration.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/browser.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/builtin.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/builtin.js
new file mode 100644
index 0000000000..531305ea89
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/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.duration.prototype.add
+description: >
+ Tests that Temporal.Duration.prototype.add
+ 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.Duration.prototype.add),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Duration.prototype.add),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Duration.prototype.add),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Duration.prototype.add.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/calendar-dateadd-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/calendar-dateadd-called-with-options-undefined.js
new file mode 100644
index 0000000000..4955abacdd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/calendar-dateadd-called-with-options-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.duration.prototype.add
+description: >
+ BuiltinTimeZoneGetInstantFor calls Calendar.dateAdd with undefined as the
+ options value
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarDateAddUndefinedOptions();
+const timeZone = TemporalHelpers.oneShiftTimeZone(new Temporal.Instant(0n), 3600e9);
+const instance = new Temporal.Duration(1, 1, 1, 1);
+instance.add(instance, { relativeTo: new Temporal.ZonedDateTime(0n, timeZone, calendar) });
+assert.sameValue(calendar.dateAddCallCount, 3);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/calendar-dateadd-called-with-plaindate-instance.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/calendar-dateadd-called-with-plaindate-instance.js
new file mode 100644
index 0000000000..59ad94274b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/calendar-dateadd-called-with-plaindate-instance.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.duration.prototype.add
+description: >
+ relativeTo parameters that are not ZonedDateTime or undefined, are always
+ converted to PlainDate for observable calendar calls
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarDateAddPlainDateInstance();
+const instance = new Temporal.Duration(1, 1, 1, 1);
+const relativeTo = new Temporal.PlainDate(2000, 1, 1, calendar);
+calendar.specificPlainDate = relativeTo;
+instance.add(instance, { relativeTo });
+assert(calendar.dateAddCallCount > 0, "assertions in calendar.dateAdd() should have been tested");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/calendar-dateadd.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/calendar-dateadd.js
new file mode 100644
index 0000000000..899590bc37
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/calendar-dateadd.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.duration.prototype.add
+description: Duration.prototype.add should call dateAdd with the appropriate values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+const expected = [
+ {
+ plainDate: [1920, 5, "M05", 3],
+ duration: [2, 0, 0, 4, 0, 0, 0, 0, 0, 0],
+ },
+ {
+ plainDate: [1922, 5, "M05", 7],
+ duration: [0, 10, 0, 0, 0, 0, 0, 0, 0, 0],
+ },
+];
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ dateAdd(plainDate, duration, options) {
+ TemporalHelpers.assertPlainDate(plainDate, ...expected[calls].plainDate,
+ `plainDate argument ${calls}`);
+ TemporalHelpers.assertDuration(duration, ...expected[calls].duration,
+ `duration argument ${calls}`);
+ assert.sameValue(options, undefined, "options argument");
+ ++calls;
+ return super.dateAdd(plainDate, duration, options);
+ }
+}
+const relativeTo = new Temporal.PlainDate(1920, 5, 3, new CustomCalendar());
+const duration = new Temporal.Duration(2, 0, 0, 4, 2);
+const result = duration.add({ months: 10, hours: 14 }, { relativeTo });
+TemporalHelpers.assertDuration(result, 2, 10, 0, 4, 16, 0, 0, 0, 0, 0, "result");
+assert.sameValue(calls, 2, "should have called dateAdd");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/calendar-dateuntil-called-with-singular-largestunit.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/calendar-dateuntil-called-with-singular-largestunit.js
new file mode 100644
index 0000000000..c2f15e5d42
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/calendar-dateuntil-called-with-singular-largestunit.js
@@ -0,0 +1,81 @@
+// |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.duration.prototype.add
+description: The options object passed to calendar.dateUntil has a largestUnit property with its value in the singular form
+info: |
+ sec-temporal.duration.prototype.add step 6:
+ 6. Let _result_ be ? AddDuration(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], _other_.[[Years]], _other_.[[Months]], _other_.[[Weeks]], _other_.[[Days]], _other_.[[Hours]], _other_.[[Minutes]], _other_.[[Seconds]], _other_.[[Milliseconds]], _other_.[[Microseconds]], _other_.[[Nanoseconds]], _relativeTo_).
+ sec-temporal-addduration steps 6-7:
+ 6. If _relativeTo_ has an [[InitializedTemporalPlainDateTime]] internal slot, then
+ ...
+ j. Let _dateLargestUnit_ be ! LargerOfTwoTemporalUnits(*"day"*, _largestUnit_).
+ k. Let _differenceOptions_ be ! OrdinaryObjectCreate(*null*).
+ l. Perform ! CreateDataPropertyOrThrow(_differenceOptions_, *"largestUnit"*, _dateLargestUnit_).
+ m. Let _dateDifference_ be ? CalendarDateUntil(_calendar_, _datePart_, _end_, _differenceOptions_).
+ ...
+ 7. Else,
+ a. Assert: _relativeTo_ has an [[IntializedTemporalZonedDateTime]] internal slot.
+ ...
+ f. If _largestUnit_ is not one of *"year"*, *"month"*, *"week"*, or *"day"*, then
+ ...
+ g. Else,
+ i. Let _result_ be ? DifferenceZonedDateTime(_relativeTo_.[[Nanoseconds]], _endNs_, _timeZone_, _calendar_, _largestUnit_).
+ sec-temporal-differencezoneddatetime steps 7 and 11:
+ 7. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _calendar_, _largestUnit_, _options_).
+ 11. Let _result_ be ? NanosecondsToDays(_timeRemainderNs_, _intermediate_).
+ sec-temporal-nanosecondstodays step 11:
+ 11. 1. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _relativeTo_.[[Calendar]], *"day"*).
+ sec-temporal-differenceisodatetime steps 9–11:
+ 9. Let _dateLargestUnit_ be ! LargerOfTwoTemporalUnits(*"day"*, _largestUnit_).
+ 10. Let _untilOptions_ be ? MergeLargestUnitOption(_options_, _dateLargestUnit_).
+ 11. Let _dateDifference_ be ? CalendarDateUntil(_calendar_, _date1_, _date2_, _untilOptions_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkCalendarDateUntilLargestUnitSingular(
+ (calendar, largestUnit, index) => {
+ const one = new Temporal.Duration(...[...Array(index).fill(0), ...Array(10 - index).fill(1)]);
+ const two = new Temporal.Duration(...[...Array(index).fill(0), ...Array(10 - index).fill(2)]);
+ const relativeTo = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+ one.add(two, { relativeTo, largestUnit });
+ },
+ {
+ years: ["year"],
+ months: ["month"],
+ weeks: ["week"],
+ days: [],
+ hours: [],
+ minutes: [],
+ seconds: [],
+ milliseconds: [],
+ microseconds: [],
+ nanoseconds: []
+ }
+);
+
+TemporalHelpers.checkCalendarDateUntilLargestUnitSingular(
+ (calendar, largestUnit, index) => {
+ const one = new Temporal.Duration(...[...Array(index).fill(0), ...Array(10 - index).fill(1)]);
+ const two = new Temporal.Duration(...[...Array(index).fill(0), ...Array(10 - index).fill(2)]);
+ const relativeTo = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC", calendar);
+ one.add(two, { relativeTo, largestUnit });
+ },
+ {
+ years: ["year"],
+ months: ["month"],
+ weeks: ["week"],
+ days: [],
+ hours: [],
+ minutes: [],
+ seconds: [],
+ milliseconds: [],
+ microseconds: [],
+ nanoseconds: []
+ }
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/calendar-fields-iterable.js
new file mode 100644
index 0000000000..cba264cc9c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/calendar-fields-iterable.js
@@ -0,0 +1,35 @@
+// |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.duration.prototype.add
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.duration.prototype.add step 5:
+ 5. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_).
+ sec-temporal-torelativetemporalobject step 4.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+const duration1 = new Temporal.Duration(1);
+const duration2 = new Temporal.Duration(0, 12);
+duration1.add(duration2, { relativeTo: { year: 2000, month: 1, day: 1, calendar } });
+
+assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/calendar-temporal-object.js
new file mode 100644
index 0000000000..c3f778355d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/calendar-temporal-object.js
@@ -0,0 +1,30 @@
+// |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.duration.prototype.add
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.duration.prototype.add step 5:
+ 5. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_).
+ sec-temporal-torelativetemporalobject step 4.b:
+ b. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2b
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const duration1 = new Temporal.Duration(1);
+ const duration2 = new Temporal.Duration(0, 12);
+ duration1.add(duration2, { relativeTo: { year: 2000, month: 1, day: 1, calendar: temporalObject } });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..9006e385f9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/constructor-in-calendar-fields.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.duration.prototype.add
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const relativeTo = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar, timeZone: 'Europe/Paris' };
+const instance = new Temporal.Duration(1, 0, 0, 1);
+
+assert.throws(RangeError, () => instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/duplicate-calendar-fields.js
new file mode 100644
index 0000000000..b382a81332
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/duplicate-calendar-fields.js
@@ -0,0 +1,19 @@
+// |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.duration.prototype.add
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['day'], ['month'], ['monthCode'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const relativeTo = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar, timeZone: 'Europe/Paris' };
+ const instance = new Temporal.Duration(1, 0, 0, 1);
+
+ assert.throws(RangeError, () => instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..02cb7e78cd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/infinity-throws-rangeerror.js
@@ -0,0 +1,34 @@
+// |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.
+
+/*---
+description: Temporal.Duration.prototype.add throws a RangeError if any value in a property bag is Infinity
+esid: sec-temporal.duration.prototype.add
+features: [Temporal]
+---*/
+
+const fields = ['years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds', 'milliseconds', 'microseconds', 'nanoseconds'];
+
+const instance = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+const relativeTo = new Temporal.PlainDateTime(2000, 1, 1);
+
+fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.add({ [field]: Infinity }, { relativeTo }));
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return Infinity;
+ }
+};
+
+fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => instance.add({ [field]: obj }, { relativeTo }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/intermediate-instant-too-large-with-zoneddatetime.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/intermediate-instant-too-large-with-zoneddatetime.js
new file mode 100644
index 0000000000..e952b9a041
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/intermediate-instant-too-large-with-zoneddatetime.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.add
+description: >
+ AddZonedDateTime throws a RangeError when the intermediate instant is too large.
+features: [Temporal]
+---*/
+
+const plainDate = new Temporal.PlainDate(1970, 1, 1);
+const zonedDateTime = new Temporal.ZonedDateTime(0n, "UTC", "iso8601");
+
+var duration = Temporal.Duration.from({days: 1, seconds: Number.MAX_SAFE_INTEGER - 86400});
+
+var options = {relativeTo: zonedDateTime};
+
+assert.throws(RangeError, () => duration.add(duration, options));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/length.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/length.js
new file mode 100644
index 0000000000..c8162fbb3e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/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.duration.prototype.add
+description: Temporal.Duration.prototype.add.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.Duration.prototype.add, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/name.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/name.js
new file mode 100644
index 0000000000..79e706d0f3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/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.duration.prototype.add
+description: Temporal.Duration.prototype.add.name is "add".
+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.Duration.prototype.add, "name", {
+ value: "add",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/nanoseconds-is-number-max-safe-integer.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/nanoseconds-is-number-max-safe-integer.js
new file mode 100644
index 0000000000..54c8386d91
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/nanoseconds-is-number-max-safe-integer.js
@@ -0,0 +1,60 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.add
+description: >
+ AddDuration computes on exact mathematical number values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainDate = new Temporal.PlainDate(1970, 1, 1);
+const zonedDateTime = new Temporal.ZonedDateTime(0n, "UTC", "iso8601");
+
+// Largest temporal unit is "day".
+const duration1 = Temporal.Duration.from({nanoseconds: Number.MAX_SAFE_INTEGER});
+const duration2 = Temporal.Duration.from({nanoseconds: 2, days: 1});
+const nanos = BigInt(Number.MAX_SAFE_INTEGER) + 2n;
+
+TemporalHelpers.assertDuration(
+ duration1.add(duration2),
+ 0, 0, 0,
+ 1 + Number((nanos / (24n * 60n * 60n * 1_000_000_000n))),
+ Number((nanos / (60n * 60n * 1_000_000_000n)) % 24n),
+ Number((nanos / (60n * 1_000_000_000n)) % 60n),
+ Number((nanos / 1_000_000_000n) % 60n),
+ Number((nanos / 1_000_000n) % 1000n),
+ Number((nanos / 1000n) % 1000n),
+ Number(nanos % 1000n),
+ "duration1.add(duration2)"
+);
+
+TemporalHelpers.assertDuration(
+ duration1.add(duration2, {relativeTo: plainDate}),
+ 0, 0, 0,
+ 1 + Number((nanos / (24n * 60n * 60n * 1_000_000_000n))),
+ Number((nanos / (60n * 60n * 1_000_000_000n)) % 24n),
+ Number((nanos / (60n * 1_000_000_000n)) % 60n),
+ Number((nanos / 1_000_000_000n) % 60n),
+ Number((nanos / 1_000_000n) % 1000n),
+ Number((nanos / 1000n) % 1000n),
+ Number(nanos % 1000n),
+ "duration1.add(duration2, {relativeTo: plainDate})"
+);
+
+TemporalHelpers.assertDuration(
+ duration1.add(duration2, {relativeTo: zonedDateTime}),
+ 0, 0, 0,
+ 1 + Number((nanos / (24n * 60n * 60n * 1_000_000_000n))),
+ Number((nanos / (60n * 60n * 1_000_000_000n)) % 24n),
+ Number((nanos / (60n * 1_000_000_000n)) % 60n),
+ Number((nanos / 1_000_000_000n) % 60n),
+ Number((nanos / 1_000_000n) % 1000n),
+ Number((nanos / 1000n) % 1000n),
+ Number(nanos % 1000n),
+ "duration1.add(duration2, {relativeTo: zonedDateTime})"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/negative-infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/negative-infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..9fb65ac5e7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/negative-infinity-throws-rangeerror.js
@@ -0,0 +1,34 @@
+// |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.
+
+/*---
+description: Temporal.Duration.prototype.add throws a RangeError if any value in a property bag is -Infinity
+esid: sec-temporal.duration.prototype.add
+features: [Temporal]
+---*/
+
+const fields = ['years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds', 'milliseconds', 'microseconds', 'nanoseconds'];
+
+const instance = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+const relativeTo = new Temporal.PlainDateTime(2000, 1, 1);
+
+fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.add({ [field]: -Infinity }, { relativeTo }));
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return -Infinity;
+ }
+};
+
+fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => instance.add({ [field]: obj }, { relativeTo }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/non-integer-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/non-integer-throws-rangeerror.js
new file mode 100644
index 0000000000..58f40bec39
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/non-integer-throws-rangeerror.js
@@ -0,0 +1,29 @@
+// |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.duration.prototype.add
+description: A non-integer value for any recognized property in the property bag, throws a RangeError
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(0, 0, 0, 1, 2, 3, 4, 987, 654, 321);
+const fields = [
+ "years",
+ "months",
+ "weeks",
+ "days",
+ "hours",
+ "minutes",
+ "seconds",
+ "milliseconds",
+ "microseconds",
+ "nanoseconds",
+];
+fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.add({ [field]: 1.5 }));
+ assert.throws(RangeError, () => instance.add({ [field]: -1.5 }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/normalized-time-duration-to-days-loop-arbitrarily.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/normalized-time-duration-to-days-loop-arbitrarily.js
new file mode 100644
index 0000000000..62833e87c6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/normalized-time-duration-to-days-loop-arbitrarily.js
@@ -0,0 +1,78 @@
+// |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.duration.prototype.add
+description: >
+ NormalizedTimeDurationToDays can loop arbitrarily up to max safe integer
+info: |
+ NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ , precalculatedPlainDatetime ] )
+ ...
+ 21. Repeat, while done is false,
+ a. Let oneDayFarther be ? AddDaysToZonedDateTime(relativeResult.[[Instant]],
+ relativeResult.[[DateTime]], timeZoneRec, zonedRelativeTo.[[Calendar]], sign).
+ b. Set dayLengthNs to NormalizedTimeDurationFromEpochNanosecondsDifference(oneDayFarther.[[EpochNanoseconds]],
+ relativeResult.[[EpochNanoseconds]]).
+ c. Let oneDayLess be ? SubtractNormalizedTimeDuration(norm, dayLengthNs).
+ c. If NormalizedTimeDurationSign(oneDayLess) × sign ≥ 0, then
+ i. Set norm to oneDayLess.
+ ii. Set relativeResult to oneDayFarther.
+ iii. Set days to days + sign.
+ d. Else,
+ i. Set done to true.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calls = [];
+const duration = Temporal.Duration.from({ days: 1 });
+
+function createRelativeTo(count) {
+ const dayLengthNs = 86400000000000n;
+ const dayInstant = new Temporal.Instant(dayLengthNs);
+ const substitutions = [];
+ const timeZone = new Temporal.TimeZone("UTC");
+ // Return constant value for first _count_ calls
+ TemporalHelpers.substituteMethod(
+ timeZone,
+ "getPossibleInstantsFor",
+ substitutions
+ );
+ substitutions.length = count;
+ let i = 0;
+ for (i = 0; i < substitutions.length; i++) {
+ // (this value)
+ substitutions[i] = [dayInstant];
+ }
+ // Record calls in calls[]
+ TemporalHelpers.observeMethod(calls, timeZone, "getPossibleInstantsFor");
+ return new Temporal.ZonedDateTime(0n, timeZone);
+}
+
+let zdt = createRelativeTo(50);
+calls.splice(0); // Reset calls list after ZonedDateTime construction
+duration.add(duration, {
+ relativeTo: zdt,
+});
+assert.sameValue(
+ calls.length,
+ 50 + 1,
+ "Expected duration.add to call getPossibleInstantsFor correct number of times"
+);
+
+zdt = createRelativeTo(100);
+calls.splice(0); // Reset calls list after previous loop + ZonedDateTime construction
+duration.add(duration, {
+ relativeTo: zdt,
+});
+assert.sameValue(
+ calls.length,
+ 100 + 1,
+ "Expected duration.add to call getPossibleInstantsFor correct number of times"
+);
+
+zdt = createRelativeTo(107);
+assert.throws(RangeError, () => duration.add(duration, { relativeTo: zdt }), "107-2 days > 2⁵³ ns");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/not-a-constructor.js
new file mode 100644
index 0000000000..cfdd60c053
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/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.duration.prototype.add
+description: >
+ Temporal.Duration.prototype.add 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.Duration.prototype.add();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Duration.prototype.add), false,
+ "isConstructor(Temporal.Duration.prototype.add)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/options-object.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/options-object.js
new file mode 100644
index 0000000000..3684dd7baa
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/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.duration.prototype.add
+description: Empty or a function object may be used as options
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(0, 0, 0, 0, 1);
+
+const result1 = instance.add({ hours: 1 }, {});
+TemporalHelpers.assertDuration(
+ result1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0,
+ "options may be an empty plain object"
+);
+
+const result2 = instance.add({ hours: 1 }, () => {});
+TemporalHelpers.assertDuration(
+ result2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0,
+ "options may be a function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/options-undefined.js
new file mode 100644
index 0000000000..d79d382bbd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/options-undefined.js
@@ -0,0 +1,35 @@
+// |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.duration.prototype.add
+description: Verify that undefined options are handled correctly.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const duration1 = new Temporal.Duration(1);
+const duration2 = new Temporal.Duration(0, 12);
+const duration3 = new Temporal.Duration(0, 0, 0, 1);
+const duration4 = new Temporal.Duration(0, 0, 0, 0, 24);
+
+assert.throws(RangeError, () => duration1.add(duration2), "no options with years");
+TemporalHelpers.assertDuration(duration3.add(duration4),
+ 0, 0, 0, /* days = */ 2, 0, 0, 0, 0, 0, 0,
+ "no options with days");
+
+const optionValues = [
+ [undefined, "undefined"],
+ [{}, "plain object"],
+ [() => {}, "lambda"],
+];
+for (const [options, description] of optionValues) {
+ assert.throws(RangeError, () => duration1.add(duration2, options),
+ `options ${description} with years`);
+ TemporalHelpers.assertDuration(duration3.add(duration4, options),
+ 0, 0, 0, /* days = */ 2, 0, 0, 0, 0, 0, 0,
+ `options ${description} with days`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/options-wrong-type.js
new file mode 100644
index 0000000000..1b9e554588
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/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.duration.prototype.add
+description: TypeError thrown when options argument is a primitive
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const badOptions = [
+ null,
+ true,
+ "some string",
+ Symbol(),
+ 1,
+ 2n,
+];
+
+const instance = new Temporal.Duration(0, 0, 0, 0, 1);
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.add({ hours: 1 }, value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/order-of-operations.js
new file mode 100644
index 0000000000..a530ba55e9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/order-of-operations.js
@@ -0,0 +1,493 @@
+// |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.duration.prototype.add
+description: Properties on an object passed to add() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ // ToTemporalDurationRecord
+ "get fields.days",
+ "get fields.days.valueOf",
+ "call fields.days.valueOf",
+ "get fields.hours",
+ "get fields.hours.valueOf",
+ "call fields.hours.valueOf",
+ "get fields.microseconds",
+ "get fields.microseconds.valueOf",
+ "call fields.microseconds.valueOf",
+ "get fields.milliseconds",
+ "get fields.milliseconds.valueOf",
+ "call fields.milliseconds.valueOf",
+ "get fields.minutes",
+ "get fields.minutes.valueOf",
+ "call fields.minutes.valueOf",
+ "get fields.months",
+ "get fields.months.valueOf",
+ "call fields.months.valueOf",
+ "get fields.nanoseconds",
+ "get fields.nanoseconds.valueOf",
+ "call fields.nanoseconds.valueOf",
+ "get fields.seconds",
+ "get fields.seconds.valueOf",
+ "call fields.seconds.valueOf",
+ "get fields.weeks",
+ "get fields.weeks.valueOf",
+ "call fields.weeks.valueOf",
+ "get fields.years",
+ "get fields.years.valueOf",
+ "call fields.years.valueOf",
+ // ToRelativeTemporalObject
+ "get options.relativeTo",
+];
+const actual = [];
+
+const simpleFields = TemporalHelpers.propertyBagObserver(actual, {
+ years: 0,
+ months: 0,
+ weeks: 0,
+ days: 1,
+ hours: 1,
+ minutes: 1,
+ seconds: 1,
+ milliseconds: 1,
+ microseconds: 1,
+ nanoseconds: 1,
+}, "fields");
+
+function createOptionsObserver(relativeTo = undefined) {
+ return TemporalHelpers.propertyBagObserver(actual, { relativeTo }, "options");
+}
+
+// basic order of observable operations, without any calendar units:
+const simpleInstance = new Temporal.Duration(0, 0, 0, 1, 1, 1, 1, 1, 1, 1);
+simpleInstance.add(simpleFields, createOptionsObserver());
+assert.compareArray(actual, expected, "order of operations");
+actual.splice(0); // clear
+
+const expectedOpsForPlainRelativeTo = expected.concat([
+ // ToRelativeTemporalObject
+ "get options.relativeTo.calendar",
+ "has options.relativeTo.calendar.dateAdd",
+ "has options.relativeTo.calendar.dateFromFields",
+ "has options.relativeTo.calendar.dateUntil",
+ "has options.relativeTo.calendar.day",
+ "has options.relativeTo.calendar.dayOfWeek",
+ "has options.relativeTo.calendar.dayOfYear",
+ "has options.relativeTo.calendar.daysInMonth",
+ "has options.relativeTo.calendar.daysInWeek",
+ "has options.relativeTo.calendar.daysInYear",
+ "has options.relativeTo.calendar.fields",
+ "has options.relativeTo.calendar.id",
+ "has options.relativeTo.calendar.inLeapYear",
+ "has options.relativeTo.calendar.mergeFields",
+ "has options.relativeTo.calendar.month",
+ "has options.relativeTo.calendar.monthCode",
+ "has options.relativeTo.calendar.monthDayFromFields",
+ "has options.relativeTo.calendar.monthsInYear",
+ "has options.relativeTo.calendar.weekOfYear",
+ "has options.relativeTo.calendar.year",
+ "has options.relativeTo.calendar.yearMonthFromFields",
+ "has options.relativeTo.calendar.yearOfWeek",
+ "get options.relativeTo.calendar.dateFromFields",
+ "get options.relativeTo.calendar.fields",
+ "call options.relativeTo.calendar.fields",
+ // PrepareTemporalFields
+ "get options.relativeTo.day",
+ "get options.relativeTo.day.valueOf",
+ "call options.relativeTo.day.valueOf",
+ "get options.relativeTo.hour",
+ "get options.relativeTo.microsecond",
+ "get options.relativeTo.millisecond",
+ "get options.relativeTo.minute",
+ "get options.relativeTo.month",
+ "get options.relativeTo.month.valueOf",
+ "call options.relativeTo.month.valueOf",
+ "get options.relativeTo.monthCode",
+ "get options.relativeTo.monthCode.toString",
+ "call options.relativeTo.monthCode.toString",
+ "get options.relativeTo.nanosecond",
+ "get options.relativeTo.offset",
+ "get options.relativeTo.second",
+ "get options.relativeTo.timeZone",
+ "get options.relativeTo.year",
+ "get options.relativeTo.year.valueOf",
+ "call options.relativeTo.year.valueOf",
+ // InterpretTemporalDateTimeFields
+ "call options.relativeTo.calendar.dateFromFields",
+ // lookup in AddDurationToOrSubtractDurationFromDuration
+ "get options.relativeTo.calendar.dateAdd",
+ "get options.relativeTo.calendar.dateUntil",
+ // AddDuration
+ "call options.relativeTo.calendar.dateAdd",
+ "call options.relativeTo.calendar.dateAdd",
+ "call options.relativeTo.calendar.dateUntil",
+]);
+
+const instance = new Temporal.Duration(1, 2, 0, 4, 5, 6, 7, 987, 654, 321);
+
+const fields = TemporalHelpers.propertyBagObserver(actual, {
+ years: 1,
+ months: 1,
+ weeks: 1,
+ days: 1,
+ hours: 1,
+ minutes: 1,
+ seconds: 1,
+ milliseconds: 1,
+ microseconds: 1,
+ nanoseconds: 1,
+}, "fields");
+
+const plainRelativeTo = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2000,
+ month: 1,
+ monthCode: "M01",
+ day: 1,
+ calendar: TemporalHelpers.calendarObserver(actual, "options.relativeTo.calendar"),
+}, "options.relativeTo");
+
+instance.add(fields, createOptionsObserver(plainRelativeTo));
+assert.compareArray(actual, expectedOpsForPlainRelativeTo, "order of operations with PlainDate relativeTo");
+actual.splice(0); // clear
+
+const expectedOpsForPlainRelativeToNoCalendarOperations = [
+ // ToTemporalDurationRecord
+ "get fields.days",
+ "get fields.days.valueOf",
+ "call fields.days.valueOf",
+ "get fields.hours",
+ "get fields.hours.valueOf",
+ "call fields.hours.valueOf",
+ "get fields.microseconds",
+ "get fields.microseconds.valueOf",
+ "call fields.microseconds.valueOf",
+ "get fields.milliseconds",
+ "get fields.milliseconds.valueOf",
+ "call fields.milliseconds.valueOf",
+ "get fields.minutes",
+ "get fields.minutes.valueOf",
+ "call fields.minutes.valueOf",
+ "get fields.months",
+ "get fields.nanoseconds",
+ "get fields.nanoseconds.valueOf",
+ "call fields.nanoseconds.valueOf",
+ "get fields.seconds",
+ "get fields.seconds.valueOf",
+ "call fields.seconds.valueOf",
+ "get fields.weeks",
+ "get fields.years",
+ // ToRelativeTemporalObject
+ "get options.relativeTo",
+ "get options.relativeTo.calendar",
+ "has options.relativeTo.calendar.dateAdd",
+ "has options.relativeTo.calendar.dateFromFields",
+ "has options.relativeTo.calendar.dateUntil",
+ "has options.relativeTo.calendar.day",
+ "has options.relativeTo.calendar.dayOfWeek",
+ "has options.relativeTo.calendar.dayOfYear",
+ "has options.relativeTo.calendar.daysInMonth",
+ "has options.relativeTo.calendar.daysInWeek",
+ "has options.relativeTo.calendar.daysInYear",
+ "has options.relativeTo.calendar.fields",
+ "has options.relativeTo.calendar.id",
+ "has options.relativeTo.calendar.inLeapYear",
+ "has options.relativeTo.calendar.mergeFields",
+ "has options.relativeTo.calendar.month",
+ "has options.relativeTo.calendar.monthCode",
+ "has options.relativeTo.calendar.monthDayFromFields",
+ "has options.relativeTo.calendar.monthsInYear",
+ "has options.relativeTo.calendar.weekOfYear",
+ "has options.relativeTo.calendar.year",
+ "has options.relativeTo.calendar.yearMonthFromFields",
+ "has options.relativeTo.calendar.yearOfWeek",
+ "get options.relativeTo.calendar.dateFromFields",
+ "get options.relativeTo.calendar.fields",
+ "call options.relativeTo.calendar.fields",
+ // PrepareTemporalFields
+ "get options.relativeTo.day",
+ "get options.relativeTo.day.valueOf",
+ "call options.relativeTo.day.valueOf",
+ "get options.relativeTo.hour",
+ "get options.relativeTo.microsecond",
+ "get options.relativeTo.millisecond",
+ "get options.relativeTo.minute",
+ "get options.relativeTo.month",
+ "get options.relativeTo.month.valueOf",
+ "call options.relativeTo.month.valueOf",
+ "get options.relativeTo.monthCode",
+ "get options.relativeTo.monthCode.toString",
+ "call options.relativeTo.monthCode.toString",
+ "get options.relativeTo.nanosecond",
+ "get options.relativeTo.offset",
+ "get options.relativeTo.second",
+ "get options.relativeTo.timeZone",
+ "get options.relativeTo.year",
+ "get options.relativeTo.year.valueOf",
+ "call options.relativeTo.year.valueOf",
+ // InterpretTemporalDateTimeFields
+ "call options.relativeTo.calendar.dateFromFields",
+ // lookup in AddDurationToOrSubtractDurationFromDuration
+ "get options.relativeTo.calendar.dateAdd",
+ "get options.relativeTo.calendar.dateUntil",
+];
+
+const noCalendarInstance = new Temporal.Duration(0, 0, 0, 4, 5, 6, 7, 987, 654, 321);
+
+const noCalendarFields = TemporalHelpers.propertyBagObserver(actual, {
+ days: 1,
+ hours: 1,
+ minutes: 1,
+ seconds: 1,
+ milliseconds: 1,
+ microseconds: 1,
+ nanoseconds: 1,
+}, "fields");
+
+noCalendarInstance.add(noCalendarFields, createOptionsObserver(plainRelativeTo));
+assert.compareArray(actual, expectedOpsForPlainRelativeToNoCalendarOperations, "order of operations with PlainDate relativeTo and no calendar units");
+actual.splice(0); // clear
+
+const expectedOpsForZonedRelativeTo = expected.concat([
+ // ToRelativeTemporalObject
+ "get options.relativeTo.calendar",
+ "has options.relativeTo.calendar.dateAdd",
+ "has options.relativeTo.calendar.dateFromFields",
+ "has options.relativeTo.calendar.dateUntil",
+ "has options.relativeTo.calendar.day",
+ "has options.relativeTo.calendar.dayOfWeek",
+ "has options.relativeTo.calendar.dayOfYear",
+ "has options.relativeTo.calendar.daysInMonth",
+ "has options.relativeTo.calendar.daysInWeek",
+ "has options.relativeTo.calendar.daysInYear",
+ "has options.relativeTo.calendar.fields",
+ "has options.relativeTo.calendar.id",
+ "has options.relativeTo.calendar.inLeapYear",
+ "has options.relativeTo.calendar.mergeFields",
+ "has options.relativeTo.calendar.month",
+ "has options.relativeTo.calendar.monthCode",
+ "has options.relativeTo.calendar.monthDayFromFields",
+ "has options.relativeTo.calendar.monthsInYear",
+ "has options.relativeTo.calendar.weekOfYear",
+ "has options.relativeTo.calendar.year",
+ "has options.relativeTo.calendar.yearMonthFromFields",
+ "has options.relativeTo.calendar.yearOfWeek",
+ "get options.relativeTo.calendar.dateFromFields",
+ "get options.relativeTo.calendar.fields",
+ "call options.relativeTo.calendar.fields",
+ // PrepareTemporalFields
+ "get options.relativeTo.day",
+ "get options.relativeTo.day.valueOf",
+ "call options.relativeTo.day.valueOf",
+ "get options.relativeTo.hour",
+ "get options.relativeTo.hour.valueOf",
+ "call options.relativeTo.hour.valueOf",
+ "get options.relativeTo.microsecond",
+ "get options.relativeTo.microsecond.valueOf",
+ "call options.relativeTo.microsecond.valueOf",
+ "get options.relativeTo.millisecond",
+ "get options.relativeTo.millisecond.valueOf",
+ "call options.relativeTo.millisecond.valueOf",
+ "get options.relativeTo.minute",
+ "get options.relativeTo.minute.valueOf",
+ "call options.relativeTo.minute.valueOf",
+ "get options.relativeTo.month",
+ "get options.relativeTo.month.valueOf",
+ "call options.relativeTo.month.valueOf",
+ "get options.relativeTo.monthCode",
+ "get options.relativeTo.monthCode.toString",
+ "call options.relativeTo.monthCode.toString",
+ "get options.relativeTo.nanosecond",
+ "get options.relativeTo.nanosecond.valueOf",
+ "call options.relativeTo.nanosecond.valueOf",
+ "get options.relativeTo.offset",
+ "get options.relativeTo.offset.toString",
+ "call options.relativeTo.offset.toString",
+ "get options.relativeTo.second",
+ "get options.relativeTo.second.valueOf",
+ "call options.relativeTo.second.valueOf",
+ "get options.relativeTo.timeZone",
+ "get options.relativeTo.year",
+ "get options.relativeTo.year.valueOf",
+ "call options.relativeTo.year.valueOf",
+ // InterpretTemporalDateTimeFields
+ "call options.relativeTo.calendar.dateFromFields",
+ // ToRelativeTemporalObject again
+ "has options.relativeTo.timeZone.getOffsetNanosecondsFor",
+ "has options.relativeTo.timeZone.getPossibleInstantsFor",
+ "has options.relativeTo.timeZone.id",
+ // InterpretISODateTimeOffset
+ "get options.relativeTo.timeZone.getOffsetNanosecondsFor",
+ "get options.relativeTo.timeZone.getPossibleInstantsFor",
+ "call options.relativeTo.timeZone.getPossibleInstantsFor",
+ "call options.relativeTo.timeZone.getOffsetNanosecondsFor",
+ // lookup in AddDurationToOrSubtractDurationFromDuration
+ "get options.relativeTo.calendar.dateAdd",
+ "get options.relativeTo.calendar.dateUntil",
+ // AddDuration
+ "call options.relativeTo.timeZone.getOffsetNanosecondsFor",
+ // AddDuration → AddZonedDateTime 1
+ "call options.relativeTo.calendar.dateAdd",
+ "call options.relativeTo.timeZone.getPossibleInstantsFor",
+ // AddDuration → AddZonedDateTime 2
+ "call options.relativeTo.timeZone.getOffsetNanosecondsFor",
+ "call options.relativeTo.calendar.dateAdd",
+ "call options.relativeTo.timeZone.getPossibleInstantsFor",
+ // AddDuration → DifferenceZonedDateTime
+ "call options.relativeTo.timeZone.getOffsetNanosecondsFor",
+ // AddDuration → DifferenceZonedDateTime → DifferenceISODateTime
+ "call options.relativeTo.calendar.dateUntil",
+ // AddDuration → DifferenceZonedDateTime → AddZonedDateTime
+ "call options.relativeTo.calendar.dateAdd",
+ "call options.relativeTo.timeZone.getPossibleInstantsFor",
+ // AddDuration → DifferenceZonedDateTime → NanosecondsToDays
+ "call options.relativeTo.timeZone.getOffsetNanosecondsFor",
+ "call options.relativeTo.timeZone.getOffsetNanosecondsFor",
+ // AddDuration → DifferenceZonedDateTime → NanosecondsToDays → AddZonedDateTime 1
+ "call options.relativeTo.timeZone.getPossibleInstantsFor",
+ // AddDuration → DifferenceZonedDateTime → NanosecondsToDays → AddZonedDateTime 2
+ "call options.relativeTo.timeZone.getPossibleInstantsFor",
+]);
+
+const zonedRelativeTo = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2000,
+ month: 1,
+ monthCode: "M01",
+ day: 1,
+ hour: 0,
+ minute: 0,
+ second: 0,
+ millisecond: 0,
+ microsecond: 0,
+ nanosecond: 0,
+ offset: "+00:00",
+ calendar: TemporalHelpers.calendarObserver(actual, "options.relativeTo.calendar"),
+ timeZone: TemporalHelpers.timeZoneObserver(actual, "options.relativeTo.timeZone"),
+}, "options.relativeTo");
+
+instance.add(fields, createOptionsObserver(zonedRelativeTo));
+assert.compareArray(actual, expectedOpsForZonedRelativeTo, "order of operations with ZonedDateTime relativeTo");
+actual.splice(0); // clear
+
+const expectedOpsForZonedRelativeToNoDaysOperations = [
+ // ToTemporalDurationRecord
+ "get fields.days",
+ "get fields.hours",
+ "get fields.hours.valueOf",
+ "call fields.hours.valueOf",
+ "get fields.microseconds",
+ "get fields.microseconds.valueOf",
+ "call fields.microseconds.valueOf",
+ "get fields.milliseconds",
+ "get fields.milliseconds.valueOf",
+ "call fields.milliseconds.valueOf",
+ "get fields.minutes",
+ "get fields.minutes.valueOf",
+ "call fields.minutes.valueOf",
+ "get fields.months",
+ "get fields.nanoseconds",
+ "get fields.nanoseconds.valueOf",
+ "call fields.nanoseconds.valueOf",
+ "get fields.seconds",
+ "get fields.seconds.valueOf",
+ "call fields.seconds.valueOf",
+ "get fields.weeks",
+ "get fields.years",
+ // ToRelativeTemporalObject
+ "get options.relativeTo",
+ "get options.relativeTo.calendar",
+ "has options.relativeTo.calendar.dateAdd",
+ "has options.relativeTo.calendar.dateFromFields",
+ "has options.relativeTo.calendar.dateUntil",
+ "has options.relativeTo.calendar.day",
+ "has options.relativeTo.calendar.dayOfWeek",
+ "has options.relativeTo.calendar.dayOfYear",
+ "has options.relativeTo.calendar.daysInMonth",
+ "has options.relativeTo.calendar.daysInWeek",
+ "has options.relativeTo.calendar.daysInYear",
+ "has options.relativeTo.calendar.fields",
+ "has options.relativeTo.calendar.id",
+ "has options.relativeTo.calendar.inLeapYear",
+ "has options.relativeTo.calendar.mergeFields",
+ "has options.relativeTo.calendar.month",
+ "has options.relativeTo.calendar.monthCode",
+ "has options.relativeTo.calendar.monthDayFromFields",
+ "has options.relativeTo.calendar.monthsInYear",
+ "has options.relativeTo.calendar.weekOfYear",
+ "has options.relativeTo.calendar.year",
+ "has options.relativeTo.calendar.yearMonthFromFields",
+ "has options.relativeTo.calendar.yearOfWeek",
+ "get options.relativeTo.calendar.dateFromFields",
+ "get options.relativeTo.calendar.fields",
+ "call options.relativeTo.calendar.fields",
+ // PrepareTemporalFields
+ "get options.relativeTo.day",
+ "get options.relativeTo.day.valueOf",
+ "call options.relativeTo.day.valueOf",
+ "get options.relativeTo.hour",
+ "get options.relativeTo.hour.valueOf",
+ "call options.relativeTo.hour.valueOf",
+ "get options.relativeTo.microsecond",
+ "get options.relativeTo.microsecond.valueOf",
+ "call options.relativeTo.microsecond.valueOf",
+ "get options.relativeTo.millisecond",
+ "get options.relativeTo.millisecond.valueOf",
+ "call options.relativeTo.millisecond.valueOf",
+ "get options.relativeTo.minute",
+ "get options.relativeTo.minute.valueOf",
+ "call options.relativeTo.minute.valueOf",
+ "get options.relativeTo.month",
+ "get options.relativeTo.month.valueOf",
+ "call options.relativeTo.month.valueOf",
+ "get options.relativeTo.monthCode",
+ "get options.relativeTo.monthCode.toString",
+ "call options.relativeTo.monthCode.toString",
+ "get options.relativeTo.nanosecond",
+ "get options.relativeTo.nanosecond.valueOf",
+ "call options.relativeTo.nanosecond.valueOf",
+ "get options.relativeTo.offset",
+ "get options.relativeTo.offset.toString",
+ "call options.relativeTo.offset.toString",
+ "get options.relativeTo.second",
+ "get options.relativeTo.second.valueOf",
+ "call options.relativeTo.second.valueOf",
+ "get options.relativeTo.timeZone",
+ "get options.relativeTo.year",
+ "get options.relativeTo.year.valueOf",
+ "call options.relativeTo.year.valueOf",
+ // InterpretTemporalDateTimeFields
+ "call options.relativeTo.calendar.dateFromFields",
+ // ToRelativeTemporalObject again
+ "has options.relativeTo.timeZone.getOffsetNanosecondsFor",
+ "has options.relativeTo.timeZone.getPossibleInstantsFor",
+ "has options.relativeTo.timeZone.id",
+ // InterpretISODateTimeOffset
+ "get options.relativeTo.timeZone.getOffsetNanosecondsFor",
+ "get options.relativeTo.timeZone.getPossibleInstantsFor",
+ "call options.relativeTo.timeZone.getPossibleInstantsFor",
+ "call options.relativeTo.timeZone.getOffsetNanosecondsFor",
+ // lookup in AddDurationToOrSubtractDurationFromDuration
+ "get options.relativeTo.calendar.dateAdd",
+ "get options.relativeTo.calendar.dateUntil",
+];
+
+const noDaysInstance = new Temporal.Duration(0, 0, 0, 0, 5, 6, 7, 987, 654, 321);
+
+const noDaysFields = TemporalHelpers.propertyBagObserver(actual, {
+ hours: 1,
+ minutes: 1,
+ seconds: 1,
+ milliseconds: 1,
+ microseconds: 1,
+ nanoseconds: 1,
+}, "fields");
+
+noDaysInstance.add(noDaysFields, createOptionsObserver(zonedRelativeTo));
+assert.compareArray(actual, expectedOpsForZonedRelativeToNoDaysOperations, "order of operations with ZonedDateTime relativeTo and no units above days");
+actual.splice(0); // clear
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/precision-exact-mathematical-values.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/precision-exact-mathematical-values.js
new file mode 100644
index 0000000000..95eaf3a618
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/precision-exact-mathematical-values.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.add
+description: >
+ AddDuration computes on exact mathematical number values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+// Largest temporal unit is "microsecond".
+let duration1 = Temporal.Duration.from({microseconds: Number.MAX_SAFE_INTEGER + 1, nanoseconds: 0});
+let duration2 = Temporal.Duration.from({microseconds: 1, nanoseconds: 1000});
+
+TemporalHelpers.assertDuration(
+ duration1.add(duration2),
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 9007199254740994,
+ 0,
+ "duration1.add(duration2)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/precision-no-floating-point-loss.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/precision-no-floating-point-loss.js
new file mode 100644
index 0000000000..9a6f46f50f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/precision-no-floating-point-loss.js
@@ -0,0 +1,52 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.add
+description: >
+ BalanceDuration computes floating-point values that are the same as exact math
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainDate = new Temporal.PlainDate(1970, 1, 1);
+const zonedDateTime = new Temporal.ZonedDateTime(0n, "UTC", "iso8601");
+
+// Largest temporal unit is "day".
+const duration1 = Temporal.Duration.from({seconds: 4503599627370495, nanoseconds: 499_999_999});
+const duration2 = Temporal.Duration.from({seconds: 4503599627370495 - 86400, nanoseconds: 499_999_999, days: 1});
+const nanos = 4503599627370495_499_999_999n * 2n;
+
+TemporalHelpers.assertDuration(
+ duration1.add(duration2),
+ 0, 0, 0,
+ Number((nanos / (24n * 60n * 60n * 1_000_000_000n))),
+ Number((nanos / (60n * 60n * 1_000_000_000n)) % 24n),
+ Number((nanos / (60n * 1_000_000_000n)) % 60n),
+ Number((nanos / 1_000_000_000n) % 60n),
+ Number((nanos / 1_000_000n) % 1000n),
+ Number((nanos / 1000n) % 1000n),
+ Number(nanos % 1000n),
+ "duration1.add(duration2)"
+);
+
+TemporalHelpers.assertDuration(
+ duration1.add(duration2, {relativeTo: plainDate}),
+ 0, 0, 0,
+ Number((nanos / (24n * 60n * 60n * 1_000_000_000n))),
+ Number((nanos / (60n * 60n * 1_000_000_000n)) % 24n),
+ Number((nanos / (60n * 1_000_000_000n)) % 60n),
+ Number((nanos / 1_000_000_000n) % 60n),
+ Number((nanos / 1_000_000n) % 1000n),
+ Number((nanos / 1000n) % 1000n),
+ Number(nanos % 1000n),
+ "duration1.add(duration2, {relativeTo: plainDate})"
+);
+
+// Throws a RangeError because the intermediate instant is too large.
+assert.throws(RangeError, () => {
+ duration1.add(duration2, {relativeTo: zonedDateTime});
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/prop-desc.js
new file mode 100644
index 0000000000..d03c1b0316
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/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.duration.prototype.add
+description: The "add" property of Temporal.Duration.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Duration.prototype.add,
+ "function",
+ "`typeof Duration.prototype.add` is `function`"
+);
+
+verifyProperty(Temporal.Duration.prototype, "add", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/proto-in-calendar-fields.js
new file mode 100644
index 0000000000..ace6c5df23
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/proto-in-calendar-fields.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.duration.prototype.add
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const relativeTo = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar, timeZone: 'Europe/Paris' };
+const instance = new Temporal.Duration(1, 0, 0, 1);
+
+assert.throws(RangeError, () => instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/read-time-fields-before-datefromfields.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/read-time-fields-before-datefromfields.js
new file mode 100644
index 0000000000..0d49491f52
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/read-time-fields-before-datefromfields.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.duration.prototype.add
+description: The time fields are read from the object before being passed to dateFromFields().
+info: |
+ sec-temporal.duration.prototype.add step 5:
+ 5. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_).
+ sec-temporal-torelativetemporalobject step 4.g:
+ g. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+ sec-temporal-interprettemporaldatetimefields steps 1–2:
+ 1. Let _timeResult_ be ? ToTemporalTimeRecord(_fields_).
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarMakeInvalidGettersTime();
+const duration1 = new Temporal.Duration(1);
+const duration2 = new Temporal.Duration(0, 12);
+duration1.add(duration2, { relativeTo: { year: 2000, month: 1, day: 1, calendar } });
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..2834eff68a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-infinity-throws-rangeerror.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.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.duration.prototype.add
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+const base = { year: 2000, month: 5, day: 2, hour: 15, minute: 30, second: 45, millisecond: 987, microsecond: 654, nanosecond: 321 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.add(instance, { relativeTo: { ...base, [prop]: inf } }), `${prop} property cannot be ${inf} in relativeTo`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.add(instance, { relativeTo: { ...base, [prop]: obj } }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-leap-second.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-leap-second.js
new file mode 100644
index 0000000000..502ae3e1c6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-leap-second.js
@@ -0,0 +1,46 @@
+// |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.duration.prototype.add
+description: Leap second is constrained in both an ISO string and a property bag
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(1, 0, 0, 1);
+
+let relativeTo = "2016-12-31T23:59:60";
+const result1 = instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo });
+TemporalHelpers.assertDuration(
+ result1,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "leap second is a valid ISO string for PlainDate relativeTo"
+);
+
+relativeTo = "2016-12-31T23:59:60+00:00[UTC]";
+const result2 = instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo });
+TemporalHelpers.assertDuration(
+ result2,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "leap second is a valid ISO string for ZonedDateTime relativeTo"
+);
+
+relativeTo = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+const result3 = instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo });
+TemporalHelpers.assertDuration(
+ result3,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "second: 60 is valid in a property bag for PlainDate relativeTo"
+);
+
+relativeTo = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60, timeZone: "UTC" };
+const result4 = instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo });
+TemporalHelpers.assertDuration(
+ result4,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "second: 60 is valid in a property bag for ZonedDateTime relativeTo"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-month.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-month.js
new file mode 100644
index 0000000000..aacb771697
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-month.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.duration.prototype.add
+description: relativeTo with months.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const oneMonth = new Temporal.Duration(0, 1);
+const days30 = new Temporal.Duration(0, 0, 0, 30);
+TemporalHelpers.assertDuration(oneMonth.add(days30, { relativeTo: Temporal.PlainDate.from('2018-01-01') }),
+ 0, 2, 0, 2, 0, 0, 0, 0, 0, 0, "January");
+TemporalHelpers.assertDuration(oneMonth.add(days30, { relativeTo: Temporal.PlainDate.from('2018-02-01') }),
+ 0, 1, 0, 30, 0, 0, 0, 0, 0, 0, "February");
+TemporalHelpers.assertDuration(oneMonth.add(days30, { relativeTo: Temporal.PlainDate.from('2018-03-01') }),
+ 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, "March");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-number.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-number.js
new file mode 100644
index 0000000000..68d0daf24f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-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.duration.prototype.add
+description: A number cannot be used in place of a relativeTo
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(1, 0, 0, 1);
+
+const numbers = [
+ 1,
+ -20191101,
+ 20191101,
+ 1234567890,
+];
+
+for (const relativeTo of numbers) {
+ assert.throws(
+ TypeError,
+ () => instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }),
+ `A number (${relativeTo}) is not a valid ISO string for relativeTo`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-order.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-order.js
new file mode 100644
index 0000000000..5b45e61706
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-order.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.duration.prototype.add
+description: relativeTo with years.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const d1 = new Temporal.Duration(0, 1, 0, 0);
+const d2 = new Temporal.Duration(0, 0, 0, 30);
+const relativeTo = new Temporal.PlainDate(2000, 1, 1);
+TemporalHelpers.assertDuration(d1.add(d2, { relativeTo }),
+ 0, 2, 0, 1, 0, 0, 0, 0, 0, 0,
+ "first this is resolved against relativeTo, then the argument against relativeTo + this");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-ambiguous-wall-clock-time.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-ambiguous-wall-clock-time.js
new file mode 100644
index 0000000000..22403087eb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-ambiguous-wall-clock-time.js
@@ -0,0 +1,92 @@
+// |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.duration.prototype.add
+description: >
+ Correct time zone calls are made when converting a ZonedDateTime-like
+ relativeTo property bag denoting an ambiguous wall-clock time
+includes: [temporalHelpers.js, compareArray.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+
+const dstTimeZone = TemporalHelpers.springForwardFallBackTimeZone();
+const dstTimeZoneObserver = TemporalHelpers.timeZoneObserver(actual, "timeZone", {
+ getOffsetNanosecondsFor: dstTimeZone.getOffsetNanosecondsFor.bind(dstTimeZone),
+ getPossibleInstantsFor: dstTimeZone.getPossibleInstantsFor.bind(dstTimeZone),
+});
+const calendar = TemporalHelpers.calendarObserver(actual, "calendar");
+
+const instance = new Temporal.Duration(1, 0, 0, 1);
+
+let relativeTo = { year: 2000, month: 4, day: 2, hour: 2, minute: 30, timeZone: dstTimeZoneObserver, calendar };
+instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo });
+
+const expected = [
+ // GetTemporalCalendarSlotValueWithISODefault
+ "has calendar.dateAdd",
+ "has calendar.dateFromFields",
+ "has calendar.dateUntil",
+ "has calendar.day",
+ "has calendar.dayOfWeek",
+ "has calendar.dayOfYear",
+ "has calendar.daysInMonth",
+ "has calendar.daysInWeek",
+ "has calendar.daysInYear",
+ "has calendar.fields",
+ "has calendar.id",
+ "has calendar.inLeapYear",
+ "has calendar.mergeFields",
+ "has calendar.month",
+ "has calendar.monthCode",
+ "has calendar.monthDayFromFields",
+ "has calendar.monthsInYear",
+ "has calendar.weekOfYear",
+ "has calendar.year",
+ "has calendar.yearMonthFromFields",
+ "has calendar.yearOfWeek",
+ // lookup
+ "get calendar.dateFromFields",
+ "get calendar.fields",
+ // CalendarFields
+ "call calendar.fields",
+ // InterpretTemporalDateTimeFields
+ "call calendar.dateFromFields",
+ // ToTemporalTimeZoneSlotValue
+ "has timeZone.getOffsetNanosecondsFor",
+ "has timeZone.getPossibleInstantsFor",
+ "has timeZone.id",
+ // lookup
+ "get timeZone.getOffsetNanosecondsFor",
+ "get timeZone.getPossibleInstantsFor",
+ // InterpretISODateTimeOffset
+ "call timeZone.getPossibleInstantsFor",
+];
+
+const expectedSpringForward = expected.concat([
+ // DisambiguatePossibleInstants
+ "call timeZone.getOffsetNanosecondsFor",
+ "call timeZone.getOffsetNanosecondsFor",
+ "call timeZone.getPossibleInstantsFor",
+]);
+assert.compareArray(
+ actual.slice(0, expectedSpringForward.length), // ignore operations after ToRelativeTemporalObject
+ expectedSpringForward,
+ "order of operations converting property bag at skipped wall-clock time"
+);
+actual.splice(0); // clear
+
+relativeTo = { year: 2000, month: 10, day: 29, hour: 1, minute: 30, timeZone: dstTimeZoneObserver, calendar };
+instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo });
+
+assert.compareArray(
+ actual.slice(0, expected.length), // ignore operations after ToRelativeTemporalObject
+ expected,
+ "order of operations converting property bag at repeated wall-clock time"
+);
+actual.splice(0); // clear
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..36a32857ed
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-builtin-calendar-no-array-iteration.js
@@ -0,0 +1,25 @@
+// |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.duration.prototype.add
+description: >
+ Calling the method with a relativeTo property bag with a builtin calendar
+ causes no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const timeZone = "UTC";
+const instance = new Temporal.Duration(1, 0, 0, 1);
+const relativeTo = { year: 2000, month: 5, day: 2, hour: 21, minute: 43, second: 5, timeZone, calendar: "iso8601" };
+instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo });
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-calendar-datefromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..a57eb2358e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-calendar-datefromfields-called-with-null-prototype-fields.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.duration.prototype.add
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.Duration(1, 0, 0, 1);
+const relativeTo = { year: 2000, month: 5, day: 2, calendar };
+instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo });
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-calendar-number.js
new file mode 100644
index 0000000000..524a67a9aa
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-calendar-number.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.duration.prototype.add
+description: A number as calendar in relativeTo property bag is invalid
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(1, 0, 0, 1);
+
+const numbers = [
+ 1,
+ 19970327,
+ -19970327,
+ 1234567890,
+];
+
+for (const calendar of numbers) {
+ const relativeTo = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ TypeError,
+ () => instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }),
+ `A number (${calendar}) is not a valid ISO string for relativeTo.calendar`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-calendar-string.js
new file mode 100644
index 0000000000..b02db2d0fa
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-calendar-string.js
@@ -0,0 +1,29 @@
+// |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.duration.prototype.add
+description: >
+ Builtin dateFromFields method is not observably called when the property bag
+ has a string-valued calendar property
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dateFromFieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dateFromFields");
+Object.defineProperty(Temporal.Calendar.prototype, "dateFromFields", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("dateFromFields should not be looked up");
+ },
+});
+
+const instance = new Temporal.Duration(1, 0, 0, 1);
+const relativeTo = { year: 2000, month: 5, day: 2, calendar: "iso8601" };
+instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo });
+
+Object.defineProperty(Temporal.Calendar.prototype, "dateFromFields", dateFromFieldsOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-calendar-wrong-type.js
new file mode 100644
index 0000000000..a9469e120c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-calendar-wrong-type.js
@@ -0,0 +1,48 @@
+// |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.duration.prototype.add
+description: >
+ Appropriate error thrown when relativeTo.calendar cannot be converted to a
+ calendar object or string
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.Duration(1, 0, 0, 1);
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [calendar, description] of primitiveTests) {
+ const relativeTo = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ typeof calendar === 'string' ? RangeError : TypeError,
+ () => instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.PlainDate, "Temporal.PlainDate, object"],
+ [Temporal.PlainDate.prototype, "Temporal.PlainDate.prototype, object"],
+ [Temporal.ZonedDateTime, "Temporal.ZonedDateTime, object"],
+ [Temporal.ZonedDateTime.prototype, "Temporal.ZonedDateTime.prototype, object"],
+];
+
+for (const [calendar, description] of typeErrorTests) {
+ const relativeTo = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(TypeError, () => instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }), `${description} is not a valid property bag and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-getpossibleinstantsfor-called-with-iso8601-calendar.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-getpossibleinstantsfor-called-with-iso8601-calendar.js
new file mode 100644
index 0000000000..ee5dff3253
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-getpossibleinstantsfor-called-with-iso8601-calendar.js
@@ -0,0 +1,59 @@
+// |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.duration.prototype.add
+description: >
+ Time zone's getPossibleInstantsFor is called with a PlainDateTime with the
+ built-in ISO 8601 calendar
+features: [Temporal]
+info: |
+ DisambiguatePossibleInstants:
+ 2. Let _n_ be _possibleInstants_'s length.
+ ...
+ 5. Assert: _n_ = 0.
+ ...
+ 19. If _disambiguation_ is *"earlier"*, then
+ ...
+ c. Let _earlierDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*).
+ d. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _earlierDateTime_).
+ ...
+ 20. Assert: _disambiguation_ is *"compatible"* or *"later"*.
+ ...
+ 23. Let _laterDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*).
+ 24. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _laterDateTime_).
+---*/
+
+class SkippedDateTime extends Temporal.TimeZone {
+ constructor() {
+ super("UTC");
+ this.calls = 0;
+ }
+
+ getPossibleInstantsFor(dateTime) {
+ // Calls occur in pairs. For the first one return no possible instants so
+ // that DisambiguatePossibleInstants will call it again
+ if (this.calls++ % 2 == 0) {
+ return [];
+ }
+
+ assert.sameValue(
+ dateTime.getISOFields().calendar,
+ "iso8601",
+ "getPossibleInstantsFor called with dateTime with built-in ISO 8601 calendar"
+ );
+ return super.getPossibleInstantsFor(dateTime);
+ }
+}
+
+const nonBuiltinISOCalendar = new Temporal.Calendar("iso8601");
+const timeZone = new SkippedDateTime();
+const relativeTo = { year: 2000, month: 5, day: 2, timeZone, calendar: nonBuiltinISOCalendar };
+
+const instance = new Temporal.Duration(1, 0, 0, 1);
+instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo });
+
+assert.sameValue(timeZone.calls, 6, "getPossibleInstantsFor should have been called 6 times");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-invalid-offset-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-invalid-offset-string.js
new file mode 100644
index 0000000000..c6f575879c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-invalid-offset-string.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.duration.prototype.add
+description: relativeTo property bag with offset property is rejected if offset is in the wrong format
+features: [Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.Duration(1, 0, 0, 1);
+
+const badOffsets = [
+ "00:00", // missing sign
+ "+0", // too short
+ "-000:00", // too long
+ 0, // must be a string
+ null, // must be a string
+ true, // must be a string
+ 1000n, // must be a string
+];
+badOffsets.forEach((offset) => {
+ const relativeTo = { year: 2021, month: 10, day: 28, offset, timeZone };
+ assert.throws(
+ typeof(offset) === 'string' ? RangeError : TypeError,
+ () => instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }),
+ `"${offset} is not a valid offset string`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-no-time-units.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-no-time-units.js
new file mode 100644
index 0000000000..01757425e6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-no-time-units.js
@@ -0,0 +1,18 @@
+// |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.duration.prototype.add
+description: Missing time units in relativeTo property bag default to 0
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(1, 0, 0, 1);
+
+let relativeTo = { year: 2000, month: 1, day: 1 };
+const result = instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo });
+TemporalHelpers.assertDuration(result, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "missing time units default to 0");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..0bc42adcab
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-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.duration.prototype.add
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+ const other = new Temporal.Duration(2);
+ assert.throws(RangeError, () => duration.add(other, { relativeTo: { year: 2000, month: 5, day: 2, hour: 12, timeZone } }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..4396087829
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-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.duration.prototype.add
+description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable
+features: [BigInt, Symbol, Temporal, arrow-function]
+---*/
+
+[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => {
+ const timeZone = new Temporal.TimeZone("UTC");
+ const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+ const other = new Temporal.Duration(2);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => duration.add(other, { relativeTo: { year: 2000, month: 5, day: 2, hour: 12, timeZone } }),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..b25e868afa
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-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.duration.prototype.add
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+ const other = new Temporal.Duration(2);
+ assert.throws(RangeError, () => duration.add(other, { relativeTo: { year: 2000, month: 5, day: 2, hour: 12, timeZone } }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..f8f652b459
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-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.duration.prototype.add
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+ const other = new Temporal.Duration(2);
+ assert.throws(TypeError, () => duration.add(other, { relativeTo: { year: 2000, month: 5, day: 2, hour: 12, timeZone } }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-string-datetime.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-string-datetime.js
new file mode 100644
index 0000000000..c5e27fe49b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-string-datetime.js
@@ -0,0 +1,66 @@
+// |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.duration.prototype.add
+description: Conversion of ISO date-time strings to Temporal.TimeZone instances
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(1);
+
+let timeZone = "2021-08-19T17:30";
+assert.throws(RangeError, () => instance.add(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } }), "bare date-time string is not a time zone");
+
+[
+ "2021-08-19T17:30-07:00:01",
+ "2021-08-19T17:30-07:00:00",
+ "2021-08-19T17:30-07:00:00.1",
+ "2021-08-19T17:30-07:00:00.0",
+ "2021-08-19T17:30-07:00:00.01",
+ "2021-08-19T17:30-07:00:00.00",
+ "2021-08-19T17:30-07:00:00.001",
+ "2021-08-19T17:30-07:00:00.000",
+ "2021-08-19T17:30-07:00:00.0001",
+ "2021-08-19T17:30-07:00:00.0000",
+ "2021-08-19T17:30-07:00:00.00001",
+ "2021-08-19T17:30-07:00:00.00000",
+ "2021-08-19T17:30-07:00:00.000001",
+ "2021-08-19T17:30-07:00:00.000000",
+ "2021-08-19T17:30-07:00:00.0000001",
+ "2021-08-19T17:30-07:00:00.0000000",
+ "2021-08-19T17:30-07:00:00.00000001",
+ "2021-08-19T17:30-07:00:00.00000000",
+ "2021-08-19T17:30-07:00:00.000000001",
+ "2021-08-19T17:30-07:00:00.000000000",
+].forEach((timeZone) => {
+ assert.throws(
+ RangeError,
+ () => instance.add(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } }),
+ `ISO string ${timeZone} with a sub-minute offset is not a valid time zone`
+ );
+});
+
+// The following are all valid strings so should not throw:
+
+[
+ "2021-08-19T17:30Z",
+ "2021-08-19T1730Z",
+ "2021-08-19T17:30-07:00",
+ "2021-08-19T1730-07:00",
+ "2021-08-19T17:30-0700",
+ "2021-08-19T1730-0700",
+ "2021-08-19T17:30[UTC]",
+ "2021-08-19T1730[UTC]",
+ "2021-08-19T17:30Z[UTC]",
+ "2021-08-19T1730Z[UTC]",
+ "2021-08-19T17:30-07:00[UTC]",
+ "2021-08-19T1730-07:00[UTC]",
+ "2021-08-19T17:30-0700[UTC]",
+ "2021-08-19T1730-0700[UTC]",
+].forEach((timeZone) => {
+ instance.add(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-string-leap-second.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-string-leap-second.js
new file mode 100644
index 0000000000..930e32aca6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-string-leap-second.js
@@ -0,0 +1,22 @@
+// |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.duration.prototype.add
+description: Leap second is a valid ISO string for TimeZone
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(1);
+let timeZone = "2016-12-31T23:59:60+00:00[UTC]";
+
+// A string with a leap second is a valid ISO string, so the following
+// operation should not throw
+
+instance.add(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } });
+
+timeZone = "2021-08-19T17:30:45.123456789+23:59[+23:59:60]";
+assert.throws(RangeError, () => instance.add(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } }), "leap second in time zone name not valid");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-string-year-zero.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-string-year-zero.js
new file mode 100644
index 0000000000..5ef8aeda19
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-string-year-zero.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.duration.prototype.add
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+const instance = new Temporal.Duration(1);
+invalidStrings.forEach((timeZone) => {
+ assert.throws(
+ RangeError,
+ () => instance.add(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } }),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-string.js
new file mode 100644
index 0000000000..876b2496d3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-string.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.duration.prototype.add
+description: Time zone IDs are valid input for a time zone
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getPossibleInstantsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getPossibleInstantsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be looked up");
+ },
+});
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+
+const instance = new Temporal.Duration(1);
+
+// The following are all valid strings so should not throw:
+
+["UTC", "+01:00"].forEach((timeZone) => {
+ instance.add(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } });
+});
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", getPossibleInstantsForOriginal);
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-wrong-type.js
new file mode 100644
index 0000000000..dc34502fdc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-wrong-type.js
@@ -0,0 +1,42 @@
+// |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.duration.prototype.add
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or object for TimeZone
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.Duration(1);
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [19761118, "number that would convert to a valid ISO string in other contexts"],
+ [1n, "bigint"],
+];
+
+for (const [timeZone, description] of primitiveTests) {
+ assert.throws(
+ typeof timeZone === 'string' ? RangeError : TypeError,
+ () => instance.add(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } }),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "object not implementing time zone protocol"],
+ [new Temporal.Calendar("iso8601"), "calendar instance"],
+];
+
+for (const [timeZone, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.add(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } }), `${description} is not a valid object and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-required.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-required.js
new file mode 100644
index 0000000000..605328223a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-required.js
@@ -0,0 +1,36 @@
+// |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.duration.prototype.add
+description: relativeTo is required if the largest unit is at least weeks.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const d = Temporal.Duration.from({ hours: 1 });
+const dy = Temporal.Duration.from({ years: 1 });
+const dm = Temporal.Duration.from({ months: 1 });
+const dw = Temporal.Duration.from({ weeks: 1 });
+assert.throws(RangeError, () => d.add(dy));
+assert.throws(RangeError, () => d.add(dm));
+assert.throws(RangeError, () => d.add(dw));
+assert.throws(RangeError, () => dy.add(d));
+assert.throws(RangeError, () => dm.add(d));
+assert.throws(RangeError, () => dw.add(d));
+const relativeTo = Temporal.PlainDate.from("2000-01-01");
+TemporalHelpers.assertDuration(d.add(dy, { relativeTo }),
+ 1, 0, 0, 0, 1, 0, 0, 0, 0, 0);
+TemporalHelpers.assertDuration(d.add(dm, { relativeTo }),
+ 0, 1, 0, 0, 1, 0, 0, 0, 0, 0);
+TemporalHelpers.assertDuration(d.add(dw, { relativeTo }),
+ 0, 0, 1, 0, 1, 0, 0, 0, 0, 0);
+TemporalHelpers.assertDuration(dy.add(d, { relativeTo }),
+ 1, 0, 0, 0, 1, 0, 0, 0, 0, 0);
+TemporalHelpers.assertDuration(dm.add(d, { relativeTo }),
+ 0, 1, 0, 0, 1, 0, 0, 0, 0, 0);
+TemporalHelpers.assertDuration(dw.add(d, { relativeTo }),
+ 0, 0, 1, 0, 1, 0, 0, 0, 0, 0);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-string-datetime.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-string-datetime.js
new file mode 100644
index 0000000000..bc9b9524b0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-string-datetime.js
@@ -0,0 +1,41 @@
+// |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.duration.prototype.add
+description: >
+ Conversion of ISO date-time strings as relativeTo option to
+ Temporal.ZonedDateTime or Temporal.PlainDateTime instances
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(1, 0, 0, 1);
+
+let relativeTo = "2019-11-01T00:00";
+const result1 = instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo });
+TemporalHelpers.assertDuration(result1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "bare date-time string is a plain relativeTo");
+
+relativeTo = "2019-11-01T00:00-07:00";
+const result2 = instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo });
+TemporalHelpers.assertDuration(result2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "date-time + offset is a plain relativeTo");
+
+relativeTo = "2019-11-01T00:00[-07:00]";
+const result3 = instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo });
+TemporalHelpers.assertDuration(result3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "date-time + IANA annotation is a zoned relativeTo");
+
+relativeTo = "2019-11-01T00:00Z[-07:00]";
+const result4 = instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo });
+TemporalHelpers.assertDuration(result4, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "date-time + Z + IANA annotation is a zoned relativeTo");
+
+relativeTo = "2019-11-01T00:00+00:00[UTC]";
+const result5 = instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo });
+TemporalHelpers.assertDuration(result5, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "date-time + offset + IANA annotation is a zoned relativeTo");
+
+relativeTo = "2019-11-01T00:00Z";
+assert.throws(RangeError, () => instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }), "date-time + Z throws without an IANA annotation");
+relativeTo = "2019-11-01T00:00+04:15[UTC]";
+assert.throws(RangeError, () => instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }), "date-time + offset + IANA annotation throws if wall time and exact time mismatch");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-string-invalid.js
new file mode 100644
index 0000000000..83bf562a03
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-string-invalid.js
@@ -0,0 +1,16 @@
+// |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.duration.prototype.add
+description: RangeError thrown if relativeTo is a string with the wrong format
+features: [Temporal]
+---*/
+
+['bad string', '15:30:45.123456', 'iso8601', 'UTC', 'P1YT1H'].forEach((relativeTo) => {
+ const duration = new Temporal.Duration(1, 0, 0, 15);
+ assert.throws(RangeError, () => duration.add(new Temporal.Duration(0, 0, 0, 16), { relativeTo }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-string-plaindatetime.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-string-plaindatetime.js
new file mode 100644
index 0000000000..3b768e5f64
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-string-plaindatetime.js
@@ -0,0 +1,18 @@
+// |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.duration.prototype.add
+description: The relativeTo option accepts a PlainDateTime-like ISO 8601 string
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+['2000-01-01', '2000-01-01T00:00', '2000-01-01T00:00[u-ca=iso8601]'].forEach((relativeTo) => {
+ const duration = new Temporal.Duration(1, 0, 0, 15);
+ const result = duration.add(new Temporal.Duration(0, 0, 0, 16), { relativeTo });
+ TemporalHelpers.assertDuration(result, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-string-zoneddatetime-wrong-offset.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-string-zoneddatetime-wrong-offset.js
new file mode 100644
index 0000000000..67d5d83915
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-string-zoneddatetime-wrong-offset.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.duration.prototype.add
+description: Throws if a ZonedDateTime-like relativeTo string has the wrong UTC offset
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(1, 0, 0, 1);
+const relativeTo = "2000-01-01T00:00+05:30[UTC]";
+assert.throws(
+ RangeError,
+ () => instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }),
+ "add should throw RangeError on a string with UTC offset mismatch"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-string-zoneddatetime.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-string-zoneddatetime.js
new file mode 100644
index 0000000000..5326a3424d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-string-zoneddatetime.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.duration.prototype.add
+description: The relativeTo option accepts a ZonedDateTime-like ISO 8601 string
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ '2000-01-01[UTC]',
+ '2000-01-01T00:00[UTC]',
+ '2000-01-01T00:00+00:00[UTC]',
+ '2000-01-01T00:00+00:00[UTC][u-ca=iso8601]',
+].forEach((relativeTo) => {
+ const duration = new Temporal.Duration(1, 0, 0, 15);
+ const result = duration.add(new Temporal.Duration(0, 0, 0, 16), { relativeTo });
+ TemporalHelpers.assertDuration(result, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-sub-minute-offset.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-sub-minute-offset.js
new file mode 100644
index 0000000000..6e10351643
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-sub-minute-offset.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.duration.prototype.add
+description: relativeTo string accepts trailing zeroes in sub-minute UTC offset
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(1, 0, 0, 1);
+let result;
+let relativeTo;
+const action = (relativeTo) => instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo });
+
+relativeTo = "1970-01-01T00:00-00:45:00[-00:45]";
+result = action(relativeTo);
+TemporalHelpers.assertDateDuration(result, 1, 0, 0, 0, "ISO string offset accepted with zero seconds (string)");
+
+relativeTo = { year: 1970, month: 1, day: 1, offset: "+00:45:00.000000000", timeZone: "+00:45" };
+result = action(relativeTo);
+TemporalHelpers.assertDateDuration(result, 1, 0, 0, 0, "ISO string offset accepted with zero seconds (property bag)");
+
+relativeTo = "1970-01-01T00:00+00:44:30.123456789[+00:45]";
+assert.throws(RangeError, () => action(relativeTo), "rounding is not accepted between ISO offset and time zone");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-wrong-type.js
new file mode 100644
index 0000000000..9ddc26b490
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-wrong-type.js
@@ -0,0 +1,46 @@
+// |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.duration.prototype.add
+description: >
+ Appropriate error thrown when relativeTo cannot be converted to a valid
+ relativeTo string or property bag
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.Duration(1, 0, 0, 1);
+
+const primitiveTests = [
+ [undefined, "undefined"],
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [relativeTo, description] of primitiveTests) {
+ assert.throws(
+ typeof relativeTo === 'string' || typeof relativeTo === 'undefined' ? RangeError : TypeError,
+ () => instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }),
+ `${description} does not convert to a valid ISO string (first argument)`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.PlainDate, "Temporal.PlainDate, object"],
+ [Temporal.PlainDate.prototype, "Temporal.PlainDate.prototype, object"],
+ [Temporal.ZonedDateTime, "Temporal.ZonedDateTime, object"],
+ [Temporal.ZonedDateTime.prototype, "Temporal.ZonedDateTime.prototype, object"],
+];
+
+for (const [relativeTo, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }), `${description} is not a valid property bag and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-year.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-year.js
new file mode 100644
index 0000000000..8626a4bdff
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-year.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.duration.prototype.add
+description: relativeTo with years.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const oneYear = new Temporal.Duration(1);
+const days365 = new Temporal.Duration(0, 0, 0, 365);
+TemporalHelpers.assertDuration(oneYear.add(days365, { relativeTo: Temporal.PlainDate.from("2016-01-01") }),
+ 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, "non-leap year");
+TemporalHelpers.assertDuration(oneYear.add(days365, { relativeTo: Temporal.PlainDate.from("2015-01-01") }),
+ 1, 11, 0, 30, 0, 0, 0, 0, 0, 0, "leap year");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-zoneddatetime-negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-zoneddatetime-negative-epochnanoseconds.js
new file mode 100644
index 0000000000..0ac2cf925f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-zoneddatetime-negative-epochnanoseconds.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.duration.prototype.add
+description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch
+info: |
+ sec-temporal-getisopartsfromepoch step 1:
+ 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 10<sup>6</sup>.
+ sec-temporal-builtintimezonegetplaindatetimefor step 2:
+ 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]).
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const relativeTo = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC");
+const duration = new Temporal.Duration(0, 0, 0, 1);
+
+// This code path shows up anywhere we convert an exact time, before the Unix
+// epoch, with nonzero microseconds or nanoseconds, into a wall time; in this
+// case via relativeTo.
+
+const result = duration.add(duration, { relativeTo });
+TemporalHelpers.assertDuration(result, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-zoneddatetime-normalized-time-duration-to-days-range-errors.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-zoneddatetime-normalized-time-duration-to-days-range-errors.js
new file mode 100644
index 0000000000..7acd5929b6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-zoneddatetime-normalized-time-duration-to-days-range-errors.js
@@ -0,0 +1,146 @@
+// |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.duration.prototype.add
+description: >
+ Abstract operation NormalizedTimeDurationToDays can throw four different
+ RangeErrors.
+info: |
+ NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ , precalculatedPlainDateTime ] )
+ 22. If days < 0 and sign = 1, throw a RangeError exception.
+ 23. If days > 0 and sign = -1, throw a RangeError exception.
+ ...
+ 25. If NormalizedTimeDurationSign(_norm_) = 1 and sign = -1, throw a RangeError exception.
+ ...
+ 28. If dayLength ≥ 2⁵³, throw a RangeError exception.
+features: [Temporal, BigInt]
+includes: [temporalHelpers.js]
+---*/
+
+const dayNs = 86_400_000_000_000;
+const dayDuration = Temporal.Duration.from({ days: 1 });
+const epochInstant = new Temporal.Instant(0n);
+
+function timeZoneSubstituteValues(
+ getPossibleInstantsFor,
+ getOffsetNanosecondsFor
+) {
+ const tz = new Temporal.TimeZone("UTC");
+ TemporalHelpers.substituteMethod(
+ tz,
+ "getPossibleInstantsFor",
+ getPossibleInstantsFor
+ );
+ TemporalHelpers.substituteMethod(
+ tz,
+ "getOffsetNanosecondsFor",
+ getOffsetNanosecondsFor
+ );
+ return tz;
+}
+
+// Step 22: days < 0 and sign = 1
+let zdt = new Temporal.ZonedDateTime(
+ -1n, // Set DifferenceZonedDateTime _ns1_
+ timeZoneSubstituteValues(
+ [
+ TemporalHelpers.SUBSTITUTE_SKIP, // Behave normally for first call, AddDuration step 15
+ [epochInstant], // Returned in AddDuration step 16, setting _endNs_ -> DifferenceZonedDateTime _ns2_
+ [epochInstant], // Returned in step 16, setting _relativeResult_
+ ],
+ [
+ // Behave normally in 3 calls made prior to NormalizedTimeDurationToDays
+ TemporalHelpers.SUBSTITUTE_SKIP,
+ TemporalHelpers.SUBSTITUTE_SKIP,
+ TemporalHelpers.SUBSTITUTE_SKIP,
+ dayNs - 1, // Returned in step 8, setting _startDateTime_
+ -dayNs + 1, // Returned in step 9, setting _endDateTime_
+ ]
+ )
+);
+assert.throws(RangeError, () =>
+ // Adding day to day sets largestUnit to 'day', avoids having any week/month/year components in differences
+ dayDuration.add(dayDuration, {
+ relativeTo: zdt,
+ }),
+ "days < 0 and sign = 1"
+);
+
+// Step 23: days > 0 and sign = -1
+zdt = new Temporal.ZonedDateTime(
+ 1n, // Set DifferenceZonedDateTime _ns1_
+ timeZoneSubstituteValues(
+ [
+ TemporalHelpers.SUBSTITUTE_SKIP, // Behave normally for first call, AddDuration step 15
+ [epochInstant], // Returned in AddDuration step 16, setting _endNs_ -> DifferenceZonedDateTime _ns2_
+ [epochInstant], // Returned in step 16, setting _relativeResult_
+ ],
+ [
+ // Behave normally in 3 calls made prior to NanosecondsToDays
+ TemporalHelpers.SUBSTITUTE_SKIP,
+ TemporalHelpers.SUBSTITUTE_SKIP,
+ TemporalHelpers.SUBSTITUTE_SKIP,
+ -dayNs + 1, // Returned in step 8, setting _startDateTime_
+ dayNs - 1, // Returned in step 9, setting _endDateTime_
+ ]
+ )
+);
+assert.throws(RangeError, () =>
+ // Adding day to day sets largestUnit to 'day', avoids having any week/month/year components in differences
+ dayDuration.add(dayDuration, {
+ relativeTo: zdt,
+ }),
+ "days > 0 and sign = -1"
+);
+
+// Step 25: nanoseconds > 0 and sign = -1
+zdt = new Temporal.ZonedDateTime(
+ 0n, // Set DifferenceZonedDateTime _ns1_
+ timeZoneSubstituteValues(
+ [
+ TemporalHelpers.SUBSTITUTE_SKIP, // Behave normally for first call, AddDuration step 15
+ [new Temporal.Instant(-1n)], // Returned in AddDuration step 16, setting _endNs_ -> DifferenceZonedDateTime _ns2_
+ [new Temporal.Instant(-2n)], // Returned in step 16, setting _relativeResult_
+ [new Temporal.Instant(-4n)], // Returned in step 21.a, setting _oneDayFarther_
+ ],
+ [
+ // Behave normally in 3 calls made prior to NanosecondsToDays
+ TemporalHelpers.SUBSTITUTE_SKIP,
+ TemporalHelpers.SUBSTITUTE_SKIP,
+ TemporalHelpers.SUBSTITUTE_SKIP,
+ dayNs - 1, // Returned in step 8, setting _startDateTime_
+ -dayNs + 1, // Returned in step 9, setting _endDateTime_
+ ]
+ )
+);
+assert.throws(RangeError, () =>
+ // Adding day to day sets largestUnit to 'day', avoids having any week/month/year components in differences
+ dayDuration.add(dayDuration, {
+ relativeTo: zdt,
+ }),
+ "nanoseconds > 0 and sign = -1"
+);
+
+// Step 28: day length is an unsafe integer
+zdt = new Temporal.ZonedDateTime(
+ 0n,
+ timeZoneSubstituteValues(
+ [
+ TemporalHelpers.SUBSTITUTE_SKIP, // Behave normally for AddDuration step 15
+ TemporalHelpers.SUBSTITUTE_SKIP, // Behave normally for AddDuration step 16
+ TemporalHelpers.SUBSTITUTE_SKIP, // Behave normally for step 16, setting _relativeResult_
+ // Returned in step 21.a, making _oneDayFarther_ 2^53 ns later than _relativeResult_
+ [new Temporal.Instant(2n ** 53n + 2n * BigInt(dayNs))],
+ ],
+ []
+ )
+);
+assert.throws(RangeError, () =>
+ dayDuration.add(dayDuration, {
+ relativeTo: zdt,
+ }),
+ "Should throw RangeError when time zone calculates an outrageous day length"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..530264c10b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.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.duration.prototype.add
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+ const other = new Temporal.Duration(2);
+ const relativeTo = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => duration.add(other, { relativeTo }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..97988bf28f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.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.duration.prototype.add
+description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable
+features: [BigInt, Symbol, Temporal, arrow-function]
+---*/
+
+[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => {
+ const timeZone = new Temporal.TimeZone("UTC");
+ const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+ const other = new Temporal.Duration(2);
+ const relativeTo = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => duration.add(other, { relativeTo }),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..3c8d0e5cbd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.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.duration.prototype.add
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+ const other = new Temporal.Duration(2);
+ const relativeTo = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => duration.add(other, { relativeTo }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..b545cc6c02
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,29 @@
+// |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.duration.prototype.add
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+ const other = new Temporal.Duration(2);
+ const relativeTo = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => duration.add(other, { relativeTo }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/result-out-of-range-1.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/result-out-of-range-1.js
new file mode 100644
index 0000000000..a6860fa259
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/result-out-of-range-1.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.add
+description: >
+ BalanceDuration throws a RangeError when the result is too large.
+features: [Temporal]
+---*/
+
+const plainDate = new Temporal.PlainDate(1970, 1, 1);
+const zonedDateTime = new Temporal.ZonedDateTime(0n, "UTC", "iso8601");
+
+// Largest temporal unit is "second".
+const duration = Temporal.Duration.from({seconds: Number.MAX_SAFE_INTEGER});
+
+assert.throws(RangeError, () => {
+ duration.add(duration);
+});
+
+assert.throws(RangeError, () => {
+ duration.add(duration, {relativeTo: plainDate});
+});
+
+assert.throws(RangeError, () => {
+ duration.add(duration, {relativeTo: zonedDateTime});
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/result-out-of-range-2.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/result-out-of-range-2.js
new file mode 100644
index 0000000000..016cf10b57
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/result-out-of-range-2.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.add
+description: >
+ BalanceDuration throws a RangeError when the result is too large.
+features: [Temporal]
+---*/
+
+// Math.trunc(Number.MAX_SAFE_INTEGER / 86400) === 104249991374
+var duration = Temporal.Duration.from({days: 104249991374});
+
+assert.throws(RangeError, () => duration.add(duration));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/shell.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/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/Duration/prototype/add/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/subclassing-ignored.js
new file mode 100644
index 0000000000..59afb2df5f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/subclassing-ignored.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.duration.prototype.add
+description: Objects of a subclass are never created as return values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.Duration,
+ [0, 0, 0, 4, 5, 6, 7, 987, 654, 321],
+ "add",
+ [{ nanoseconds: 1 }],
+ (result) => TemporalHelpers.assertDuration(result, 0, 0, 0, 4, 5, 6, 7, 987, 654, 322),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/timezone-getpossibleinstantsfor-iterable.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/timezone-getpossibleinstantsfor-iterable.js
new file mode 100644
index 0000000000..5da8057d22
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/timezone-getpossibleinstantsfor-iterable.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.duration.prototype.add
+description: An iterable returned from timeZone.getPossibleInstantsFor is consumed after each call
+info: |
+ sec-temporal.duration.prototype.add steps 5–6:
+ 5. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_).
+ 6. Let _result_ be ? AddDuration(_duration_.[[Years]], [...], _duration_.[[Nanoseconds]], _other_.[[Years]], [...], _other_.[[Nanoseconds]], _relativeTo_).
+ sec-temporal-torelativetemporalobject step 6.d:
+ d. Let _epochNanoseconds_ be ? InterpretISODateTimeOffset(_result_.[[Year]], [...], _result_.[[Nanosecond]], _offsetNs_, _timeZone_, *"compatible"*, *"reject"*).
+ sec-temporal-interpretisodatetimeoffset step 7:
+ 7. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_).
+ sec-temporal-addduration steps 7.d–e and 7.g.i:
+ d. Let _intermediateNs_ be ? AddZonedDateTime(_relativeTo_.[[Nanoseconds]], _timeZone_, _calendar_, _y1_, [...], _ns1_).
+ e. Let _endNs_ be ? AddZonedDateTime(_intermediateNs_, _timeZone_, _calendar_, _y2_, [...], _ns2_).
+ [...]
+ i. Let _result_ be ? DifferenceZonedDateTime(_relativeTo_.[[Nanoseconds]], _endNs_, _timeZone_, _calendar_, _largestUnit_).
+ sec-temporal-differencezoneddatetime step 8:
+ 8. Let _intermediateNs_ be ? AddZonedDateTime(_ns1_, _timeZone_, _calendar_, _dateDifference_.[[Years]], _dateDifference_.[[Months]], _dateDifference_.[[Weeks]], 0, 0, 0, 0, 0, 0, 0).
+ sec-temporal-addzoneddatetime step 8:
+ 8. Let _intermediateInstant_ be ? BuiltinTimeZoneGetInstantFor(_timeZone_, _intermediateDateTime_, *"compatible"*).
+ sec-temporal-builtintimezonegetinstantfor step 1:
+ 1. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_).
+ sec-temporal-getpossibleinstantsfor step 2:
+ 2. Let _list_ be ? IterableToList(_possibleInstants_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "2000-01-01T09:00:00", // called once on the input relativeTo object
+ "2001-01-01T09:00:00", // called once on relativeTo plus the receiver
+ "2002-01-01T09:00:00", // called once on relativeTo plus the receiver plus the argument
+ "2002-01-01T09:00:00", // called once on relativeTo plus the years, months, and weeks from the difference of relativeTo minus endNs
+];
+
+TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => {
+ const duration1 = new Temporal.Duration(1);
+ const duration2 = new Temporal.Duration(0, 12);
+ duration1.add(duration2, { relativeTo: { year: 2000, month: 1, day: 1, hour: 9, timeZone } });
+}, expected);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/year-zero.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/year-zero.js
new file mode 100644
index 0000000000..1265aeaa13
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/year-zero.js
@@ -0,0 +1,20 @@
+// |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.duration.prototype.add
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const instance = new Temporal.Duration(1, 0, 0, 1);
+
+let relativeTo = "-000000-11-04T00:00";
+assert.throws(
+ RangeError,
+ () => { instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }); },
+ "reject minus zero as extended year"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/blank/basic.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/blank/basic.js
new file mode 100644
index 0000000000..4c4451c7b0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/blank/basic.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-get-temporal.duration.prototype.blank
+description: Basic tests for blank.
+features: [Temporal]
+---*/
+
+assert.sameValue(Temporal.Duration.from("P3DT1H").blank, false);
+assert.sameValue(Temporal.Duration.from("-PT2H20M30S").blank, false);
+assert.sameValue(Temporal.Duration.from("PT0S").blank, true);
+const zero = Temporal.Duration.from({
+ years: 0,
+ months: 0,
+ weeks: 0,
+ days: 0,
+ hours: 0,
+ minutes: 0,
+ seconds: 0,
+ milliseconds: 0,
+ microseconds: 0,
+ nanoseconds: 0
+});
+assert.sameValue(zero.blank, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/blank/branding.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/blank/branding.js
new file mode 100644
index 0000000000..fe9b6a4f04
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/blank/branding.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-get-temporal.duration.prototype.blank
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const blank = Object.getOwnPropertyDescriptor(Temporal.Duration.prototype, "blank").get;
+
+assert.sameValue(typeof blank, "function");
+
+assert.throws(TypeError, () => blank.call(undefined), "undefined");
+assert.throws(TypeError, () => blank.call(null), "null");
+assert.throws(TypeError, () => blank.call(true), "true");
+assert.throws(TypeError, () => blank.call(""), "empty string");
+assert.throws(TypeError, () => blank.call(Symbol()), "symbol");
+assert.throws(TypeError, () => blank.call(1), "1");
+assert.throws(TypeError, () => blank.call({}), "plain object");
+assert.throws(TypeError, () => blank.call(Temporal.Duration), "Temporal.Duration");
+assert.throws(TypeError, () => blank.call(Temporal.Duration.prototype), "Temporal.Duration.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/blank/browser.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/blank/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/blank/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/blank/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/blank/prop-desc.js
new file mode 100644
index 0000000000..f2f50d3080
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/blank/prop-desc.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-get-temporal.duration.prototype.blank
+description: The "blank" property of Temporal.Duration.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.Duration.prototype, "blank");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/blank/shell.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/blank/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/blank/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/browser.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/constructor.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/constructor.js
new file mode 100644
index 0000000000..323b227d12
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/constructor.js
@@ -0,0 +1,20 @@
+// |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.duration.prototype.constructor
+description: Test for Temporal.Duration.prototype.constructor.
+info: The initial value of Temporal.Duration.prototype.constructor is %Temporal.Duration%.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Duration.prototype, "constructor", {
+ value: Temporal.Duration,
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/days/branding.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/days/branding.js
new file mode 100644
index 0000000000..063449c232
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/days/branding.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-get-temporal.duration.prototype.days
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const days = Object.getOwnPropertyDescriptor(Temporal.Duration.prototype, "days").get;
+
+assert.sameValue(typeof days, "function");
+
+assert.throws(TypeError, () => days.call(undefined), "undefined");
+assert.throws(TypeError, () => days.call(null), "null");
+assert.throws(TypeError, () => days.call(true), "true");
+assert.throws(TypeError, () => days.call(""), "empty string");
+assert.throws(TypeError, () => days.call(Symbol()), "symbol");
+assert.throws(TypeError, () => days.call(1), "1");
+assert.throws(TypeError, () => days.call({}), "plain object");
+assert.throws(TypeError, () => days.call(Temporal.Duration), "Temporal.Duration");
+assert.throws(TypeError, () => days.call(Temporal.Duration.prototype), "Temporal.Duration.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/days/browser.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/days/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/days/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/days/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/days/prop-desc.js
new file mode 100644
index 0000000000..e4c9137a17
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/days/prop-desc.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-get-temporal.duration.prototype.days
+description: The "days" property of Temporal.Duration.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.Duration.prototype, "days");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/days/shell.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/days/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/days/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/hours/branding.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/hours/branding.js
new file mode 100644
index 0000000000..89ca8ae208
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/hours/branding.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-get-temporal.duration.prototype.hours
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const hours = Object.getOwnPropertyDescriptor(Temporal.Duration.prototype, "hours").get;
+
+assert.sameValue(typeof hours, "function");
+
+assert.throws(TypeError, () => hours.call(undefined), "undefined");
+assert.throws(TypeError, () => hours.call(null), "null");
+assert.throws(TypeError, () => hours.call(true), "true");
+assert.throws(TypeError, () => hours.call(""), "empty string");
+assert.throws(TypeError, () => hours.call(Symbol()), "symbol");
+assert.throws(TypeError, () => hours.call(1), "1");
+assert.throws(TypeError, () => hours.call({}), "plain object");
+assert.throws(TypeError, () => hours.call(Temporal.Duration), "Temporal.Duration");
+assert.throws(TypeError, () => hours.call(Temporal.Duration.prototype), "Temporal.Duration.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/hours/browser.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/hours/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/hours/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/hours/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/hours/prop-desc.js
new file mode 100644
index 0000000000..f74ac45719
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/hours/prop-desc.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-get-temporal.duration.prototype.hours
+description: The "hours" property of Temporal.Duration.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.Duration.prototype, "hours");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/hours/shell.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/hours/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/hours/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/microseconds/branding.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/microseconds/branding.js
new file mode 100644
index 0000000000..a2bf62462c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/microseconds/branding.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-get-temporal.duration.prototype.microseconds
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const microseconds = Object.getOwnPropertyDescriptor(Temporal.Duration.prototype, "microseconds").get;
+
+assert.sameValue(typeof microseconds, "function");
+
+assert.throws(TypeError, () => microseconds.call(undefined), "undefined");
+assert.throws(TypeError, () => microseconds.call(null), "null");
+assert.throws(TypeError, () => microseconds.call(true), "true");
+assert.throws(TypeError, () => microseconds.call(""), "empty string");
+assert.throws(TypeError, () => microseconds.call(Symbol()), "symbol");
+assert.throws(TypeError, () => microseconds.call(1), "1");
+assert.throws(TypeError, () => microseconds.call({}), "plain object");
+assert.throws(TypeError, () => microseconds.call(Temporal.Duration), "Temporal.Duration");
+assert.throws(TypeError, () => microseconds.call(Temporal.Duration.prototype), "Temporal.Duration.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/microseconds/browser.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/microseconds/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/microseconds/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/microseconds/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/microseconds/prop-desc.js
new file mode 100644
index 0000000000..2878e71ad8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/microseconds/prop-desc.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-get-temporal.duration.prototype.microseconds
+description: The "microseconds" property of Temporal.Duration.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.Duration.prototype, "microseconds");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/microseconds/shell.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/microseconds/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/microseconds/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/milliseconds/branding.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/milliseconds/branding.js
new file mode 100644
index 0000000000..458aaea111
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/milliseconds/branding.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-get-temporal.duration.prototype.milliseconds
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const milliseconds = Object.getOwnPropertyDescriptor(Temporal.Duration.prototype, "milliseconds").get;
+
+assert.sameValue(typeof milliseconds, "function");
+
+assert.throws(TypeError, () => milliseconds.call(undefined), "undefined");
+assert.throws(TypeError, () => milliseconds.call(null), "null");
+assert.throws(TypeError, () => milliseconds.call(true), "true");
+assert.throws(TypeError, () => milliseconds.call(""), "empty string");
+assert.throws(TypeError, () => milliseconds.call(Symbol()), "symbol");
+assert.throws(TypeError, () => milliseconds.call(1), "1");
+assert.throws(TypeError, () => milliseconds.call({}), "plain object");
+assert.throws(TypeError, () => milliseconds.call(Temporal.Duration), "Temporal.Duration");
+assert.throws(TypeError, () => milliseconds.call(Temporal.Duration.prototype), "Temporal.Duration.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/milliseconds/browser.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/milliseconds/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/milliseconds/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/milliseconds/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/milliseconds/prop-desc.js
new file mode 100644
index 0000000000..f645b27d20
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/milliseconds/prop-desc.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-get-temporal.duration.prototype.milliseconds
+description: The "milliseconds" property of Temporal.Duration.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.Duration.prototype, "milliseconds");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/milliseconds/shell.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/milliseconds/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/milliseconds/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/minutes/branding.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/minutes/branding.js
new file mode 100644
index 0000000000..27341c3cf5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/minutes/branding.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-get-temporal.duration.prototype.minutes
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const minutes = Object.getOwnPropertyDescriptor(Temporal.Duration.prototype, "minutes").get;
+
+assert.sameValue(typeof minutes, "function");
+
+assert.throws(TypeError, () => minutes.call(undefined), "undefined");
+assert.throws(TypeError, () => minutes.call(null), "null");
+assert.throws(TypeError, () => minutes.call(true), "true");
+assert.throws(TypeError, () => minutes.call(""), "empty string");
+assert.throws(TypeError, () => minutes.call(Symbol()), "symbol");
+assert.throws(TypeError, () => minutes.call(1), "1");
+assert.throws(TypeError, () => minutes.call({}), "plain object");
+assert.throws(TypeError, () => minutes.call(Temporal.Duration), "Temporal.Duration");
+assert.throws(TypeError, () => minutes.call(Temporal.Duration.prototype), "Temporal.Duration.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/minutes/browser.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/minutes/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/minutes/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/minutes/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/minutes/prop-desc.js
new file mode 100644
index 0000000000..3f04727668
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/minutes/prop-desc.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-get-temporal.duration.prototype.minutes
+description: The "minutes" property of Temporal.Duration.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.Duration.prototype, "minutes");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/minutes/shell.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/minutes/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/minutes/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/months/branding.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/months/branding.js
new file mode 100644
index 0000000000..01737c1bf4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/months/branding.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-get-temporal.duration.prototype.months
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const months = Object.getOwnPropertyDescriptor(Temporal.Duration.prototype, "months").get;
+
+assert.sameValue(typeof months, "function");
+
+assert.throws(TypeError, () => months.call(undefined), "undefined");
+assert.throws(TypeError, () => months.call(null), "null");
+assert.throws(TypeError, () => months.call(true), "true");
+assert.throws(TypeError, () => months.call(""), "empty string");
+assert.throws(TypeError, () => months.call(Symbol()), "symbol");
+assert.throws(TypeError, () => months.call(1), "1");
+assert.throws(TypeError, () => months.call({}), "plain object");
+assert.throws(TypeError, () => months.call(Temporal.Duration), "Temporal.Duration");
+assert.throws(TypeError, () => months.call(Temporal.Duration.prototype), "Temporal.Duration.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/months/browser.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/months/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/months/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/months/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/months/prop-desc.js
new file mode 100644
index 0000000000..a3b1923f2a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/months/prop-desc.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-get-temporal.duration.prototype.months
+description: The "months" property of Temporal.Duration.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.Duration.prototype, "months");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/months/shell.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/months/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/months/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/nanoseconds/branding.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/nanoseconds/branding.js
new file mode 100644
index 0000000000..512cdd4602
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/nanoseconds/branding.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-get-temporal.duration.prototype.nanoseconds
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const nanoseconds = Object.getOwnPropertyDescriptor(Temporal.Duration.prototype, "nanoseconds").get;
+
+assert.sameValue(typeof nanoseconds, "function");
+
+assert.throws(TypeError, () => nanoseconds.call(undefined), "undefined");
+assert.throws(TypeError, () => nanoseconds.call(null), "null");
+assert.throws(TypeError, () => nanoseconds.call(true), "true");
+assert.throws(TypeError, () => nanoseconds.call(""), "empty string");
+assert.throws(TypeError, () => nanoseconds.call(Symbol()), "symbol");
+assert.throws(TypeError, () => nanoseconds.call(1), "1");
+assert.throws(TypeError, () => nanoseconds.call({}), "plain object");
+assert.throws(TypeError, () => nanoseconds.call(Temporal.Duration), "Temporal.Duration");
+assert.throws(TypeError, () => nanoseconds.call(Temporal.Duration.prototype), "Temporal.Duration.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/nanoseconds/browser.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/nanoseconds/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/nanoseconds/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/nanoseconds/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/nanoseconds/prop-desc.js
new file mode 100644
index 0000000000..faf9c259b1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/nanoseconds/prop-desc.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-get-temporal.duration.prototype.nanoseconds
+description: The "nanoseconds" property of Temporal.Duration.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.Duration.prototype, "nanoseconds");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/nanoseconds/shell.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/nanoseconds/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/nanoseconds/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/basic.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/basic.js
new file mode 100644
index 0000000000..3bc3d93362
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/basic.js
@@ -0,0 +1,54 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.negated
+description: Temporal.Duration.prototype.negated will return negated value of the input duration.
+info: |
+ 3. Return ? CreateTemporalDuration(abs(duration.[[Years]]), abs(duration.[[Months]]), abs(duration.[[Weeks]]), abs(duration.[[Days]]), abs(duration.[[Hours]]), abs(duration.[[Minutes]]), abs(duration.[[Seconds]]), abs(duration.[[Milliseconds]]), abs(duration.[[Microseconds]]), abs(duration.[[Nanoseconds]])).
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+let d1 = new Temporal.Duration();
+TemporalHelpers.assertDuration(
+ d1.negated(), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "zeros");
+
+let d2 = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
+TemporalHelpers.assertDuration(
+ d2.negated(), -1, -2, -3, -4, -5, -6, -7, -8, -9, -10,
+ "positive values");
+
+let d3 = new Temporal.Duration(1e5, 2e5, 3e5, 4e5, 5e5, 6e5, 7e5, 8e5, 9e5, 10e5);
+TemporalHelpers.assertDuration(
+ d3.negated(), -1e5, -2e5, -3e5, -4e5, -5e5, -6e5, -7e5, -8e5, -9e5, -10e5,
+ "large positive values");
+
+let d4 = new Temporal.Duration(-1, -2, -3, -4, -5, -6, -7, -8, -9, -10);
+TemporalHelpers.assertDuration(
+ d4.negated(), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+ "negative values");
+
+let d5 = new Temporal.Duration(-1e5, -2e5, -3e5, -4e5, -5e5, -6e5, -7e5, -8e5, -9e5, -10e5);
+TemporalHelpers.assertDuration(
+ d5.negated(), 1e5, 2e5, 3e5, 4e5, 5e5, 6e5, 7e5, 8e5, 9e5, 10e5,
+ "large negative values");
+
+let d6 = new Temporal.Duration(1, 0, 3, 0, 5, 0, 7, 0, 9, 0);
+TemporalHelpers.assertDuration(
+ d6.negated(), -1, 0, -3, 0, -5, 0, -7, 0, -9, 0,
+ "some zeros with positive values");
+
+let d7 = new Temporal.Duration(-1, 0, -3, 0, -5, 0, -7, 0, -9, 0);
+TemporalHelpers.assertDuration(
+ d7.negated(), 1, 0, 3, 0, 5, 0, 7, 0, 9, 0,
+ "some zeros with negative values");
+
+let d8 = new Temporal.Duration(0, -2, 0, -4, 0, -6, 0, -8, 0, -10);
+TemporalHelpers.assertDuration(
+ d8.negated(), 0, 2, 0, 4, 0, 6, 0, 8, 0, 10,
+ "other zeros with negative values");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/branding.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/branding.js
new file mode 100644
index 0000000000..a6246ddb46
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/branding.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.duration.prototype.negated
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const negated = Temporal.Duration.prototype.negated;
+
+assert.sameValue(typeof negated, "function");
+
+assert.throws(TypeError, () => negated.call(undefined), "undefined");
+assert.throws(TypeError, () => negated.call(null), "null");
+assert.throws(TypeError, () => negated.call(true), "true");
+assert.throws(TypeError, () => negated.call(""), "empty string");
+assert.throws(TypeError, () => negated.call(Symbol()), "symbol");
+assert.throws(TypeError, () => negated.call(1), "1");
+assert.throws(TypeError, () => negated.call({}), "plain object");
+assert.throws(TypeError, () => negated.call(Temporal.Duration), "Temporal.Duration");
+assert.throws(TypeError, () => negated.call(Temporal.Duration.prototype), "Temporal.Duration.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/browser.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/builtin.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/builtin.js
new file mode 100644
index 0000000000..3ad1282799
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/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.duration.prototype.negated
+description: >
+ Tests that Temporal.Duration.prototype.negated
+ 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.Duration.prototype.negated),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Duration.prototype.negated),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Duration.prototype.negated),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Duration.prototype.negated.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/length.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/length.js
new file mode 100644
index 0000000000..1af561f0f8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/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.duration.prototype.negated
+description: Temporal.Duration.prototype.negated.length is 0
+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.Duration.prototype.negated, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/name.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/name.js
new file mode 100644
index 0000000000..b2db8c5f1d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/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.duration.prototype.negated
+description: Temporal.Duration.prototype.negated.name is "negated".
+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.Duration.prototype.negated, "name", {
+ value: "negated",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/not-a-constructor.js
new file mode 100644
index 0000000000..f086eb7903
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/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.duration.prototype.negated
+description: >
+ Temporal.Duration.prototype.negated 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.Duration.prototype.negated();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Duration.prototype.negated), false,
+ "isConstructor(Temporal.Duration.prototype.negated)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/prop-desc.js
new file mode 100644
index 0000000000..63962bc625
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/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.duration.prototype.negated
+description: The "negated" property of Temporal.Duration.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Duration.prototype.negated,
+ "function",
+ "`typeof Duration.prototype.negated` is `function`"
+);
+
+verifyProperty(Temporal.Duration.prototype, "negated", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/shell.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/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/Duration/prototype/negated/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/subclassing-ignored.js
new file mode 100644
index 0000000000..436e4cafd6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/subclassing-ignored.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.duration.prototype.negated
+description: Objects of a subclass are never created as return values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.Duration,
+ [0, 0, 0, 4, 5, 6, 7, 987, 654, 321],
+ "negated",
+ [],
+ (result) => TemporalHelpers.assertDuration(result, 0, 0, 0, -4, -5, -6, -7, -987, -654, -321),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/prop-desc.js
new file mode 100644
index 0000000000..e1aed784ac
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/prop-desc.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-duration-prototype
+description: The "prototype" property of Temporal.Duration
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(typeof Temporal.Duration.prototype, "object");
+assert.notSameValue(Temporal.Duration.prototype, null);
+
+verifyProperty(Temporal.Duration, "prototype", {
+ writable: false,
+ enumerable: false,
+ configurable: false,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/balance-negative-result.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/balance-negative-result.js
new file mode 100644
index 0000000000..2ec0fd8248
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/balance-negative-result.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.duration.prototype.round
+description: A negative duration result is balanced correctly by the modulo operation in NanosecondsToDays
+info: |
+ sec-temporal-nanosecondstodays step 6:
+ 6. If Type(_relativeTo_) is not Object or _relativeTo_ does not have an [[InitializedTemporalZonedDateTime]] internal slot, then
+ a. Return the new Record { ..., [[Nanoseconds]]: abs(_nanoseconds_) modulo _dayLengthNs_ × _sign_, ... }.
+ sec-temporal-balanceduration step 4:
+ 4. If _largestUnit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*, then
+ a. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _relativeTo_).
+ sec-temporal.duration.prototype.round step 25:
+ 25. Let _result_ be ? BalanceDuration(_balanceResult_.[[Days]], _adjustResult_.[[Hours]], _adjustResult_.[[Minutes]], _adjustResult_.[[Seconds]], _adjustResult_.[[Milliseconds]], _adjustResult_.[[Microseconds]], _adjustResult_.[[Nanoseconds]], _largestUnit_, _relativeTo_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(0, 0, 0, 0, -60);
+const result = duration.round({ largestUnit: "days" });
+TemporalHelpers.assertDuration(result, 0, 0, 0, -2, -12, 0, 0, 0, 0, 0);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/balance-subseconds.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/balance-subseconds.js
new file mode 100644
index 0000000000..c250101272
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/balance-subseconds.js
@@ -0,0 +1,18 @@
+// |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.duration.prototype.round
+description: Balancing from subsecond units to seconds happens correctly
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const pos = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 999, 999999, 999999999);
+TemporalHelpers.assertDuration(pos.round({ largestUnit: "seconds" }), 0, 0, 0, 0, 0, 0, 2, 998, 998, 999);
+
+const neg = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, -999, -999999, -999999999);
+TemporalHelpers.assertDuration(neg.round({ largestUnit: "seconds" }), 0, 0, 0, 0, 0, 0, -2, -998, -998, -999);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/branding.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/branding.js
new file mode 100644
index 0000000000..cf4313c117
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/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.duration.prototype.round
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const round = Temporal.Duration.prototype.round;
+
+assert.sameValue(typeof round, "function");
+
+const args = ["hour"];
+
+assert.throws(TypeError, () => round.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => round.apply(null, args), "null");
+assert.throws(TypeError, () => round.apply(true, args), "true");
+assert.throws(TypeError, () => round.apply("", args), "empty string");
+assert.throws(TypeError, () => round.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => round.apply(1, args), "1");
+assert.throws(TypeError, () => round.apply({}, args), "plain object");
+assert.throws(TypeError, () => round.apply(Temporal.Duration, args), "Temporal.Duration");
+assert.throws(TypeError, () => round.apply(Temporal.Duration.prototype, args), "Temporal.Duration.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/browser.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/builtin.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/builtin.js
new file mode 100644
index 0000000000..5d6b359ca4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/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.duration.prototype.round
+description: >
+ Tests that Temporal.Duration.prototype.round
+ 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.Duration.prototype.round),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Duration.prototype.round),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Duration.prototype.round),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Duration.prototype.round.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/calendar-dateadd-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/calendar-dateadd-called-with-options-undefined.js
new file mode 100644
index 0000000000..5080d4a6e4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/calendar-dateadd-called-with-options-undefined.js
@@ -0,0 +1,60 @@
+// |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.duration.prototype.round
+description: >
+ BuiltinTimeZoneGetInstantFor calls Calendar.dateAdd with undefined as the
+ options value
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarDateAddUndefinedOptions();
+const timeZone = TemporalHelpers.oneShiftTimeZone(new Temporal.Instant(0n), 3600e9);
+const relativeTo = new Temporal.ZonedDateTime(0n, timeZone, calendar);
+
+// Rounding with smallestUnit a calendar unit.
+// The calls come from these paths:
+// Duration.round() ->
+// RoundDuration ->
+// MoveRelativeZonedDateTime -> AddZonedDateTime -> calendar.dateAdd()
+// MoveRelativeDate -> calendar.dateAdd()
+// BalanceDateDurationRelative -> calendar.dateAdd()
+
+const instance1 = new Temporal.Duration(1, 1, 1, 1, 1);
+instance1.round({ smallestUnit: "weeks", relativeTo });
+assert.sameValue(calendar.dateAddCallCount, 3, "rounding with calendar smallestUnit");
+
+// Rounding with a non-default largestUnit to cover the path in
+// UnbalanceDurationRelative where larger units are converted into smaller
+// units; and with a smallestUnit larger than days to cover the path in
+// RoundDuration where days are converted into larger units.
+// The calls come from these paths:
+// Duration.round() ->
+// UnbalanceDurationRelative -> MoveRelativeDate -> calendar.dateAdd()
+// RoundDuration -> MoveRelativeDate -> calendar.dateAdd() (2x)
+// BalanceDateDurationRelative -> calendar.dateAdd()
+// MoveRelativeZonedDateTime -> AddZonedDateTime -> calendar.dateAdd()
+
+calendar.dateAddCallCount = 0;
+
+const instance2 = new Temporal.Duration(0, 1, 1, 1);
+instance2.round({ largestUnit: "weeks", smallestUnit: "weeks", relativeTo });
+assert.sameValue(calendar.dateAddCallCount, 5, "rounding with non-default largestUnit and calendar smallestUnit");
+
+// Rounding with smallestUnit days only.
+// The calls come from these paths:
+// Duration.round() ->
+// RoundDuration ->
+// MoveRelativeZonedDateTime -> AddZonedDateTime -> calendar.dateAdd()
+// BalanceDateDurationRelative -> calendar.dateAdd()
+
+calendar.dateAddCallCount = 0;
+
+const instance3 = new Temporal.Duration(1, 1, 1, 1, 1);
+instance3.round({ smallestUnit: "days", relativeTo });
+assert.sameValue(calendar.dateAddCallCount, 2, "rounding with days smallestUnit");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/calendar-dateadd-called-with-plaindate-instance.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/calendar-dateadd-called-with-plaindate-instance.js
new file mode 100644
index 0000000000..71a57abb3c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/calendar-dateadd-called-with-plaindate-instance.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.duration.prototype.round
+description: >
+ relativeTo parameters that are not ZonedDateTime or undefined, are always
+ converted to PlainDate for observable calendar calls
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarDateAddPlainDateInstance();
+const instance = new Temporal.Duration(1, 1, 1, 1);
+const relativeTo = new Temporal.PlainDate(2000, 1, 1, calendar);
+calendar.specificPlainDate = relativeTo;
+instance.round({ largestUnit: "days", relativeTo });
+assert(calendar.dateAddCallCount > 0, "assertions in calendar.dateAdd() should have been tested");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/calendar-dateuntil-called-with-singular-largestunit.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/calendar-dateuntil-called-with-singular-largestunit.js
new file mode 100644
index 0000000000..6338e1a492
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/calendar-dateuntil-called-with-singular-largestunit.js
@@ -0,0 +1,157 @@
+// |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.duration.prototype.round
+description: The options object passed to calendar.dateUntil has a largestUnit property with its value in the singular form
+info: |
+ sec-temporal.duration.prototype.round steps 23–27:
+ 23. Let _unbalanceResult_ be ? UnbalanceDateDurationRelative(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _largestUnit_, _relativeTo_).
+ 24. Let _roundResult_ be (? RoundDuration(_unbalanceResult_.[[Years]], _unbalanceResult_.[[Months]], _unbalanceResult_.[[Weeks]], _unbalanceResult_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], _roundingIncrement_, _smallestUnit_, _roundingMode_, _relativeTo_)).[[DurationRecord]].
+ 25. Let _adjustResult_ be ? AdjustRoundedDurationDays(_roundResult_.[[Years]], _roundResult_.[[Months]], _roundResult_.[[Weeks]], _roundResult_.[[Days]], _roundResult_.[[Hours]], _roundResult_.[[Minutes]], _roundResult_.[[Seconds]], _roundResult_.[[Milliseconds]], _roundResult_.[[Microseconds]], _roundResult_.[[Nanoseconds]], _roundingIncrement_, _smallestUnit_, _roundingMode_, _relativeTo_).
+ 26. Let _balanceResult_ be ? BalanceDuration(_adjustResult_.[[Days]], _adjustResult_.[[Hours]], _adjustResult_.[[Minutes]], _adjustResult_.[[Seconds]], _adjustResult_.[[Milliseconds]], _adjustResult_.[[Microseconds]], _adjustResult_.[[Nanoseconds]], _largestUnit_, _relativeTo_).
+ 27. Let _result_ be ? BalanceDurationRelative(_adjustResult_.[[Years]], _adjustResult_.[[Months]], _adjustResult_.[[Weeks]], _balanceResult_.[[Days]], _largestUnit_, _relativeTo_).
+ sec-temporal-unbalancedatedurationrelative step 3:
+ 3. If _largestUnit_ is *"month"*, then
+ ...
+ g. Let _untilOptions_ be ! OrdinaryObjectCreate(*null*).
+ h. Perform ! CreateDataPropertyOrThrow(_untilOptions_, *"largestUnit"*, *"month"*).
+ i. Let _untilResult_ be ? CalendarDateUntil(_calendarRec_.[[Receiver]], _plainRelativeTo_, _later_, _untilOptions_, _calendarRec_.[[DateUntil]]).
+ sec-temporal-roundduration steps 5.d and 8.n–p:
+ 5. If _unit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*, then
+ ...
+ d. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _intermediate_).
+ ...
+ 8. If _unit_ is *"year"*, then
+ ...
+ n. Let _untilOptions_ be ! OrdinaryObjectCreate(*null*).
+ o. Perform ! CreateDataPropertyOrThrow(_untilOptions_, *"largestUnit"*, *"year"*).
+ p. Let _timePassed_ be ? CalendarDateUntil(_calendar_, _relativeTo_, _daysLater_, _untilOptions_)
+ sec-temporal-adjustroundeddurationdays steps 1 and 9:
+ 1. If _relativeTo_ does not have an [[InitializedTemporalZonedDateTime]] internal slot; or _unit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*; or _unit_ is *"nanosecond"* and _increment_ is 1, then
+ a. Return ...
+ ...
+ 9. Let _adjustedDateDuration_ be ? AddDuration(_years_, _months_, _weeks_, _days_, 0, 0, 0, 0, 0, 0, 0, 0, 0, _direction_, 0, 0, 0, 0, 0, 0, _relativeTo_).
+ sec-temporal-addduration step 7.a–g:
+ a. Assert: _relativeTo_ has an [[IntializedTemporalZonedDateTime]] internal slot.
+ ...
+ f. If _largestUnit_ is not one of *"year"*, *"month"*, *"week"*, or *"day"*, then
+ ...
+ g. Else,
+ i. Let _result_ be ? DifferenceZonedDateTime(_relativeTo_.[[Nanoseconds]], _endNs_, _timeZone_, _calendar_, _largestUnit_).
+ sec-temporal-balancedurationrelative steps 1, 9.m–o, and 9.q.vi–viii:
+ 1. If _largestUnit_ is not one of *"year"*, *"month"*, or *"week"*, or _years_, _months_, _weeks_, and _days_ are all 0, then
+ a. Return ...
+ ...
+ 9. If _largestUnit_ is *"year"*, then
+ ...
+ m. Let _untilOptions_ be ! OrdinaryObjectCreate(*null*).
+ n. Perform ! CreateDataPropertyOrThrow(_untilOptions_, *"largestUnit"*, *"month"*).
+ o. Let _untilResult_ be ? CalendarDateUntil(_calendar_, _relativeTo_, _newRelativeTo_, _untilOptions_, _dateUntil_).
+ p. ...
+ q. Repeat, while abs(_months_) ≥ abs(_oneYearMonths_),
+ ...
+ vi. Let _untilOptions_ be ! OrdinaryObjectCreate(*null*).
+ vii. Perform ! CreateDataPropertyOrThrow(_untilOptions_, *"largestUnit"*, *"month"*).
+ viii. Let _untilResult_ be ? CalendarDateUntil(_calendar_, _relativeTo_, _newRelativeTo_, _untilOptions_, _dateUntil_).
+ sec-temporal-balanceduration step 3.a:
+ 3. If _largestUnit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*, then
+ a. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _relativeTo_).
+ sec-temporal-differencezoneddatetime steps 7 and 11:
+ 7. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _calendar_, _largestUnit_, _options_).
+ 11. Let _result_ be ? NanosecondsToDays(_timeRemainderNs_, _intermediate_).
+ sec-temporal-nanosecondstodays step 11:
+ 11. 1. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _relativeTo_.[[Calendar]], *"day"*).
+ sec-temporal-differenceisodatetime steps 9–11:
+ 9. Let _dateLargestUnit_ be ! LargerOfTwoTemporalUnits(*"day"*, _largestUnit_).
+ 10. Let _untilOptions_ be ? MergeLargestUnitOption(_options_, _dateLargestUnit_).
+ 11. Let _dateDifference_ be ? CalendarDateUntil(_calendar_, _date1_, _date2_, _untilOptions_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+// Check with smallestUnit nanoseconds but roundingIncrement > 1; each call
+// should result in one call to dateUntil() originating from
+// AdjustRoundedDurationDays, with largestUnit equal to the largest unit in
+// the duration higher than "day".
+// Additionally one call in BalanceDateDurationRelative with the same
+// largestUnit.
+// Other calls have largestUnit: "day" so the difference is taken in ISO
+// calendar space.
+
+const durations = [
+ [1, 0, 0, 0, 0, 0, 0, 0, 0, 86399_999_999_999],
+ [0, 1, 0, 0, 0, 0, 0, 0, 0, 86399_999_999_999],
+ [0, 0, 1, 0, 0, 0, 0, 0, 0, 86399_999_999_999],
+ [0, 0, 0, 0, 0, 0, 0, 0, 0, 86399_999_999_999],
+ [0, 0, 0, 0, 0, 0, 0, 0, 0, 86399_999_999_999],
+ [0, 0, 0, 0, 0, 0, 0, 0, 0, 86399_999_999_999],
+ [0, 0, 0, 0, 0, 0, 0, 0, 0, 86399_999_999_999],
+ [0, 0, 0, 0, 0, 0, 0, 0, 0, 86399_999_999_999],
+ [0, 0, 0, 0, 0, 0, 0, 0, 0, 86399_999_999_999],
+ [0, 0, 0, 0, 0, 0, 0, 0, 0, 86399_999_999_999],
+].map((args) => new Temporal.Duration(...args));
+
+TemporalHelpers.checkCalendarDateUntilLargestUnitSingular(
+ (calendar, largestUnit, index) => {
+ const duration = durations[index];
+ const relativeTo = new Temporal.ZonedDateTime(0n, "UTC", calendar);
+ duration.round({ largestUnit, roundingIncrement: 2, roundingMode: 'ceil', relativeTo });
+ },
+ {
+ years: ["year", "year"],
+ months: ["month", "month"],
+ weeks: ["week", "week"],
+ days: [],
+ hours: [],
+ minutes: [],
+ seconds: [],
+ milliseconds: [],
+ microseconds: [],
+ nanoseconds: []
+ }
+);
+
+// Check the path that converts months to years and vice versa in
+// BalanceDurationRelative and UnbalanceDurationRelative.
+
+TemporalHelpers.checkCalendarDateUntilLargestUnitSingular(
+ (calendar, largestUnit) => {
+ const duration = new Temporal.Duration(5, 60);
+ const relativeTo = new Temporal.PlainDateTime(2000, 5, 2, 0, 0, 0, 0, 0, 0, calendar);
+ duration.round({ largestUnit, relativeTo });
+ },
+ {
+ years: ["year"],
+ months: ["month", "month"],
+ weeks: ["week"],
+ days: [],
+ hours: [],
+ minutes: [],
+ seconds: [],
+ milliseconds: [],
+ microseconds: [],
+ nanoseconds: []
+ }
+);
+
+// Check the paths that call dateUntil() in RoundDuration. These paths do not
+// call dateUntil() in AdjustRoundedDurationDays. Note that there is no
+// largestUnit: "month" call in BalanceDurationRelative, because the durations
+// have rounded down to 0.
+
+TemporalHelpers.checkCalendarDateUntilLargestUnitSingular(
+ (calendar, largestUnit) => {
+ const duration = new Temporal.Duration(0, 1, 0, 0, 1, 1, 1, 1, 1, 1);
+ const relativeTo = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar);
+ duration.round({ largestUnit, smallestUnit: largestUnit, relativeTo });
+ }, {
+ years: ["year"],
+ months: ["month"],
+ weeks: ["week", "week"],
+ days: []
+ }
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/calendar-fields-iterable.js
new file mode 100644
index 0000000000..0c7b4a6f43
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/calendar-fields-iterable.js
@@ -0,0 +1,34 @@
+// |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.duration.prototype.round
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.duration.prototype.round step 19:
+ 19. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_).
+ sec-temporal-torelativetemporalobject step 4.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+const duration = new Temporal.Duration(1, 1, 1, 1, 1, 1, 1);
+duration.round({ smallestUnit: 'months', relativeTo: { year: 2000, month: 1, day: 1, calendar } });
+
+assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/calendar-possibly-required.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/calendar-possibly-required.js
new file mode 100644
index 0000000000..50a6ab43ee
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/calendar-possibly-required.js
@@ -0,0 +1,65 @@
+// |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.duration.prototype.round
+description: relativeTo argument required if days = 0 but years/months/weeks non-zero
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+const duration1 = new Temporal.Duration(1);
+const duration2 = new Temporal.Duration(0, 12);
+const duration3 = new Temporal.Duration(0, 0, 5);
+const duration4 = new Temporal.Duration(0, 0, 0, 42);
+const pd = new Temporal.PlainDate(2021, 12, 15);
+const relativeToYears = { relativeTo: pd, largestUnit: "years" };
+const relativeToMonths = { relativeTo: pd, largestUnit: "months" };
+const relativeToWeeks = { relativeTo: pd, largestUnit: "weeks" };
+const relativeToDays = { relativeTo: pd, largestUnit: "days" };
+
+assert.throws(
+ RangeError,
+ () => { duration1.round({ relativeTo: pd }); },
+ "rounding a year Duration fails without largest/smallest unit"
+);
+
+TemporalHelpers.assertDuration(duration1.round(relativeToYears), 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "round year duration to years");
+TemporalHelpers.assertDuration(duration1.round(relativeToMonths), 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, "round year duration to months");
+TemporalHelpers.assertDuration(duration1.round(relativeToWeeks), 0, 0, 52, 1, 0, 0, 0, 0, 0, 0, "round year duration to weeks");
+TemporalHelpers.assertDuration(duration1.round(relativeToDays), 0, 0, 0, 365, 0, 0, 0, 0, 0, 0, "round year duration to days");
+
+assert.throws(
+ RangeError,
+ () => { duration2.round({ relativeTo: pd }); },
+ "rounding a month Duration fails without largest/smallest unit"
+);
+
+TemporalHelpers.assertDuration(duration2.round(relativeToYears), 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "round month duration to years");
+TemporalHelpers.assertDuration(duration2.round(relativeToMonths), 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, "round month duration to months");
+TemporalHelpers.assertDuration(duration2.round(relativeToWeeks), 0, 0, 52, 1, 0, 0, 0, 0, 0, 0, "round month duration to weeks");
+TemporalHelpers.assertDuration(duration2.round(relativeToDays), 0, 0, 0, 365, 0, 0, 0, 0, 0, 0, "round month duration to days");
+
+assert.throws(
+ RangeError,
+ () => { duration3.round({ relativeTo: pd }); },
+ "rounding a week Duration fails without largest/smallest unit"
+);
+
+TemporalHelpers.assertDuration(duration3.round(relativeToYears), 0, 1, 0, 4, 0, 0, 0, 0, 0, 0, "round week duration to years");
+TemporalHelpers.assertDuration(duration3.round(relativeToMonths), 0, 1, 0, 4, 0, 0, 0, 0, 0, 0, "round week duration to months");
+TemporalHelpers.assertDuration(duration3.round(relativeToWeeks), 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, "round week duration to weeks");
+TemporalHelpers.assertDuration(duration3.round(relativeToDays), 0, 0, 0, 35, 0, 0, 0, 0, 0, 0, "round week duration to days");
+
+assert.throws(
+ RangeError,
+ () => { duration4.round({ relativeTo: pd }); },
+ "rounding a day Duration fails without largest/smallest unit"
+);
+
+TemporalHelpers.assertDuration(duration4.round(relativeToYears), 0, 1, 0, 11, 0, 0, 0, 0, 0, 0, "round day duration to years");
+TemporalHelpers.assertDuration(duration4.round(relativeToMonths), 0, 1, 0, 11, 0, 0, 0, 0, 0, 0, "round day duration to months");
+TemporalHelpers.assertDuration(duration4.round(relativeToWeeks), 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, "round day duration to weeks");
+TemporalHelpers.assertDuration(duration4.round(relativeToDays), 0, 0, 0, 42, 0, 0, 0, 0, 0, 0, "round day duration to days");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/calendar-temporal-object.js
new file mode 100644
index 0000000000..e008bac6d6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/calendar-temporal-object.js
@@ -0,0 +1,29 @@
+// |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.duration.prototype.round
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.duration.prototype.round step 19:
+ 19. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_).
+ sec-temporal-torelativetemporalobject step 4.b:
+ b. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const duration = new Temporal.Duration(1, 1, 1, 1, 1, 1, 1);
+ duration.round({ smallestUnit: 'months', relativeTo: { year: 2000, month: 1, day: 1, calendar: temporalObject } });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..46f7ebdd18
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/constructor-in-calendar-fields.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.duration.prototype.round
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const relativeTo = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar, timeZone: 'Europe/Paris' };
+const instance = new Temporal.Duration(1, 0, 0, 0, 24);
+
+assert.throws(RangeError, () => instance.round({ largestUnit: "years", relativeTo }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/date-and-time-durations-opposite-signs.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/date-and-time-durations-opposite-signs.js
new file mode 100644
index 0000000000..59ba14faa6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/date-and-time-durations-opposite-signs.js
@@ -0,0 +1,45 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2024 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.duration.prototype.round
+description: >
+ Rounding calculation can result in duration date and time components with
+ opposite signs
+info: |
+ AdjustRoundedDurationDays ( years, months, weeks, days, norm, increment, unit, roundingMode, zonedRelativeTo,
+ calendarRec, timeZoneRec, precalculatedPlainDateTime )
+ 11. Let _adjustedDateDuration_ be ? AddDuration(_years_, _months_, _weeks_, _days_, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ _direction_, 0, 0, 0, 0, 0, 0, *undefined*, _zonedRelativeTo_, _calendarRec_, _timeZoneRec_,
+ _precalculatedPlainDateTime_).
+ 12. Let _roundRecord_ be ! RoundDuration(0, 0, 0, 0, _oneDayLess_, _increment_, _unit_, _roundingMode_).
+ 13. Return ? CombineDateAndNormalizedTimeDuration(_adjustedDateDuration_,
+ _roundRecord_.[[NormalizedDuration]].[[NormalizedTime]]).
+features: [Temporal]
+---*/
+
+// Based on a test case by André Bargull
+
+const calendar = new class extends Temporal.Calendar {
+ #dateUntil = 0;
+
+ dateUntil(one, two, options) {
+ let result = super.dateUntil(one, two, options);
+ if (++this.#dateUntil === 2) {
+ result = result.negated();
+ }
+ return result;
+ }
+}("iso8601");
+
+const relativeTo = new Temporal.ZonedDateTime(0n, "UTC", calendar);
+
+let d = new Temporal.Duration(1, 0, 0, 10, 25);
+
+assert.throws(RangeError, () => d.round({
+ smallestUnit: "nanoseconds",
+ roundingIncrement: 5,
+ relativeTo,
+}));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/dateuntil-field.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/dateuntil-field.js
new file mode 100644
index 0000000000..310838d012
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/dateuntil-field.js
@@ -0,0 +1,41 @@
+// |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.duration.prototype.round
+description: >
+ When consulting calendar.dateUntil() to balance or unbalance a duration, no
+ properties are accessed on the result Duration
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+
+class CalendarDateUntilObservable extends Temporal.Calendar {
+ dateUntil(...args) {
+ actual.push("call dateUntil");
+ const returnValue = super.dateUntil(...args);
+ TemporalHelpers.observeProperty(actual, returnValue, "years", Infinity);
+ TemporalHelpers.observeProperty(actual, returnValue, "months", Infinity);
+ TemporalHelpers.observeProperty(actual, returnValue, "weeks", Infinity);
+ TemporalHelpers.observeProperty(actual, returnValue, "days", Infinity);
+ return returnValue;
+ }
+}
+
+const calendar = new CalendarDateUntilObservable("iso8601");
+const relativeTo = new Temporal.PlainDate(2018, 10, 12, calendar);
+
+const expected = [
+ "call dateUntil", // UnbalanceDateDurationRelative
+ "call dateUntil", // BalanceDateDurationRelative
+];
+
+const years = new Temporal.Duration(2);
+const result = years.round({ largestUnit: "months", relativeTo });
+TemporalHelpers.assertDuration(result, 0, 24, 0, 0, 0, 0, 0, 0, 0, 0, "result");
+assert.compareArray(actual, expected, "operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/duplicate-calendar-fields.js
new file mode 100644
index 0000000000..b8eb4afffc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/duplicate-calendar-fields.js
@@ -0,0 +1,19 @@
+// |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.duration.prototype.round
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['day'], ['month'], ['monthCode'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const relativeTo = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar, timeZone: 'Europe/Paris' };
+ const instance = new Temporal.Duration(1, 0, 0, 0, 24);
+
+ assert.throws(RangeError, () => instance.round({ largestUnit: "years", relativeTo }));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/duration-out-of-range-added-to-relativeto.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/duration-out-of-range-added-to-relativeto.js
new file mode 100644
index 0000000000..b564b27eb4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/duration-out-of-range-added-to-relativeto.js
@@ -0,0 +1,28 @@
+// |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.duration.prototype.round
+description: RangeError thrown when calendar part of duration added to relativeTo is out of range
+features: [Temporal]
+info: |
+ RoundDuration:
+ 8.k. Let _isoResult_ be ! AddISODate(_plainRelativeTo_.[[ISOYear]]. _plainRelativeTo_.[[ISOMonth]], _plainRelativeTo_.[[ISODay]], 0, 0, 0, truncate(_fractionalDays_), *"constrain"*).
+ l. Let _wholeDaysLater_ be ? CreateTemporalDate(_isoResult_.[[Year]], _isoResult_.[[Month]], _isoResult_.[[Day]], _calendar_).
+---*/
+
+// Based on a test case by André Bargull <andre.bargull@gmail.com>
+
+const instance = new Temporal.Duration(0, 0, 0, /* days = */ 500_000_000);
+const relativeTo = new Temporal.PlainDate(2000, 1, 1);
+assert.throws(RangeError, () => instance.round({relativeTo, smallestUnit: "years"}));
+assert.throws(RangeError, () => instance.round({relativeTo, smallestUnit: "months"}));
+assert.throws(RangeError, () => instance.round({relativeTo, smallestUnit: "weeks"}));
+
+const negInstance = new Temporal.Duration(0, 0, 0, /* days = */ -500_000_000);
+assert.throws(RangeError, () => negInstance.round({relativeTo, smallestUnit: "years"}));
+assert.throws(RangeError, () => negInstance.round({relativeTo, smallestUnit: "months"}));
+assert.throws(RangeError, () => negInstance.round({relativeTo, smallestUnit: "weeks"}));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/february-leap-year.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/february-leap-year.js
new file mode 100644
index 0000000000..7f56244690
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/february-leap-year.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.duration.prototype.round
+description: RangeError thrown when largestUnit option not one of the allowed string values
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+// Based on a test case by André Bargull <andre.bargull@gmail.com>
+
+// Note: One day after February in a leap year.
+const relativeTo = new Temporal.PlainDate(1972, 3, 1);
+
+const options = {
+ largestUnit: "years",
+ relativeTo,
+};
+
+const twoDaysLessThanFourYears = new Temporal.Duration(3, 11, 0, 27);
+TemporalHelpers.assertDuration(
+ twoDaysLessThanFourYears.round(options),
+ 3, 11, 0, 27, 0, 0, 0, 0, 0, 0,
+ "Two days less than four years starting in February in a leap year shouldn't balance up"
+);
+
+const oneDayLessThanFourYears = new Temporal.Duration(3, 11, 0, 28);
+TemporalHelpers.assertDuration(
+ oneDayLessThanFourYears.round(options),
+ 3, 11, 0, 28, 0, 0, 0, 0, 0, 0,
+ "One day less than four years starting in February in a leap year shouldn't balance up"
+);
+
+const fourYears = new Temporal.Duration(3, 11, 0, 29);
+TemporalHelpers.assertDuration(
+ fourYears.round(options),
+ 4, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "Four years starting in February in a leap year should balance up"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/largestunit-correct-rebalancing.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/largestunit-correct-rebalancing.js
new file mode 100644
index 0000000000..04dd7258b8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/largestunit-correct-rebalancing.js
@@ -0,0 +1,27 @@
+// |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.duration.prototype.round
+description: Balancing from hours or smaller to weeks or bigger happens correctly.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const day_duration = 100;
+
+const tests = [ ["days", { days: day_duration }],
+ ["hours", { hours: day_duration * 24 }],
+ ["minutes", { minutes: day_duration * 24 * 60 }],
+ ["seconds", { seconds: day_duration * 24 * 60 * 60 }],
+ ["milliseconds", { milliseconds: day_duration * 24 * 60 * 60 * 1000 }],
+ ["microseconds", { microseconds: day_duration * 24 * 60 * 60 * 1000 * 1000 }],
+ ["nanoseconds", { nanoseconds: day_duration * 24 * 60 * 60 * 1000 * 1000 * 1000 }]];
+
+for ([unit, duration_desc] of tests)
+ TemporalHelpers.assertDuration(Temporal.Duration.from(duration_desc).round({ relativeTo: '2023-02-21', largestUnit: 'month' }),
+ 0, 3, 0, 11, 0, 0, 0, 0, 0, 0, `rounding from ${unit}`);
+
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/largestunit-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/largestunit-invalid-string.js
new file mode 100644
index 0000000000..8c1073124f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/largestunit-invalid-string.js
@@ -0,0 +1,14 @@
+// |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.duration.prototype.round
+description: RangeError thrown when largestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(0, 0, 0, 0, 12, 34, 56, 123, 987, 500);
+assert.throws(RangeError, () => duration.round({ largestUnit: "other string" }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/largestunit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/largestunit-plurals-accepted.js
new file mode 100644
index 0000000000..1e26317c63
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/largestunit-plurals-accepted.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.duration.prototype.round
+description: Plural units are accepted as well for the largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+const relativeTo = new Temporal.PlainDate(2000, 1, 1);
+const validUnits = [
+ "year",
+ "month",
+ "week",
+ "day",
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+];
+TemporalHelpers.checkPluralUnitsAccepted((largestUnit) => duration.round({ largestUnit, relativeTo }), validUnits);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/largestunit-smallestunit-default.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/largestunit-smallestunit-default.js
new file mode 100644
index 0000000000..4a5009c63b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/largestunit-smallestunit-default.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.duration.prototype.round
+description: assumes a different default for largestUnit if smallestUnit is larger than the default
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const relativeTo = Temporal.PlainDate.from("2020-01-01");
+const almostYear = Temporal.Duration.from({ days: 364 });
+TemporalHelpers.assertDuration(almostYear.round({ smallestUnit: "years", relativeTo }),
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "years");
+const almostMonth = Temporal.Duration.from({ days: 27 });
+TemporalHelpers.assertDuration(almostMonth.round({ smallestUnit: "months", relativeTo }),
+ 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, "months");
+const almostWeek = Temporal.Duration.from({ days: 6 });
+TemporalHelpers.assertDuration(almostWeek.round({ smallestUnit: "weeks", relativeTo }),
+ 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, "weeks");
+const almostDay = Temporal.Duration.from({ seconds: 86399 });
+TemporalHelpers.assertDuration(almostDay.round({ smallestUnit: "days" }),
+ 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, "days");
+const almostHour = Temporal.Duration.from({ seconds: 3599 });
+TemporalHelpers.assertDuration(almostHour.round({ smallestUnit: "hours" }),
+ 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, "hours");
+const almostMinute = Temporal.Duration.from({ seconds: 59 });
+TemporalHelpers.assertDuration(almostMinute.round({ smallestUnit: "minutes" }),
+ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, "minutes");
+const almostSecond = Temporal.Duration.from({ nanoseconds: 999999999 });
+TemporalHelpers.assertDuration(almostSecond.round({ smallestUnit: "seconds" }),
+ 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, "seconds");
+const almostMillisecond = Temporal.Duration.from({ nanoseconds: 999999 });
+TemporalHelpers.assertDuration(almostMillisecond.round({ smallestUnit: "milliseconds" }),
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, "milliseconds");
+const almostMicrosecond = Temporal.Duration.from({ nanoseconds: 999 });
+TemporalHelpers.assertDuration(almostMicrosecond.round({ smallestUnit: "microseconds" }),
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, "microseconds");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/largestunit-smallestunit-mismatch.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/largestunit-smallestunit-mismatch.js
new file mode 100644
index 0000000000..f713e24d7f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/largestunit-smallestunit-mismatch.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.duration.prototype.round
+description: RangeError thrown when smallestUnit is larger than largestUnit
+features: [Temporal]
+---*/
+
+const d = new Temporal.Duration(5, 5, 5, 5, 5, 5, 5, 5, 5, 5);
+const relativeTo = Temporal.PlainDate.from('2020-01-01');
+const units = ["years", "months", "weeks", "days", "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, () => d.round({ largestUnit, smallestUnit, relativeTo }),
+ `${smallestUnit} > ${largestUnit}`);
+ }
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/largestunit-undefined.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/largestunit-undefined.js
new file mode 100644
index 0000000000..c14d9894b5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/largestunit-undefined.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.duration.prototype.round
+description: Fallback value for largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const duration1 = new Temporal.Duration(0, 0, 0, 0, 1, 120, 1, 123, 456, 789);
+const explicit1 = duration1.round({ largestUnit: undefined, smallestUnit: "nanosecond" });
+TemporalHelpers.assertDuration(explicit1, 0, 0, 0, 0, 3, 0, 1, 123, 456, 789, "default largestUnit is largest in input");
+const implicit1 = duration1.round({ smallestUnit: "nanosecond" });
+TemporalHelpers.assertDuration(implicit1, 0, 0, 0, 0, 3, 0, 1, 123, 456, 789, "default largestUnit is largest in input");
+
+const duration2 = new Temporal.Duration(0, 0, 0, 0, 0, 120, 1, 123, 456, 789);
+const explicit2 = duration2.round({ largestUnit: undefined, smallestUnit: "nanosecond" });
+TemporalHelpers.assertDuration(explicit2, 0, 0, 0, 0, 0, 120, 1, 123, 456, 789, "default largestUnit is largest in input");
+const implicit2 = duration2.round({ smallestUnit: "nanosecond" });
+TemporalHelpers.assertDuration(implicit2, 0, 0, 0, 0, 0, 120, 1, 123, 456, 789, "default largestUnit is largest in input");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/largestunit-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/largestunit-wrong-type.js
new file mode 100644
index 0000000000..340e343610
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/largestunit-wrong-type.js
@@ -0,0 +1,18 @@
+// |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.duration.prototype.round
+description: Type conversions for largestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(0, 0, 0, 0, 12, 34, 56, 123, 456, 789);
+TemporalHelpers.checkStringOptionWrongType("largestUnit", "minute",
+ (largestUnit) => duration.round({ largestUnit }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 754, 56, 123, 456, 789, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/length.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/length.js
new file mode 100644
index 0000000000..53f0969c65
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/length.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.duration.prototype.round
+description: Temporal.Duration.prototype.round.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.Duration.prototype.round, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/name.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/name.js
new file mode 100644
index 0000000000..c0fd4b609b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/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.duration.prototype.round
+description: Temporal.Duration.prototype.round.name is "round".
+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.Duration.prototype.round, "name", {
+ value: "round",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/normalized-time-duration-to-days-loop-arbitrarily.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/normalized-time-duration-to-days-loop-arbitrarily.js
new file mode 100644
index 0000000000..923d788246
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/normalized-time-duration-to-days-loop-arbitrarily.js
@@ -0,0 +1,79 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.duration.prototype.round
+description: >
+ NormalizedTimeDurationToDays can loop arbitrarily up to max safe integer
+info: |
+ NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ , precalculatedPlainDatetime ] )
+ ...
+ 21. Repeat, while done is false,
+ a. Let oneDayFarther be ? AddDaysToZonedDateTime(relativeResult.[[Instant]],
+ relativeResult.[[DateTime]], timeZoneRec, zonedRelativeTo.[[Calendar]], sign).
+ b. Set dayLengthNs to NormalizedTimeDurationFromEpochNanosecondsDifference(oneDayFarther.[[EpochNanoseconds]],
+ relativeResult.[[EpochNanoseconds]]).
+ c. Let oneDayLess be ? SubtractNormalizedTimeDuration(norm, dayLengthNs).
+ c. If NormalizedTimeDurationSign(oneDayLess) × sign ≥ 0, then
+ i. Set norm to oneDayLess.
+ ii. Set relativeResult to oneDayFarther.
+ iii. Set days to days + sign.
+ d. Else,
+ i. Set done to true.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calls = [];
+const duration = Temporal.Duration.from({ days: 1 });
+
+function createRelativeTo(count) {
+ const dayLengthNs = 86400000000000n;
+ const dayInstant = new Temporal.Instant(dayLengthNs);
+ const substitutions = [];
+ const timeZone = new Temporal.TimeZone("UTC");
+ // Return constant value for first _count_ calls
+ TemporalHelpers.substituteMethod(
+ timeZone,
+ "getPossibleInstantsFor",
+ substitutions
+ );
+ substitutions.length = count;
+ let i = 0;
+ for (i = 0; i < substitutions.length; i++) {
+ // (this value)
+ substitutions[i] = [dayInstant];
+ }
+ // Record calls in calls[]
+ TemporalHelpers.observeMethod(calls, timeZone, "getPossibleInstantsFor");
+ return new Temporal.ZonedDateTime(0n, timeZone);
+}
+
+let zdt = createRelativeTo(50);
+calls.splice(0); // Reset calls list after ZonedDateTime construction
+duration.round({
+ smallestUnit: "days",
+ relativeTo: zdt,
+});
+assert.sameValue(
+ calls.length,
+ 50 + 1,
+ "Expected duration.round to call getPossibleInstantsFor correct number of times"
+);
+
+zdt = createRelativeTo(100);
+calls.splice(0); // Reset calls list after previous loop + ZonedDateTime construction
+duration.round({
+ smallestUnit: "days",
+ relativeTo: zdt,
+});
+assert.sameValue(
+ calls.length,
+ 100 + 1,
+ "Expected duration.round to call getPossibleInstantsFor correct number of times"
+);
+
+zdt = createRelativeTo(107);
+assert.throws(RangeError, () => duration.round({ smallestUnit: "days", relativeTo: zdt }), "107-2 days > 2⁵³ ns");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/not-a-constructor.js
new file mode 100644
index 0000000000..1bccde9f8f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/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.duration.prototype.round
+description: >
+ Temporal.Duration.prototype.round 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.Duration.prototype.round();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Duration.prototype.round), false,
+ "isConstructor(Temporal.Duration.prototype.round)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/options-wrong-type.js
new file mode 100644
index 0000000000..0457cd4eb7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/options-wrong-type.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.duration.prototype.round
+description: TypeError thrown when options argument is missing or a non-string primitive
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const badOptions = [
+ undefined,
+ null,
+ true,
+ Symbol(),
+ 1,
+ 2n,
+];
+
+const instance = new Temporal.Duration(0, 0, 0, 0, 1);
+assert.throws(TypeError, () => instance.round(), "TypeError on missing options argument");
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.round(value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/order-of-operations.js
new file mode 100644
index 0000000000..29c587bafc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/order-of-operations.js
@@ -0,0 +1,458 @@
+// |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.duration.prototype.round
+description: Properties on objects passed to round() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "get options.largestUnit",
+ "get options.largestUnit.toString",
+ "call options.largestUnit.toString",
+ "get options.relativeTo",
+ "get options.roundingIncrement",
+ "get options.roundingIncrement.valueOf",
+ "call options.roundingIncrement.valueOf",
+ "get options.roundingMode",
+ "get options.roundingMode.toString",
+ "call options.roundingMode.toString",
+ "get options.smallestUnit",
+ "get options.smallestUnit.toString",
+ "call options.smallestUnit.toString",
+];
+const actual = [];
+
+function createOptionsObserver({ smallestUnit = "nanoseconds", largestUnit = "auto", roundingMode = "halfExpand", roundingIncrement = 1, relativeTo = undefined } = {}) {
+ return TemporalHelpers.propertyBagObserver(actual, {
+ smallestUnit,
+ largestUnit,
+ roundingMode,
+ roundingIncrement,
+ relativeTo,
+ }, "options");
+}
+
+const instance = new Temporal.Duration(0, 0, 0, 0, /* hours = */ 2400);
+
+// basic order of operations, without relativeTo:
+instance.round(createOptionsObserver({ smallestUnit: "microseconds" }));
+assert.compareArray(actual, expected, "order of operations");
+actual.splice(0); // clear
+
+const expectedOpsForPlainRelativeTo = [
+ "get options.largestUnit",
+ "get options.largestUnit.toString",
+ "call options.largestUnit.toString",
+ "get options.relativeTo",
+ "get options.relativeTo.calendar",
+ "has options.relativeTo.calendar.dateAdd",
+ "has options.relativeTo.calendar.dateFromFields",
+ "has options.relativeTo.calendar.dateUntil",
+ "has options.relativeTo.calendar.day",
+ "has options.relativeTo.calendar.dayOfWeek",
+ "has options.relativeTo.calendar.dayOfYear",
+ "has options.relativeTo.calendar.daysInMonth",
+ "has options.relativeTo.calendar.daysInWeek",
+ "has options.relativeTo.calendar.daysInYear",
+ "has options.relativeTo.calendar.fields",
+ "has options.relativeTo.calendar.id",
+ "has options.relativeTo.calendar.inLeapYear",
+ "has options.relativeTo.calendar.mergeFields",
+ "has options.relativeTo.calendar.month",
+ "has options.relativeTo.calendar.monthCode",
+ "has options.relativeTo.calendar.monthDayFromFields",
+ "has options.relativeTo.calendar.monthsInYear",
+ "has options.relativeTo.calendar.weekOfYear",
+ "has options.relativeTo.calendar.year",
+ "has options.relativeTo.calendar.yearMonthFromFields",
+ "has options.relativeTo.calendar.yearOfWeek",
+ "get options.relativeTo.calendar.dateFromFields",
+ "get options.relativeTo.calendar.fields",
+ "call options.relativeTo.calendar.fields",
+ "get options.relativeTo.day",
+ "get options.relativeTo.day.valueOf",
+ "call options.relativeTo.day.valueOf",
+ "get options.relativeTo.hour",
+ "get options.relativeTo.microsecond",
+ "get options.relativeTo.millisecond",
+ "get options.relativeTo.minute",
+ "get options.relativeTo.month",
+ "get options.relativeTo.month.valueOf",
+ "call options.relativeTo.month.valueOf",
+ "get options.relativeTo.monthCode",
+ "get options.relativeTo.monthCode.toString",
+ "call options.relativeTo.monthCode.toString",
+ "get options.relativeTo.nanosecond",
+ "get options.relativeTo.offset",
+ "get options.relativeTo.second",
+ "get options.relativeTo.timeZone",
+ "get options.relativeTo.year",
+ "get options.relativeTo.year.valueOf",
+ "call options.relativeTo.year.valueOf",
+ "call options.relativeTo.calendar.dateFromFields",
+ "get options.roundingIncrement",
+ "get options.roundingIncrement.valueOf",
+ "call options.roundingIncrement.valueOf",
+ "get options.roundingMode",
+ "get options.roundingMode.toString",
+ "call options.roundingMode.toString",
+ "get options.smallestUnit",
+ "get options.smallestUnit.toString",
+ "call options.smallestUnit.toString",
+ // lookup in Duration.p.round
+ "get options.relativeTo.calendar.dateAdd",
+ "get options.relativeTo.calendar.dateUntil",
+];
+
+const plainRelativeTo = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2001,
+ month: 5,
+ monthCode: "M05",
+ day: 2,
+ calendar: TemporalHelpers.calendarObserver(actual, "options.relativeTo.calendar"),
+}, "options.relativeTo");
+
+// basic order of observable operations, without rounding:
+instance.round(createOptionsObserver({ relativeTo: plainRelativeTo }));
+assert.compareArray(actual, expectedOpsForPlainRelativeTo, "order of operations for PlainDate relativeTo");
+actual.splice(0); // clear
+
+// code path through RoundDuration that rounds to the nearest year, with minimal calendar calls:
+const expectedMinimalOpsForYearRounding = expectedOpsForPlainRelativeTo.concat([
+ // 7.e and 7.g not called because years, months, weeks are 0
+ "call options.relativeTo.calendar.dateUntil", // 7.o
+ // 7.s not called because years, months, weeks are 0
+ "call options.relativeTo.calendar.dateAdd", // 7.y MoveRelativeDate
+]);
+instance.round(createOptionsObserver({ smallestUnit: "years", relativeTo: plainRelativeTo }));
+assert.compareArray(actual, expectedMinimalOpsForYearRounding, "order of operations with years = 0 and smallestUnit = years");
+actual.splice(0); // clear
+
+// code path through RoundDuration that rounds to the nearest year:
+const expectedOpsForYearRounding = expectedOpsForPlainRelativeTo.concat([
+ "call options.relativeTo.calendar.dateAdd", // 12.d
+ "call options.relativeTo.calendar.dateAdd", // 12.f
+ "call options.relativeTo.calendar.dateUntil", // 12.n
+ "call options.relativeTo.calendar.dateAdd", // 12.r MoveRelativeDate
+ "call options.relativeTo.calendar.dateAdd", // 12.x MoveRelativeDate
+ // BalanceDateDurationRelative
+ "call options.relativeTo.calendar.dateAdd", // 9.c
+ "call options.relativeTo.calendar.dateUntil", // 9.d
+]);
+const instanceYears = new Temporal.Duration(1, 12, 0, 0, /* hours = */ 2400);
+instanceYears.round(createOptionsObserver({ smallestUnit: "years", relativeTo: plainRelativeTo }));
+assert.compareArray(actual, expectedOpsForYearRounding, "order of operations with smallestUnit = years");
+actual.splice(0); // clear
+
+// code path through Duration.prototype.round that rounds to the nearest month:
+const expectedOpsForMonthRounding = expectedOpsForPlainRelativeTo.concat([
+ // UnbalanceDateDurationRelative
+ "call options.relativeTo.calendar.dateAdd", // 3.f
+ "call options.relativeTo.calendar.dateUntil", // 3.i
+ // RoundDuration
+ "call options.relativeTo.calendar.dateAdd", // 13.c
+ "call options.relativeTo.calendar.dateAdd", // 13.e
+ "call options.relativeTo.calendar.dateUntil", // 13.m
+ "call options.relativeTo.calendar.dateAdd", // 13.q MoveRelativeDate
+ "call options.relativeTo.calendar.dateAdd", // 13.w MoveRelativeDate
+ // BalanceDateDurationRelative
+ "call options.relativeTo.calendar.dateAdd", // 10.d
+ "call options.relativeTo.calendar.dateUntil", // 10.e
+]);
+const instance2 = new Temporal.Duration(1, 0, 0, 62);
+instance2.round(createOptionsObserver({ largestUnit: "months", smallestUnit: "months", relativeTo: plainRelativeTo }));
+assert.compareArray(actual, expectedOpsForMonthRounding, "order of operations with largestUnit = smallestUnit = months");
+actual.splice(0); // clear
+
+// code path through Duration.prototype.round that rounds to the nearest week:
+const expectedOpsForWeekRounding = expectedOpsForPlainRelativeTo.concat([
+ // UnbalanceDateDurationRelative
+ "call options.relativeTo.calendar.dateAdd", // 4.e
+ // RoundDuration
+ "call options.relativeTo.calendar.dateUntil", // 14.f
+ "call options.relativeTo.calendar.dateAdd", // 14.j MoveRelativeDate
+ "call options.relativeTo.calendar.dateAdd", // 14.p MoveRelativeDate
+ // BalanceDateDurationRelative
+ "call options.relativeTo.calendar.dateAdd", // 16
+ "call options.relativeTo.calendar.dateUntil", // 17
+]);
+const instance3 = new Temporal.Duration(1, 1, 0, 15);
+instance3.round(createOptionsObserver({ largestUnit: "weeks", smallestUnit: "weeks", relativeTo: plainRelativeTo }));
+assert.compareArray(actual, expectedOpsForWeekRounding, "order of operations with largestUnit = smallestUnit = weeks");
+actual.splice(0); // clear
+
+// code path through UnbalanceDurationRelative that rounds to the nearest day:
+const expectedOpsForDayRounding = expectedOpsForPlainRelativeTo.concat([
+ "call options.relativeTo.calendar.dateAdd", // 9
+]);
+const instance4 = new Temporal.Duration(1, 1, 1)
+instance4.round(createOptionsObserver({ largestUnit: "days", smallestUnit: "days", relativeTo: plainRelativeTo }));
+assert.compareArray(actual, expectedOpsForDayRounding, "order of operations with largestUnit = smallestUnit = days");
+actual.splice(0); // clear
+
+// code path through BalanceDateDurationRelative balancing from days up to years:
+const expectedOpsForDayToYearBalancing = expectedOpsForPlainRelativeTo.concat([
+ // BalanceDateDurationRelative
+ "call options.relativeTo.calendar.dateUntil", // 9.d
+]);
+const instance5 = new Temporal.Duration(0, 0, 0, 0, /* hours = */ 396 * 24);
+instance5.round(createOptionsObserver({ largestUnit: "years", smallestUnit: "days", relativeTo: plainRelativeTo }));
+assert.compareArray(actual, expectedOpsForDayToYearBalancing, "order of operations with largestUnit = years, smallestUnit = days");
+actual.splice(0); // clear
+
+// code path through Duration.prototype.round balancing from months up to years:
+const expectedOpsForMonthToYearBalancing = expectedOpsForPlainRelativeTo.concat([
+ // RoundDuration
+ "call options.relativeTo.calendar.dateAdd", // 13.c
+ "call options.relativeTo.calendar.dateAdd", // 13.e
+ "call options.relativeTo.calendar.dateAdd", // 13.w MoveRelativeDate
+ // BalanceDateDurationRelative
+ "call options.relativeTo.calendar.dateAdd", // 9.c
+ "call options.relativeTo.calendar.dateUntil", // 9.d
+]);
+const instance6 = new Temporal.Duration(0, 12);
+instance6.round(createOptionsObserver({ largestUnit: "years", smallestUnit: "months", relativeTo: plainRelativeTo }));
+assert.compareArray(actual, expectedOpsForMonthToYearBalancing, "order of operations with largestUnit = years, smallestUnit = months");
+actual.splice(0); // clear
+
+const expectedOpsForDayToMonthBalancing = expectedOpsForPlainRelativeTo.concat([
+ // BalanceDateDurationRelative
+ "call options.relativeTo.calendar.dateUntil", // 10.e
+]);
+const instance7 = new Temporal.Duration(0, 0, 0, 0, /* hours = */ 32 * 24);
+instance7.round(createOptionsObserver({ largestUnit: "months", smallestUnit: "days", relativeTo: plainRelativeTo }));
+assert.compareArray(actual, expectedOpsForDayToMonthBalancing, "order of operations with largestUnit = months, smallestUnit = days");
+actual.splice(0); // clear
+
+const expectedOpsForDayToWeekBalancing = expectedOpsForPlainRelativeTo.concat([
+ // BalanceDateDurationRelative
+ "call options.relativeTo.calendar.dateUntil", // 17
+]);
+const instance8 = new Temporal.Duration(0, 0, 0, 0, /* hours = */ 8 * 24);
+instance8.round(createOptionsObserver({ largestUnit: "weeks", smallestUnit: "days", relativeTo: plainRelativeTo }));
+assert.compareArray(actual, expectedOpsForDayToWeekBalancing, "order of operations with largestUnit = weeks, smallestUnit = days");
+actual.splice(0); // clear
+
+const expectedOpsForZonedRelativeTo = [
+ "get options.largestUnit",
+ "get options.largestUnit.toString",
+ "call options.largestUnit.toString",
+ "get options.relativeTo",
+ "get options.relativeTo.calendar",
+ "has options.relativeTo.calendar.dateAdd",
+ "has options.relativeTo.calendar.dateFromFields",
+ "has options.relativeTo.calendar.dateUntil",
+ "has options.relativeTo.calendar.day",
+ "has options.relativeTo.calendar.dayOfWeek",
+ "has options.relativeTo.calendar.dayOfYear",
+ "has options.relativeTo.calendar.daysInMonth",
+ "has options.relativeTo.calendar.daysInWeek",
+ "has options.relativeTo.calendar.daysInYear",
+ "has options.relativeTo.calendar.fields",
+ "has options.relativeTo.calendar.id",
+ "has options.relativeTo.calendar.inLeapYear",
+ "has options.relativeTo.calendar.mergeFields",
+ "has options.relativeTo.calendar.month",
+ "has options.relativeTo.calendar.monthCode",
+ "has options.relativeTo.calendar.monthDayFromFields",
+ "has options.relativeTo.calendar.monthsInYear",
+ "has options.relativeTo.calendar.weekOfYear",
+ "has options.relativeTo.calendar.year",
+ "has options.relativeTo.calendar.yearMonthFromFields",
+ "has options.relativeTo.calendar.yearOfWeek",
+ "get options.relativeTo.calendar.dateFromFields",
+ "get options.relativeTo.calendar.fields",
+ "call options.relativeTo.calendar.fields",
+ "get options.relativeTo.day",
+ "get options.relativeTo.day.valueOf",
+ "call options.relativeTo.day.valueOf",
+ "get options.relativeTo.hour",
+ "get options.relativeTo.hour.valueOf",
+ "call options.relativeTo.hour.valueOf",
+ "get options.relativeTo.microsecond",
+ "get options.relativeTo.microsecond.valueOf",
+ "call options.relativeTo.microsecond.valueOf",
+ "get options.relativeTo.millisecond",
+ "get options.relativeTo.millisecond.valueOf",
+ "call options.relativeTo.millisecond.valueOf",
+ "get options.relativeTo.minute",
+ "get options.relativeTo.minute.valueOf",
+ "call options.relativeTo.minute.valueOf",
+ "get options.relativeTo.month",
+ "get options.relativeTo.month.valueOf",
+ "call options.relativeTo.month.valueOf",
+ "get options.relativeTo.monthCode",
+ "get options.relativeTo.monthCode.toString",
+ "call options.relativeTo.monthCode.toString",
+ "get options.relativeTo.nanosecond",
+ "get options.relativeTo.nanosecond.valueOf",
+ "call options.relativeTo.nanosecond.valueOf",
+ "get options.relativeTo.offset",
+ "get options.relativeTo.offset.toString",
+ "call options.relativeTo.offset.toString",
+ "get options.relativeTo.second",
+ "get options.relativeTo.second.valueOf",
+ "call options.relativeTo.second.valueOf",
+ "get options.relativeTo.timeZone",
+ "get options.relativeTo.year",
+ "get options.relativeTo.year.valueOf",
+ "call options.relativeTo.year.valueOf",
+ "call options.relativeTo.calendar.dateFromFields",
+ "has options.relativeTo.timeZone.getOffsetNanosecondsFor",
+ "has options.relativeTo.timeZone.getPossibleInstantsFor",
+ "has options.relativeTo.timeZone.id",
+ "get options.relativeTo.timeZone.getOffsetNanosecondsFor",
+ "get options.relativeTo.timeZone.getPossibleInstantsFor",
+ // InterpretISODateTimeOffset
+ "call options.relativeTo.timeZone.getPossibleInstantsFor",
+ "call options.relativeTo.timeZone.getOffsetNanosecondsFor",
+ "get options.roundingIncrement",
+ "get options.roundingIncrement.valueOf",
+ "call options.roundingIncrement.valueOf",
+ "get options.roundingMode",
+ "get options.roundingMode.toString",
+ "call options.roundingMode.toString",
+ "get options.smallestUnit",
+ "get options.smallestUnit.toString",
+ "call options.smallestUnit.toString",
+];
+
+const zonedRelativeTo = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2001,
+ month: 5,
+ monthCode: "M05",
+ day: 2,
+ hour: 6,
+ minute: 54,
+ second: 32,
+ millisecond: 987,
+ microsecond: 654,
+ nanosecond: 321,
+ offset: "+00:00",
+ calendar: TemporalHelpers.calendarObserver(actual, "options.relativeTo.calendar"),
+ timeZone: TemporalHelpers.timeZoneObserver(actual, "options.relativeTo.timeZone"),
+}, "options.relativeTo");
+
+// basic order of operations with ZonedDateTime relativeTo:
+instance.round(createOptionsObserver({ relativeTo: zonedRelativeTo }));
+assert.compareArray(actual, expectedOpsForZonedRelativeTo.concat([
+ "get options.relativeTo.calendar.dateAdd",
+ "get options.relativeTo.calendar.dateUntil",
+]), "order of operations for ZonedDateTime relativeTo");
+actual.splice(0); // clear
+
+// code path through RoundDuration that rounds to the nearest year with minimal calendar operations:
+const expectedOpsForMinimalYearRoundingZoned = expectedOpsForZonedRelativeTo.concat([
+ // ToTemporalDate
+ "call options.relativeTo.timeZone.getOffsetNanosecondsFor",
+ // lookup in Duration.p.round
+ "get options.relativeTo.calendar.dateAdd",
+ "get options.relativeTo.calendar.dateUntil",
+ // NanosecondsToDays
+ "call options.relativeTo.timeZone.getOffsetNanosecondsFor", // 7. GetPlainDateTimeFor
+ "call options.relativeTo.timeZone.getOffsetNanosecondsFor", // 11. GetPlainDateTimeFor
+ // NanosecondsToDays → AddDaysToZonedDateTime
+ "call options.relativeTo.timeZone.getPossibleInstantsFor",
+ // NanosecondsToDays → AddDaysToZonedDateTime
+ "call options.relativeTo.timeZone.getPossibleInstantsFor",
+ // RoundDuration
+ // 7.e and 7.g not called because years, months, weeks are 0
+ "call options.relativeTo.calendar.dateUntil", // 7.o
+ // 7.s not called because years, months, weeks are 0
+ "call options.relativeTo.calendar.dateAdd", // 7.y MoveRelativeDate
+]);
+instance.round(createOptionsObserver({ smallestUnit: "years", relativeTo: zonedRelativeTo }));
+assert.compareArray(
+ actual,
+ expectedOpsForMinimalYearRoundingZoned,
+ "order of operations with years = 0, smallestUnit = years and ZonedDateTime relativeTo"
+);
+actual.splice(0); // clear
+
+// code path through RoundDuration that rounds to the nearest year:
+const expectedOpsForYearRoundingZoned = expectedOpsForZonedRelativeTo.concat([
+ // ToTemporalDate
+ "call options.relativeTo.timeZone.getOffsetNanosecondsFor",
+ // lookup in Duration.p.round
+ "get options.relativeTo.calendar.dateAdd",
+ "get options.relativeTo.calendar.dateUntil",
+ // MoveRelativeZonedDateTime → AddZonedDateTime
+ "call options.relativeTo.calendar.dateAdd",
+ "call options.relativeTo.timeZone.getPossibleInstantsFor",
+ // NanosecondsToDays
+ "call options.relativeTo.timeZone.getOffsetNanosecondsFor", // 8. GetPlainDateTimeFor
+ "call options.relativeTo.timeZone.getOffsetNanosecondsFor", // 9. GetPlainDateTimeFor
+ // NanosecondsToDays → AddDaysToZonedDateTime
+ "call options.relativeTo.timeZone.getPossibleInstantsFor",
+ // NanosecondsToDays → AddDaysToZonedDateTime
+ "call options.relativeTo.timeZone.getPossibleInstantsFor",
+ "call options.relativeTo.calendar.dateAdd", // 12.d
+ "call options.relativeTo.calendar.dateAdd", // 12.f
+ "call options.relativeTo.calendar.dateUntil", // 12.n
+ "call options.relativeTo.calendar.dateAdd", // 12.r MoveRelativeDate
+ "call options.relativeTo.calendar.dateAdd", // 12.x MoveRelativeDate
+ // BalanceDateDurationRelative
+ "call options.relativeTo.calendar.dateAdd", // 9.c
+ "call options.relativeTo.calendar.dateUntil", // 9.d
+]);
+instanceYears.round(createOptionsObserver({ smallestUnit: "years", relativeTo: zonedRelativeTo }));
+assert.compareArray(
+ actual,
+ expectedOpsForYearRoundingZoned,
+ "order of operations with smallestUnit = years and ZonedDateTime relativeTo"
+);
+actual.splice(0); // clear
+
+// code path that hits UnbalanceDateDurationRelative, RoundDuration, and
+// BalanceDateDurationRelative
+const expectedOpsForUnbalanceRoundBalance = expectedOpsForZonedRelativeTo.concat([
+ // ToTemporalDate
+ "call options.relativeTo.timeZone.getOffsetNanosecondsFor",
+ // lookup in Duration.p.round
+ "get options.relativeTo.calendar.dateAdd",
+ "get options.relativeTo.calendar.dateUntil",
+ // No user code calls in UnbalanceDateDurationRelative
+ // RoundDuration → MoveRelativeZonedDateTime → AddZonedDateTime
+ "call options.relativeTo.calendar.dateAdd",
+ "call options.relativeTo.timeZone.getPossibleInstantsFor", // 13. GetInstantFor
+ // RoundDuration
+ "call options.relativeTo.calendar.dateAdd", // 14.p MoveRelativeDate
+ // BalanceDateDurationRelative
+ "call options.relativeTo.calendar.dateAdd", // 10.d
+ "call options.relativeTo.calendar.dateUntil", // 10.e
+]);
+new Temporal.Duration(0, 1, 1).round(createOptionsObserver({ largestUnit: "years", smallestUnit: "weeks", relativeTo: zonedRelativeTo }));
+assert.compareArray(
+ actual,
+ expectedOpsForUnbalanceRoundBalance,
+ "order of operations with largestUnit = years, smallestUnit = weeks, and ZonedDateTime relativeTo"
+);
+actual.splice(0); // clear
+
+// code path that skips user code calls in BalanceDateDurationRelative due to
+// special case for largestUnit months and smallestUnit weeks
+const expectedOpsForWeeksSpecialCase = expectedOpsForZonedRelativeTo.concat([
+ // ToTemporalDate
+ "call options.relativeTo.timeZone.getOffsetNanosecondsFor",
+ // lookup in Duration.p.round
+ "get options.relativeTo.calendar.dateAdd",
+ "get options.relativeTo.calendar.dateUntil",
+ // RoundDuration → MoveRelativeZonedDateTime → AddZonedDateTime
+ "call options.relativeTo.calendar.dateAdd",
+ "call options.relativeTo.timeZone.getPossibleInstantsFor", // 13. GetInstantFor
+ // RoundDuration
+ "call options.relativeTo.calendar.dateAdd", // 14.p MoveRelativeDate
+]);
+new Temporal.Duration(0, 1, 1).round(createOptionsObserver({ largestUnit: "months", smallestUnit: "weeks", relativeTo: zonedRelativeTo }));
+assert.compareArray(
+ actual,
+ expectedOpsForWeeksSpecialCase,
+ "order of operations with largestUnit = months, smallestUnit = weeks, and ZonedDateTime relativeTo"
+);
+actual.splice(0); // clear
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/out-of-range-when-converting-from-normalized-duration.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/out-of-range-when-converting-from-normalized-duration.js
new file mode 100644
index 0000000000..4fde761351
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/out-of-range-when-converting-from-normalized-duration.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2024 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.round
+description: >
+ When converting the result from normalized duration form, each duration
+ component is turned into a float64-representable integer
+features: [Temporal]
+---*/
+
+const d = new Temporal.Duration(0, 0, 0, 0, 0, 0, /* s = */ Number.MAX_SAFE_INTEGER, 0, 0, /* ns = */ 999_999_999);
+assert.throws(RangeError, () => d.round({
+ largestUnit: "nanoseconds",
+ roundingIncrement: 1,
+}), "nanoseconds component is an unsafe integer after balancing");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/precision-exact-in-balance-time-duration.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/precision-exact-in-balance-time-duration.js
new file mode 100644
index 0000000000..6b1d0fa743
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/precision-exact-in-balance-time-duration.js
@@ -0,0 +1,69 @@
+// |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.duration.prototype.round
+description: BalanceTimeDuration computes on exact mathematical values.
+includes: [temporalHelpers.js]
+features: [BigInt, Temporal]
+---*/
+
+const seconds = 8692288669465520;
+
+{
+ const milliseconds = 513;
+ const d = new Temporal.Duration(0, 0, 0, 0, 0, 0, seconds, milliseconds);
+
+ const result = d.round({ largestUnit: "milliseconds" });
+
+ // The result should be the nearest Number value to 8692288669465520512
+ const expectedMilliseconds = Number(BigInt(seconds) * 1000n + BigInt(milliseconds));
+ assert.sameValue(expectedMilliseconds, 8692288669465520_513, "check expected value (ms)");
+
+ TemporalHelpers.assertDuration(result,
+ 0, 0, 0, 0,
+ 0, 0, 0,
+ expectedMilliseconds, 0, 0,
+ "BalanceTimeDuration should implement floating-point calculation correctly for largestUnit milliseconds"
+ );
+}
+
+{
+ const microseconds = 373761;
+ const d = new Temporal.Duration(0, 0, 0, 0, 0, 0, seconds, 0, microseconds);
+
+ const result = d.round({ largestUnit: "microseconds" });
+
+ // The result should be the nearest Number value to 8692288669465520373761
+ const expectedMicroseconds = Number(BigInt(seconds) * 1_000_000n + BigInt(microseconds));
+ assert.sameValue(expectedMicroseconds, 8692288669465520_373_761, "check expected value (µs)");
+
+ TemporalHelpers.assertDuration(result,
+ 0, 0, 0, 0,
+ 0, 0, 0,
+ 0, expectedMicroseconds, 0,
+ "BalanceTimeDuration should implement floating-point calculation correctly for largestUnit milliseconds"
+ );
+}
+
+
+{
+ const nanoseconds = 321_414_345;
+ const d = new Temporal.Duration(0, 0, 0, 0, 0, 0, seconds, 0, 0, nanoseconds);
+
+ const result = d.round({ largestUnit: "nanoseconds" });
+
+ // The result should be the nearest Number value to 8692288669465520321414345
+ const expectedNanoseconds = Number(BigInt(seconds) * 1_000_000_000n + BigInt(nanoseconds));
+ assert.sameValue(expectedNanoseconds, 8692288669465520_321_414_345, "check expected value (ns)");
+
+ TemporalHelpers.assertDuration(result,
+ 0, 0, 0, 0,
+ 0, 0, 0,
+ 0, 0, expectedNanoseconds,
+ "BalanceTimeDuration should implement floating-point calculation correctly for largestUnit nanoseconds"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/precision-exact-in-round-duration.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/precision-exact-in-round-duration.js
new file mode 100644
index 0000000000..97cc8d5685
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/precision-exact-in-round-duration.js
@@ -0,0 +1,53 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.round
+description: >
+ RoundDuration computes on exact mathematical values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+{
+ let duration = Temporal.Duration.from({
+ hours: 100_000,
+ nanoseconds: 5,
+ });
+
+ let rounded = duration.round({smallestUnit: "hours", roundingMode: "ceil"});
+
+ // If RoundDuration() was implemented with float64, precision loss would lead
+ // to computing an incorrect result.
+ //
+ // "PT100000H" with float64, but "PT100001H" with exact mathematical values.
+ TemporalHelpers.assertDuration(
+ rounded,
+ 0, 0, 0, 0,
+ 100001, 0, 0,
+ 0, 0, 0,
+ );
+}
+
+{
+ let duration = Temporal.Duration.from({
+ days: 1000,
+ nanoseconds: 5,
+ });
+
+ let rounded = duration.round({smallestUnit: "days", roundingMode: "ceil"});
+
+ // If RoundDuration() was implemented with float64, precision loss would lead
+ // to computing an incorrect result.
+ //
+ // "P1000D" with float64, but "P1001D" with exact mathematical values.
+ TemporalHelpers.assertDuration(
+ rounded,
+ 0, 0, 0, 1001,
+ 0, 0, 0,
+ 0, 0, 0,
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/prop-desc.js
new file mode 100644
index 0000000000..93018632c6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/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.duration.prototype.round
+description: The "round" property of Temporal.Duration.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Duration.prototype.round,
+ "function",
+ "`typeof Duration.prototype.round` is `function`"
+);
+
+verifyProperty(Temporal.Duration.prototype, "round", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/proto-in-calendar-fields.js
new file mode 100644
index 0000000000..5fd58bd3d1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/proto-in-calendar-fields.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.duration.prototype.round
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const relativeTo = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar, timeZone: 'Europe/Paris' };
+const instance = new Temporal.Duration(1, 0, 0, 0, 24);
+
+assert.throws(RangeError, () => instance.round({ largestUnit: "years", relativeTo }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/read-time-fields-before-datefromfields.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/read-time-fields-before-datefromfields.js
new file mode 100644
index 0000000000..f5bdede169
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/read-time-fields-before-datefromfields.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.duration.prototype.round
+description: The time fields are read from the object before being passed to dateFromFields().
+info: |
+ sec-temporal.duration.prototype.round step 19:
+ 19. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_).
+ sec-temporal-torelativetemporalobject step 4.g:
+ g. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+ sec-temporal-interprettemporaldatetimefields steps 1–2:
+ 1. Let _timeResult_ be ? ToTemporalTimeRecord(_fields_).
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarMakeInvalidGettersTime();
+const duration = new Temporal.Duration(1, 1, 1, 1, 1, 1, 1);
+duration.round({ smallestUnit: 'months', relativeTo: { year: 2000, month: 1, day: 1, calendar } });
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..557dcbeac2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-infinity-throws-rangeerror.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.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.duration.prototype.round
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+const base = { year: 2000, month: 5, day: 2, hour: 15, minute: 30, second: 45, millisecond: 987, microsecond: 654, nanosecond: 321 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.round({ smallestUnit: "seconds", relativeTo: { ...base, [prop]: inf } }), `${prop} property cannot be ${inf} in relativeTo`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.round({ smallestUnit: "seconds", relativeTo: { ...base, [prop]: obj } }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-leap-second.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-leap-second.js
new file mode 100644
index 0000000000..9da6485d99
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-leap-second.js
@@ -0,0 +1,46 @@
+// |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.duration.prototype.round
+description: Leap second is constrained in both an ISO string and a property bag
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(1, 0, 0, 0, 24);
+
+let relativeTo = "2016-12-31T23:59:60";
+const result1 = instance.round({ largestUnit: "years", relativeTo });
+TemporalHelpers.assertDuration(
+ result1,
+ 1, 0, 0, 1, 0, 0, 0, 0, 0, 0,
+ "leap second is a valid ISO string for PlainDate relativeTo"
+);
+
+relativeTo = "2016-12-31T23:59:60+00:00[UTC]";
+const result2 = instance.round({ largestUnit: "years", relativeTo });
+TemporalHelpers.assertDuration(
+ result2,
+ 1, 0, 0, 1, 0, 0, 0, 0, 0, 0,
+ "leap second is a valid ISO string for ZonedDateTime relativeTo"
+);
+
+relativeTo = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+const result3 = instance.round({ largestUnit: "years", relativeTo });
+TemporalHelpers.assertDuration(
+ result3,
+ 1, 0, 0, 1, 0, 0, 0, 0, 0, 0,
+ "second: 60 is valid in a property bag for PlainDate relativeTo"
+);
+
+relativeTo = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60, timeZone: "UTC" };
+const result4 = instance.round({ largestUnit: "years", relativeTo });
+TemporalHelpers.assertDuration(
+ result4,
+ 1, 0, 0, 1, 0, 0, 0, 0, 0, 0,
+ "second: 60 is valid in a property bag for ZonedDateTime relativeTo"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-number.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-number.js
new file mode 100644
index 0000000000..a38bd39f1c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-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.duration.prototype.round
+description: A number cannot be used in place of a relativeTo
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(1, 0, 0, 0, 24);
+
+const numbers = [
+ 1,
+ 20191101,
+ -20191101,
+ 1234567890,
+];
+
+for (const relativeTo of numbers) {
+ assert.throws(
+ TypeError,
+ () => instance.round({ largestUnit: "years", relativeTo }),
+ `A number (${relativeTo}) is not a valid ISO string for relativeTo`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-ambiguous-wall-clock-time.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-ambiguous-wall-clock-time.js
new file mode 100644
index 0000000000..4a63b44c79
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-ambiguous-wall-clock-time.js
@@ -0,0 +1,92 @@
+// |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.duration.prototype.round
+description: >
+ Correct time zone calls are made when converting a ZonedDateTime-like
+ relativeTo property bag denoting an ambiguous wall-clock time
+includes: [temporalHelpers.js, compareArray.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+
+const dstTimeZone = TemporalHelpers.springForwardFallBackTimeZone();
+const dstTimeZoneObserver = TemporalHelpers.timeZoneObserver(actual, "timeZone", {
+ getOffsetNanosecondsFor: dstTimeZone.getOffsetNanosecondsFor.bind(dstTimeZone),
+ getPossibleInstantsFor: dstTimeZone.getPossibleInstantsFor.bind(dstTimeZone),
+});
+const calendar = TemporalHelpers.calendarObserver(actual, "calendar");
+
+const instance = new Temporal.Duration(1, 0, 0, 0, 24);
+
+let relativeTo = { year: 2000, month: 4, day: 2, hour: 2, minute: 30, timeZone: dstTimeZoneObserver, calendar };
+instance.round({ largestUnit: "years", relativeTo });
+
+const expected = [
+ // GetTemporalCalendarSlotValueWithISODefault
+ "has calendar.dateAdd",
+ "has calendar.dateFromFields",
+ "has calendar.dateUntil",
+ "has calendar.day",
+ "has calendar.dayOfWeek",
+ "has calendar.dayOfYear",
+ "has calendar.daysInMonth",
+ "has calendar.daysInWeek",
+ "has calendar.daysInYear",
+ "has calendar.fields",
+ "has calendar.id",
+ "has calendar.inLeapYear",
+ "has calendar.mergeFields",
+ "has calendar.month",
+ "has calendar.monthCode",
+ "has calendar.monthDayFromFields",
+ "has calendar.monthsInYear",
+ "has calendar.weekOfYear",
+ "has calendar.year",
+ "has calendar.yearMonthFromFields",
+ "has calendar.yearOfWeek",
+ // lookup
+ "get calendar.dateFromFields",
+ "get calendar.fields",
+ // CalendarFields
+ "call calendar.fields",
+ // InterpretTemporalDateTimeFields
+ "call calendar.dateFromFields",
+ // ToTemporalTimeZoneSlotValue
+ "has timeZone.getOffsetNanosecondsFor",
+ "has timeZone.getPossibleInstantsFor",
+ "has timeZone.id",
+ // lookup
+ "get timeZone.getOffsetNanosecondsFor",
+ "get timeZone.getPossibleInstantsFor",
+ // InterpretISODateTimeOffset
+ "call timeZone.getPossibleInstantsFor",
+];
+
+const expectedSpringForward = expected.concat([
+ // DisambiguatePossibleInstants
+ "call timeZone.getOffsetNanosecondsFor",
+ "call timeZone.getOffsetNanosecondsFor",
+ "call timeZone.getPossibleInstantsFor",
+]);
+assert.compareArray(
+ actual.slice(0, expectedSpringForward.length), // ignore operations after ToRelativeTemporalObject
+ expectedSpringForward,
+ "order of operations converting property bag at skipped wall-clock time"
+);
+actual.splice(0); // clear
+
+relativeTo = { year: 2000, month: 10, day: 29, hour: 1, minute: 30, timeZone: dstTimeZoneObserver, calendar };
+instance.round({ largestUnit: "years", relativeTo });
+
+assert.compareArray(
+ actual.slice(0, expected.length), // ignore operations after ToRelativeTemporalObject
+ expected,
+ "order of operations converting property bag at repeated wall-clock time"
+);
+actual.splice(0); // clear
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..d892f8da70
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-builtin-calendar-no-array-iteration.js
@@ -0,0 +1,25 @@
+// |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.duration.prototype.round
+description: >
+ Calling the method with a relativeTo property bag with a builtin calendar
+ causes no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const timeZone = "UTC";
+const instance = new Temporal.Duration(1, 0, 0, 0, 24);
+const relativeTo = { year: 2000, month: 5, day: 2, hour: 21, minute: 43, second: 5, timeZone, calendar: "iso8601" };
+instance.round({ largestUnit: "years", relativeTo });
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-calendar-datefromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..5952a0d1df
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-calendar-datefromfields-called-with-null-prototype-fields.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.duration.prototype.round
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.Duration(1, 0, 0, 0, 24);
+const relativeTo = { year: 2000, month: 5, day: 2, calendar };
+instance.round({ largestUnit: "years", relativeTo });
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-calendar-number.js
new file mode 100644
index 0000000000..e7eddeccdb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-calendar-number.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.duration.prototype.round
+description: A number as calendar in relativeTo property bag is invalid
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(1, 0, 0, 0, 24);
+
+const numbers = [
+ 1,
+ 19970327,
+ -19970327,
+ 1234567890,
+];
+
+for (const calendar of numbers) {
+ const relativeTo = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ TypeError,
+ () => instance.round({ largestUnit: "years", relativeTo }),
+ `A number (${calendar}) is not a valid ISO string for relativeTo.calendar`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-calendar-string.js
new file mode 100644
index 0000000000..659ac1d8e4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-calendar-string.js
@@ -0,0 +1,29 @@
+// |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.duration.prototype.round
+description: >
+ Builtin dateFromFields method is not observably called when the property bag
+ has a string-valued calendar property
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dateFromFieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dateFromFields");
+Object.defineProperty(Temporal.Calendar.prototype, "dateFromFields", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("dateFromFields should not be looked up");
+ },
+});
+
+const instance = new Temporal.Duration(1, 0, 0, 0, 24);
+const relativeTo = { year: 2000, month: 5, day: 2, calendar: "iso8601" };
+instance.round({ largestUnit: "years", relativeTo });
+
+Object.defineProperty(Temporal.Calendar.prototype, "dateFromFields", dateFromFieldsOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-calendar-wrong-type.js
new file mode 100644
index 0000000000..319310a315
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-calendar-wrong-type.js
@@ -0,0 +1,48 @@
+// |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.duration.prototype.round
+description: >
+ Appropriate error thrown when relativeTo.calendar cannot be converted to a
+ calendar object or string
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.Duration(1, 0, 0, 0, 24);
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [calendar, description] of primitiveTests) {
+ const relativeTo = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ typeof calendar === 'string' ? RangeError : TypeError,
+ () => instance.round({ largestUnit: "years", relativeTo }),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.PlainDate, "Temporal.PlainDate, object"],
+ [Temporal.PlainDate.prototype, "Temporal.PlainDate.prototype, object"],
+ [Temporal.ZonedDateTime, "Temporal.ZonedDateTime, object"],
+ [Temporal.ZonedDateTime.prototype, "Temporal.ZonedDateTime.prototype, object"],
+];
+
+for (const [calendar, description] of typeErrorTests) {
+ const relativeTo = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(TypeError, () => instance.round({ largestUnit: "years", relativeTo }), `${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/Duration/prototype/round/relativeto-propertybag-getpossibleinstantsfor-called-with-iso8601-calendar.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-getpossibleinstantsfor-called-with-iso8601-calendar.js
new file mode 100644
index 0000000000..e32fdaf293
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-getpossibleinstantsfor-called-with-iso8601-calendar.js
@@ -0,0 +1,59 @@
+// |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.duration.prototype.round
+description: >
+ Time zone's getPossibleInstantsFor is called with a PlainDateTime with the
+ built-in ISO 8601 calendar
+features: [Temporal]
+info: |
+ DisambiguatePossibleInstants:
+ 2. Let _n_ be _possibleInstants_'s length.
+ ...
+ 5. Assert: _n_ = 0.
+ ...
+ 19. If _disambiguation_ is *"earlier"*, then
+ ...
+ c. Let _earlierDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*).
+ d. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _earlierDateTime_).
+ ...
+ 20. Assert: _disambiguation_ is *"compatible"* or *"later"*.
+ ...
+ 23. Let _laterDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*).
+ 24. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _laterDateTime_).
+---*/
+
+class SkippedDateTime extends Temporal.TimeZone {
+ constructor() {
+ super("UTC");
+ this.calls = 0;
+ }
+
+ getPossibleInstantsFor(dateTime) {
+ // Calls occur in pairs. For the first one return no possible instants so
+ // that DisambiguatePossibleInstants will call it again
+ if (this.calls++ % 2 == 0) {
+ return [];
+ }
+
+ assert.sameValue(
+ dateTime.getISOFields().calendar,
+ "iso8601",
+ "getPossibleInstantsFor called with dateTime with built-in ISO 8601 calendar"
+ );
+ return super.getPossibleInstantsFor(dateTime);
+ }
+}
+
+const nonBuiltinISOCalendar = new Temporal.Calendar("iso8601");
+const timeZone = new SkippedDateTime();
+const relativeTo = { year: 2000, month: 5, day: 2, timeZone, calendar: nonBuiltinISOCalendar };
+
+const instance = new Temporal.Duration(1, 0, 0, 0, 24);
+instance.round({ largestUnit: "years", relativeTo });
+
+assert.sameValue(timeZone.calls, 6, "getPossibleInstantsFor should have been called 6 times");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-invalid-offset-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-invalid-offset-string.js
new file mode 100644
index 0000000000..2dc7df9e97
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-invalid-offset-string.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.duration.prototype.round
+description: relativeTo property bag with offset property is rejected if offset is in the wrong format
+features: [Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.Duration(1, 0, 0, 0, 24);
+
+const badOffsets = [
+ "00:00", // missing sign
+ "+0", // too short
+ "-000:00", // too long
+ 0, // must be a string
+ null, // must be a string
+ true, // must be a string
+ 1000n, // must be a string
+];
+badOffsets.forEach((offset) => {
+ const relativeTo = { year: 2021, month: 10, day: 28, offset, timeZone };
+ assert.throws(
+ typeof(offset) === 'string' ? RangeError : TypeError,
+ () => instance.round({ largestUnit: "years", relativeTo }),
+ `"${offset} is not a valid offset string`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-no-time-units.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-no-time-units.js
new file mode 100644
index 0000000000..3aaf1f1255
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-no-time-units.js
@@ -0,0 +1,18 @@
+// |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.duration.prototype.round
+description: Missing time units in relativeTo property bag default to 0
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(1, 0, 0, 0, 24);
+
+let relativeTo = { year: 2000, month: 1, day: 1 };
+const result = instance.round({ largestUnit: "years", relativeTo });
+TemporalHelpers.assertDuration(result, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, "missing time units default to 0");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..9443f97e07
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,18 @@
+// |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.duration.prototype.round
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+ assert.throws(RangeError, () => duration.round({ smallestUnit: "seconds", relativeTo: { year: 2000, month: 5, day: 2, hour: 12, timeZone } }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..da545b519b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-getoffsetnanosecondsfor-not-callable.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.duration.prototype.round
+description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable
+features: [BigInt, Symbol, Temporal, arrow-function]
+---*/
+
+[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => {
+ const timeZone = new Temporal.TimeZone("UTC");
+ const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => duration.round({ smallestUnit: "seconds", relativeTo: { year: 2000, month: 5, day: 2, hour: 12, timeZone } }),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..e6bbe148d4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,18 @@
+// |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.duration.prototype.round
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+ assert.throws(RangeError, () => duration.round({ smallestUnit: "seconds", relativeTo: { year: 2000, month: 5, day: 2, hour: 12, timeZone } }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..7af238d77f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.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.duration.prototype.round
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+ assert.throws(TypeError, () => duration.round({ smallestUnit: "seconds", relativeTo: { year: 2000, month: 5, day: 2, hour: 12, timeZone } }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-string-datetime.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-string-datetime.js
new file mode 100644
index 0000000000..28d73fa85f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-string-datetime.js
@@ -0,0 +1,66 @@
+// |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.duration.prototype.round
+description: Conversion of ISO date-time strings to Temporal.TimeZone instances
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(1);
+
+let timeZone = "2021-08-19T17:30";
+assert.throws(RangeError, () => instance.round({ largestUnit: "months", relativeTo: { year: 2000, month: 5, day: 2, timeZone } }), "bare date-time string is not a time zone");
+
+[
+ "2021-08-19T17:30-07:00:01",
+ "2021-08-19T17:30-07:00:00",
+ "2021-08-19T17:30-07:00:00.1",
+ "2021-08-19T17:30-07:00:00.0",
+ "2021-08-19T17:30-07:00:00.01",
+ "2021-08-19T17:30-07:00:00.00",
+ "2021-08-19T17:30-07:00:00.001",
+ "2021-08-19T17:30-07:00:00.000",
+ "2021-08-19T17:30-07:00:00.0001",
+ "2021-08-19T17:30-07:00:00.0000",
+ "2021-08-19T17:30-07:00:00.00001",
+ "2021-08-19T17:30-07:00:00.00000",
+ "2021-08-19T17:30-07:00:00.000001",
+ "2021-08-19T17:30-07:00:00.000000",
+ "2021-08-19T17:30-07:00:00.0000001",
+ "2021-08-19T17:30-07:00:00.0000000",
+ "2021-08-19T17:30-07:00:00.00000001",
+ "2021-08-19T17:30-07:00:00.00000000",
+ "2021-08-19T17:30-07:00:00.000000001",
+ "2021-08-19T17:30-07:00:00.000000000",
+].forEach((timeZone) => {
+ assert.throws(
+ RangeError,
+ () => instance.round({ largestUnit: "months", relativeTo: { year: 2000, month: 5, day: 2, timeZone } }),
+ `ISO string ${timeZone} with a sub-minute offset is not a valid time zone`
+ );
+});
+
+// The following are all valid strings so should not throw:
+
+[
+ "2021-08-19T17:30Z",
+ "2021-08-19T1730Z",
+ "2021-08-19T17:30-07:00",
+ "2021-08-19T1730-07:00",
+ "2021-08-19T17:30-0700",
+ "2021-08-19T1730-0700",
+ "2021-08-19T17:30[UTC]",
+ "2021-08-19T1730[UTC]",
+ "2021-08-19T17:30Z[UTC]",
+ "2021-08-19T1730Z[UTC]",
+ "2021-08-19T17:30-07:00[UTC]",
+ "2021-08-19T1730-07:00[UTC]",
+ "2021-08-19T17:30-0700[UTC]",
+ "2021-08-19T1730-0700[UTC]",
+].forEach((timeZone) => {
+ instance.round({ largestUnit: "months", relativeTo: { year: 2000, month: 5, day: 2, timeZone } });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-string-leap-second.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-string-leap-second.js
new file mode 100644
index 0000000000..c67317b05b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-string-leap-second.js
@@ -0,0 +1,22 @@
+// |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.duration.prototype.round
+description: Leap second is a valid ISO string for TimeZone
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(1);
+let timeZone = "2016-12-31T23:59:60+00:00[UTC]";
+
+// A string with a leap second is a valid ISO string, so the following
+// operation should not throw
+
+instance.round({ largestUnit: "months", relativeTo: { year: 2000, month: 5, day: 2, timeZone } });
+
+timeZone = "2021-08-19T17:30:45.123456789+23:59[+23:59:60]";
+assert.throws(RangeError, () => instance.round({ largestUnit: "months", relativeTo: { year: 2000, month: 5, day: 2, timeZone } }), "leap second in time zone name not valid");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-string-year-zero.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-string-year-zero.js
new file mode 100644
index 0000000000..70f91f3109
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-string-year-zero.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.duration.prototype.round
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+const instance = new Temporal.Duration(1);
+invalidStrings.forEach((timeZone) => {
+ assert.throws(
+ RangeError,
+ () => instance.round({ largestUnit: "months", relativeTo: { year: 2000, month: 5, day: 2, timeZone } }),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-string.js
new file mode 100644
index 0000000000..634e5761b9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-string.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.duration.prototype.round
+description: Time zone IDs are valid input for a time zone
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getPossibleInstantsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getPossibleInstantsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be looked up");
+ },
+});
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+
+const instance = new Temporal.Duration(1);
+
+// The following are all valid strings so should not throw:
+
+["UTC", "+01:00"].forEach((timeZone) => {
+ instance.round({ largestUnit: "months", relativeTo: { year: 2000, month: 5, day: 2, timeZone } });
+});
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", getPossibleInstantsForOriginal);
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-wrong-type.js
new file mode 100644
index 0000000000..a7d0d08982
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-wrong-type.js
@@ -0,0 +1,42 @@
+// |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.duration.prototype.round
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or object for TimeZone
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.Duration(1);
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [19761118, "number that would convert to a valid ISO string in other contexts"],
+ [1n, "bigint"],
+];
+
+for (const [timeZone, description] of primitiveTests) {
+ assert.throws(
+ typeof timeZone === 'string' ? RangeError : TypeError,
+ () => instance.round({ largestUnit: "months", relativeTo: { year: 2000, month: 5, day: 2, timeZone } }),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "object not implementing time zone protocol"],
+ [new Temporal.Calendar("iso8601"), "calendar instance"],
+];
+
+for (const [timeZone, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.round({ largestUnit: "months", relativeTo: { year: 2000, month: 5, day: 2, timeZone } }), `${description} is not a valid object and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-string-datetime.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-string-datetime.js
new file mode 100644
index 0000000000..82adc4c795
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-string-datetime.js
@@ -0,0 +1,41 @@
+// |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.duration.prototype.round
+description: >
+ Conversion of ISO date-time strings as relativeTo option to
+ Temporal.ZonedDateTime or Temporal.PlainDateTime instances
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(1, 0, 0, 0, 24);
+
+let relativeTo = "2019-11-01T00:00";
+const result1 = instance.round({ largestUnit: "years", relativeTo });
+TemporalHelpers.assertDuration(result1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, "bare date-time string is a plain relativeTo");
+
+relativeTo = "2019-11-01T00:00-07:00";
+const result2 = instance.round({ largestUnit: "years", relativeTo });
+TemporalHelpers.assertDuration(result2, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, "date-time + offset is a plain relativeTo");
+
+relativeTo = "2019-11-01T00:00[-07:00]";
+const result3 = instance.round({ largestUnit: "years", relativeTo });
+TemporalHelpers.assertDuration(result3, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, "date-time + IANA annotation is a zoned relativeTo");
+
+relativeTo = "2019-11-01T00:00Z[-07:00]";
+const result4 = instance.round({ largestUnit: "years", relativeTo });
+TemporalHelpers.assertDuration(result4, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, "date-time + Z + IANA annotation is a zoned relativeTo");
+
+relativeTo = "2019-11-01T00:00+00:00[UTC]";
+const result5 = instance.round({ largestUnit: "years", relativeTo });
+TemporalHelpers.assertDuration(result5, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, "date-time + offset + IANA annotation is a zoned relativeTo");
+
+relativeTo = "2019-11-01T00:00Z";
+assert.throws(RangeError, () => instance.round({ largestUnit: "years", relativeTo }), "date-time + Z throws without an IANA annotation");
+relativeTo = "2019-11-01T00:00+04:15[UTC]";
+assert.throws(RangeError, () => instance.round({ largestUnit: "years", relativeTo }), "date-time + offset + IANA annotation throws if wall time and exact time mismatch");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-string-invalid.js
new file mode 100644
index 0000000000..0fb7786d73
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-string-invalid.js
@@ -0,0 +1,16 @@
+// |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.duration.prototype.round
+description: RangeError thrown if relativeTo is a string with the wrong format
+features: [Temporal]
+---*/
+
+['bad string', '15:30:45.123456', 'iso8601', 'UTC', 'P1YT1H'].forEach((relativeTo) => {
+ const duration = new Temporal.Duration(0, 0, 0, 31);
+ assert.throws(RangeError, () => duration.round({ largestUnit: "months", relativeTo }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-string-plaindatetime.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-string-plaindatetime.js
new file mode 100644
index 0000000000..c398314baf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-string-plaindatetime.js
@@ -0,0 +1,18 @@
+// |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.duration.prototype.round
+description: The relativeTo option accepts a PlainDateTime-like ISO 8601 string
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+['2000-01-01', '2000-01-01T00:00', '2000-01-01T00:00[u-ca=iso8601]'].forEach((relativeTo) => {
+ const duration = new Temporal.Duration(0, 0, 0, 31);
+ const result = duration.round({ largestUnit: "months", relativeTo });
+ TemporalHelpers.assertDuration(result, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-string-zoneddatetime-wrong-offset.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-string-zoneddatetime-wrong-offset.js
new file mode 100644
index 0000000000..7265bef1f2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-string-zoneddatetime-wrong-offset.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.duration.prototype.round
+description: Throws if a ZonedDateTime-like relativeTo string has the wrong UTC offset
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(1, 0, 0, 0, 24);
+const relativeTo = "2000-01-01T00:00+05:30[UTC]";
+assert.throws(
+ RangeError,
+ () => instance.round({ largestUnit: "years", relativeTo }),
+ "round should throw RangeError on a string with UTC offset mismatch"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-string-zoneddatetime.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-string-zoneddatetime.js
new file mode 100644
index 0000000000..ce97bde631
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-string-zoneddatetime.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.duration.prototype.round
+description: The relativeTo option accepts a ZonedDateTime-like ISO 8601 string
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ '2000-01-01[UTC]',
+ '2000-01-01T00:00[UTC]',
+ '2000-01-01T00:00+00:00[UTC]',
+ '2000-01-01T00:00+00:00[UTC][u-ca=iso8601]',
+].forEach((relativeTo) => {
+ const duration = new Temporal.Duration(0, 0, 0, 31);
+ const result = duration.round({ largestUnit: "months", relativeTo });
+ TemporalHelpers.assertDuration(result, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-sub-minute-offset.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-sub-minute-offset.js
new file mode 100644
index 0000000000..c6ecee882f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-sub-minute-offset.js
@@ -0,0 +1,30 @@
+// |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.duration.prototype.round
+description: relativeTo string accepts trailing zeroes in sub-minute UTC offset
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(1, 0, 0, 0, 24);
+
+let result;
+let relativeTo;
+
+const action = (relativeTo) => instance.round({ largestUnit: "years", relativeTo });
+
+relativeTo = "1970-01-01T00:00-00:45:00[-00:45]";
+result = action(relativeTo);
+TemporalHelpers.assertDateDuration(result, 1, 0, 0, 1, "ISO string offset accepted with zero seconds (string)");
+
+relativeTo = { year: 1970, month: 1, day: 1, offset: "+00:45:00.000000000", timeZone: "+00:45" };
+result = action(relativeTo);
+TemporalHelpers.assertDateDuration(result, 1, 0, 0, 1, "ISO string offset accepted with zero seconds (property bag)");
+
+relativeTo = "1970-01-01T00:00+00:44:30.123456789[+00:45]";
+assert.throws(RangeError, () => action(relativeTo), "rounding is not accepted between ISO offset and time zone");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-undefined-throw-on-calendar-units.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-undefined-throw-on-calendar-units.js
new file mode 100644
index 0000000000..fc0a93d87b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-undefined-throw-on-calendar-units.js
@@ -0,0 +1,31 @@
+// |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.duration.prototype.round
+description: >
+ The relativeTo option is required when the Duration contains years, months,
+ or weeks, and largestUnit is days; or largestUnit is weeks or months
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const oneYear = new Temporal.Duration(1);
+const oneMonth = new Temporal.Duration(0, 1);
+const oneWeek = new Temporal.Duration(0, 0, 1);
+const oneDay = new Temporal.Duration(0, 0, 0, 1);
+
+const options = { largestUnit: "days" };
+TemporalHelpers.assertDuration(oneDay.round(options), 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, "days do not require relativeTo");
+assert.throws(RangeError, () => oneWeek.round(options), "balancing weeks to days requires relativeTo");
+assert.throws(RangeError, () => oneMonth.round(options), "balancing months to days requires relativeTo");
+assert.throws(RangeError, () => oneYear.round(options), "balancing years to days requires relativeTo");
+
+["months", "weeks"].forEach((largestUnit) => {
+ [oneDay, oneWeek, oneMonth, oneYear].forEach((duration) => {
+ assert.throws(RangeError, () => duration.round({ largestUnit }), `balancing ${duration} to ${largestUnit} requires relativeTo`);
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-wrong-type.js
new file mode 100644
index 0000000000..e95c122061
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-wrong-type.js
@@ -0,0 +1,50 @@
+// |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.duration.prototype.round
+description: >
+ Appropriate error thrown when relativeTo cannot be converted to a valid
+ relativeTo string or property bag
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone('UTC');
+const instance = new Temporal.Duration(1, 0, 0, 0, 24);
+
+const primitiveTests = [
+ [undefined, 'undefined'],
+ [null, 'null'],
+ [true, 'boolean'],
+ ['', 'empty string'],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, 'bigint']
+];
+
+for (const [relativeTo, description] of primitiveTests) {
+ assert.throws(
+ typeof relativeTo === 'string' || typeof relativeTo === 'undefined' ? RangeError : TypeError,
+ () => instance.round({ largestUnit: 'years', relativeTo }),
+ `${description} does not convert to a valid ISO string (first argument)`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), 'symbol'],
+ [{}, 'plain object'],
+ [Temporal.PlainDate, 'Temporal.PlainDate, object'],
+ [Temporal.PlainDate.prototype, 'Temporal.PlainDate.prototype, object'],
+ [Temporal.ZonedDateTime, 'Temporal.ZonedDateTime, object'],
+ [Temporal.ZonedDateTime.prototype, 'Temporal.ZonedDateTime.prototype, object']
+];
+
+for (const [relativeTo, description] of typeErrorTests) {
+ assert.throws(
+ TypeError,
+ () => instance.round({ largestUnit: 'years', relativeTo }),
+ `${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/Duration/prototype/round/relativeto-zoneddatetime-convert.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-zoneddatetime-convert.js
new file mode 100644
index 0000000000..f224acfa66
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-zoneddatetime-convert.js
@@ -0,0 +1,22 @@
+// |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.duration.prototype.round
+description: An exception from TimeZone#getOffsetNanosecondsFor() is propagated.
+features: [Temporal]
+---*/
+
+class TZ extends Temporal.TimeZone {
+ constructor() { super("UTC") }
+ getOffsetNanosecondsFor() { throw new Test262Error() }
+}
+
+const tz = new TZ();
+const arg = new Temporal.ZonedDateTime(0n, tz);
+const instance = new Temporal.Duration(0, 0, 0, 365);
+
+assert.throws(Test262Error, () => instance.round({ relativeTo: arg, largestUnit: "years" }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-zoneddatetime-negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-zoneddatetime-negative-epochnanoseconds.js
new file mode 100644
index 0000000000..4169e53695
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-zoneddatetime-negative-epochnanoseconds.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.duration.prototype.round
+description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch
+info: |
+ sec-temporal-getisopartsfromepoch step 1:
+ 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 10<sup>6</sup>.
+ sec-temporal-builtintimezonegetplaindatetimefor step 2:
+ 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]).
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const relativeTo = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC");
+const duration = new Temporal.Duration(0, 0, 0, 1);
+
+// This code path shows up anywhere we convert an exact time, before the Unix
+// epoch, with nonzero microseconds or nanoseconds, into a wall time; in this
+// case via relativeTo.
+
+const result = duration.round({ relativeTo, largestUnit: "days" });
+TemporalHelpers.assertDuration(result, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-zoneddatetime-normalized-time-duration-to-days-range-errors.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-zoneddatetime-normalized-time-duration-to-days-range-errors.js
new file mode 100644
index 0000000000..150f9f419e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-zoneddatetime-normalized-time-duration-to-days-range-errors.js
@@ -0,0 +1,128 @@
+// |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.duration.prototype.round
+description: >
+ Abstract operation NormalizedTimeDurationToDays can throw four different
+ RangeErrors.
+info: |
+ NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ , precalculatedPlainDateTime ] )
+ 22. If days < 0 and sign = 1, throw a RangeError exception.
+ 23. If days > 0 and sign = -1, throw a RangeError exception.
+ ...
+ 25. If NormalizedTimeDurationSign(_norm_) = 1 and sign = -1, throw a RangeError exception.
+ ...
+ 28. If dayLength ≥ 2⁵³, throw a RangeError exception.
+features: [Temporal, BigInt]
+includes: [temporalHelpers.js]
+---*/
+
+const oneNsDuration = Temporal.Duration.from({ nanoseconds: 1 });
+const negOneNsDuration = Temporal.Duration.from({ nanoseconds: -1 });
+const dayNs = 86_400_000_000_000;
+const epochInstant = new Temporal.Instant(0n);
+
+function timeZoneSubstituteValues(
+ getPossibleInstantsFor,
+ getOffsetNanosecondsFor
+) {
+ const tz = new Temporal.TimeZone("UTC");
+ TemporalHelpers.substituteMethod(
+ tz,
+ "getPossibleInstantsFor",
+ getPossibleInstantsFor
+ );
+ TemporalHelpers.substituteMethod(
+ tz,
+ "getOffsetNanosecondsFor",
+ getOffsetNanosecondsFor
+ );
+ return tz;
+}
+
+// Step 22: days < 0 and sign = 1
+let zdt = new Temporal.ZonedDateTime(
+ 0n, // Sets _startNs_ to 0
+ timeZoneSubstituteValues(
+ [[epochInstant]], // Returned in step 16, setting _relativeResult_
+ [
+ TemporalHelpers.SUBSTITUTE_SKIP, // Pre-conversion in Duration.p.round
+ dayNs - 1, // Returned in step 8, setting _startDateTime_
+ -dayNs + 1, // Returned in step 9, setting _endDateTime_
+ ]
+ )
+);
+assert.throws(RangeError, () =>
+ // Using 1ns duration sets _nanoseconds_ to 1 and _sign_ to 1
+ oneNsDuration.round({
+ relativeTo: zdt,
+ smallestUnit: "days",
+ }),
+ "RangeError when days < 0 and sign = 1"
+);
+
+// Step 23: days > 0 and sign = -1
+zdt = new Temporal.ZonedDateTime(
+ 0n, // Sets _startNs_ to 0
+ timeZoneSubstituteValues(
+ [[epochInstant]], // Returned in step 16, setting _relativeResult_
+ [
+ TemporalHelpers.SUBSTITUTE_SKIP, // Pre-conversion in Duration.p.round
+ -dayNs + 1, // Returned in step 8, setting _startDateTime_
+ dayNs - 1, // Returned in step 9, setting _endDateTime_
+ ]
+ )
+);
+assert.throws(RangeError, () =>
+ // Using -1ns duration sets _nanoseconds_ to -1 and _sign_ to -1
+ negOneNsDuration.round({
+ relativeTo: zdt,
+ smallestUnit: "days",
+ }),
+ "RangeError when days > 0 and sign = -1"
+);
+
+// Step 25: nanoseconds > 0 and sign = -1
+zdt = new Temporal.ZonedDateTime(
+ 0n, // Sets _startNs_ to 0
+ timeZoneSubstituteValues(
+ [
+ [new Temporal.Instant(-2n)], // Returned in step 16, setting _relativeResult_
+ [new Temporal.Instant(-4n)], // Returned in step 21.a, setting _oneDayFarther_
+ ],
+ [
+ TemporalHelpers.SUBSTITUTE_SKIP, // Pre-conversion in Duration.p.round
+ dayNs - 1, // Returned in step 8, setting _startDateTime_
+ -dayNs + 1, // Returned in step 9, setting _endDateTime_
+ ]
+ )
+);
+assert.throws(RangeError, () =>
+ // Using -1ns duration sets _nanoseconds_ to -1 and _sign_ to -1
+ negOneNsDuration.round({
+ relativeTo: zdt,
+ smallestUnit: "days",
+ }),
+ "RangeError when nanoseconds > 0 and sign = -1"
+);
+
+// Step 28: day length is an unsafe integer
+zdt = new Temporal.ZonedDateTime(
+ 0n,
+ timeZoneSubstituteValues(
+ // Not called in step 16 because _days_ = 0
+ // Returned in step 21.a, making _oneDayFarther_ 2^53 ns later than _relativeResult_
+ [[new Temporal.Instant(2n ** 53n)]],
+ []
+ )
+);
+assert.throws(RangeError, () =>
+ oneNsDuration.round({
+ relativeTo: zdt,
+ smallestUnit: "days",
+ }),
+ "Should throw RangeError when time zone calculates an outrageous day length"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-zoneddatetime-slots.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-zoneddatetime-slots.js
new file mode 100644
index 0000000000..2ba84b3428
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-zoneddatetime-slots.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.duration.prototype.round
+description: Getters are not called when converting a ZonedDateTime to a PlainDate.
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.ZonedDateTime.prototype);
+const getters = ["year", "month", "monthCode", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond", "calendar"];
+
+for (const property of getters) {
+ Object.defineProperty(Temporal.ZonedDateTime.prototype, property, {
+ get() {
+ actual.push(`get ${property}`);
+ const value = prototypeDescrs[property].get.call(this);
+ return {
+ toString() {
+ actual.push(`toString ${property}`);
+ return value.toString();
+ },
+ valueOf() {
+ actual.push(`valueOf ${property}`);
+ return value;
+ },
+ };
+ },
+ });
+}
+
+const arg = new Temporal.ZonedDateTime(0n, "UTC");
+const instance = new Temporal.Duration(0, 0, 0, 365);
+instance.round({ relativeTo: arg, largestUnit: "years" });
+assert.compareArray(actual, []);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..3f73e3fe1d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-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.duration.prototype.round
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => duration.round({ smallestUnit: "seconds", relativeTo: datetime }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..2716f21038
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-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.duration.prototype.round
+description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable
+features: [BigInt, Symbol, Temporal, arrow-function]
+---*/
+
+[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => {
+ const timeZone = new Temporal.TimeZone("UTC");
+ const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => duration.round({ smallestUnit: "seconds", relativeTo: datetime }),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..d67ea734d0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-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.duration.prototype.round
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => duration.round({ smallestUnit: "seconds", relativeTo: datetime }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..f3f5c16a14
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-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.duration.prototype.round
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => duration.round({ smallestUnit: "seconds", relativeTo: datetime }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/result-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/result-out-of-range.js
new file mode 100644
index 0000000000..c618abef81
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/result-out-of-range.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.round
+description: >
+ RoundDuration throws a RangeError when the result duration is invalid.
+features: [Temporal]
+---*/
+
+const duration = Temporal.Duration.from({
+ seconds: Number.MAX_SAFE_INTEGER,
+ nanoseconds: 999_999_999,
+});
+
+assert.throws(RangeError, () => duration.round({smallestUnit: "seconds"}), "result is out of range");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/round-cross-unit-boundary.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/round-cross-unit-boundary.js
new file mode 100644
index 0000000000..7015d06e66
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/round-cross-unit-boundary.js
@@ -0,0 +1,50 @@
+// |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.duration.prototype.round
+description: Rounding can cross unit boundaries up to the implicit largestUnit
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const relativeTo = new Temporal.PlainDate(2022, 1, 1);
+const roundingMode = "expand";
+
+// Positive, date units
+{
+ const duration = new Temporal.Duration(1, 11, 0, 24);
+ const result = duration.round({ smallestUnit: "months", roundingMode, relativeTo });
+ TemporalHelpers.assertDuration(result, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, "1 year 12 months balances to 2 years");
+}
+
+// Negative, date units
+{
+ const duration = new Temporal.Duration(-1, -11, 0, -24);
+ const result = duration.round({ smallestUnit: "months", roundingMode, relativeTo });
+ TemporalHelpers.assertDuration(result, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, "-1 year -12 months balances to -2 years");
+}
+
+// Positive, time units
+{
+ const duration = new Temporal.Duration(0, 0, 0, 0, 1, 59, 59, 900);
+ const result = duration.round({ smallestUnit: "seconds", roundingMode });
+ TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, "1:59:60 balances to 2 hours");
+}
+
+// Negative, time units
+{
+ const duration = new Temporal.Duration(0, 0, 0, 0, -1, -59, -59, -900);
+ const result = duration.round({ smallestUnit: "seconds", roundingMode });
+ TemporalHelpers.assertDuration(result, 0, 0, 0, 0, -2, 0, 0, 0, 0, 0, "-1:59:60 balances to -2 hours");
+}
+
+// No balancing if smallest unit is largest unit
+{
+ const duration = new Temporal.Duration(0, 11, 0, 24);
+ const result = duration.round({ smallestUnit: "months", roundingMode, relativeTo });
+ TemporalHelpers.assertDuration(result, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, "12 months stays as is");
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/round-negative-result.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/round-negative-result.js
new file mode 100644
index 0000000000..41df6a21e5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/round-negative-result.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.duration.prototype.round
+description: A negative duration result is balanced correctly by the modulo operation in NanosecondsToDays
+info: |
+ sec-temporal-nanosecondstodays step 6:
+ 6. If Type(_relativeTo_) is not Object or _relativeTo_ does not have an [[InitializedTemporalZonedDateTime]] internal slot, then
+ a. Return the new Record { ..., [[Nanoseconds]]\: abs(_nanoseconds_) modulo _dayLengthNs_ × _sign_, ... }.
+ sec-temporal-roundduration step 6:
+ 6. If _unit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*, then
+ ...
+ d. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _intermediate_).
+ sec-temporal.duration.prototype.round step 21:
+ 21. Let _roundResult_ be ? RoundDuration(_unbalanceResult_.[[Years]], _unbalanceResult_.[[Months]], _unbalanceResult_.[[Weeks]], _unbalanceResult_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_[[Seconds]], _duration_[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], _roundingIncrement_, _smallestUnit_, _roundingMode_, _relativeTo_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(0, 0, 0, 0, -60);
+const result = duration.round({ smallestUnit: "days" });
+TemporalHelpers.assertDuration(result, 0, 0, 0, -3, 0, 0, 0, 0, 0, 0);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/rounding-is-noop.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/rounding-is-noop.js
new file mode 100644
index 0000000000..fe87f78da9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/rounding-is-noop.js
@@ -0,0 +1,89 @@
+// |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.duration.prototype.round
+description: >
+ No calendar or time zone methods are called under circumstances where rounding
+ is a no-op
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarThrowEverything();
+const timeZone = TemporalHelpers.timeZoneThrowEverything();
+const plainRelativeTo = new Temporal.PlainDate(2000, 1, 1, calendar);
+const zonedRelativeTo = new Temporal.ZonedDateTime(0n, timeZone, calendar);
+
+const d = new Temporal.Duration(0, 0, 0, 0, 23, 59, 59, 999, 999, 997);
+
+const noopRoundingOperations = [
+ [d, { smallestUnit: "nanoseconds" }, "smallestUnit ns"],
+ [d, { smallestUnit: "nanoseconds", relativeTo: plainRelativeTo }, "smallestUnit ns and plain relativeTo"],
+ [d, { smallestUnit: "nanoseconds", relativeTo: zonedRelativeTo }, "smallestUnit ns and zoned relativeTo"],
+ [d, { smallestUnit: "nanoseconds", roundingIncrement: 1 }, "round to 1 ns"],
+ // No balancing because largestUnit is already the largest unit and no time units overflow:
+ [d, { largestUnit: "hours" }, "largestUnit hours"],
+ // Unless relativeTo is ZonedDateTime, no-op is still possible with days>0:
+ [new Temporal.Duration(0, 0, 0, 1), { smallestUnit: "nanoseconds" }, "days>0 and smallestUnit ns"],
+ [new Temporal.Duration(0, 0, 0, 1), { smallestUnit: "nanoseconds", relativeTo: plainRelativeTo }, "days>0, smallestUnit ns, and plain relativeTo"],
+];
+for (const [duration, options, descr] of noopRoundingOperations) {
+ const result = duration.round(options);
+ assert.notSameValue(result, duration, "rounding result should be a new object");
+ TemporalHelpers.assertDurationsEqual(result, duration, `rounding should be a no-op with ${descr}`);
+
+ const negDuration = duration.negated();
+ const negResult = negDuration.round(options);
+ assert.notSameValue(negResult, negDuration, "rounding result should be a new object (negative)");
+ TemporalHelpers.assertDurationsEqual(negResult, negDuration, `rounding should be a no-op with ${descr} (negative)`);
+}
+
+// These operations are not no-op rounding operations, but still should not call
+// any calendar methods:
+const roundingOperationsNotCallingCalendarMethods = [
+ [d, { smallestUnit: "microseconds" }, "round to 1 µs"],
+ [d, { smallestUnit: "nanoseconds", roundingIncrement: 2 }, "round to 2 ns"],
+ [new Temporal.Duration(0, 0, 0, 0, 24), { largestUnit: "days" }, "upwards balancing requested"],
+ [d, { largestUnit: "minutes" }, "downwards balancing requested"],
+ [new Temporal.Duration(0, 0, 0, 0, 1, 120), { smallestUnit: "nanoseconds" }, "time units could overflow"],
+ [new Temporal.Duration(0, 0, 0, 1, 24), { smallestUnit: "nanoseconds" }, "hours-to-days conversion could occur"],
+];
+for (const [duration, options, descr] of roundingOperationsNotCallingCalendarMethods) {
+ const result = duration.round(options);
+ let equal = true;
+ for (const prop of ['years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds', 'milliseconds', 'microseconds', 'nanoseconds']) {
+ if (result[prop] !== duration[prop]) {
+ equal = false;
+ break;
+ }
+ }
+ assert(!equal, `round result ${result} should be different from ${duration} with ${descr}`);
+
+ const negDuration = duration.negated();
+ const negResult = negDuration.round(options);
+ equal = true;
+ for (const prop of ['years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds', 'milliseconds', 'microseconds', 'nanoseconds']) {
+ if (negResult[prop] !== negDuration[prop]) {
+ equal = false;
+ break;
+ }
+ }
+ assert(!equal, `round result ${negResult} should be different from ${negDuration} with ${descr} (negative)`);
+}
+
+// These operations should not be short-circuited because they have to call
+// calendar methods:
+const roundingOperationsCallingCalendarMethods = [
+ [new Temporal.Duration(0, 0, 1), { smallestUnit: "nanoseconds", relativeTo: plainRelativeTo }, "calendar units present"],
+ [d, { largestUnit: "days", relativeTo: zonedRelativeTo }, "largestUnit days with zoned relativeTo"],
+ [new Temporal.Duration(0, 0, 0, 1), { smallestUnit: "nanoseconds", relativeTo: zonedRelativeTo }, "hours-to-days conversion could occur with zoned relativeTo"],
+];
+
+for (const [duration, options, descr] of roundingOperationsCallingCalendarMethods) {
+ assert.throws(Test262Error, () => duration.round(options), `rounding should not be a no-op with ${descr}`);
+ assert.throws(Test262Error, () => duration.negated().round(options), `rounding should not be a no-op with ${descr} (negative)`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingincrement-nan.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingincrement-nan.js
new file mode 100644
index 0000000000..394c222fff
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingincrement-nan.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.duration.prototype.round
+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.duration.prototype.round step 18:
+ 18. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*).
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(0, 0, 0, 4, 12, 34, 56, 987, 654, 321);
+assert.throws(RangeError, () => duration.round({ smallestUnit: 'second', roundingIncrement: NaN }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingincrement-non-integer.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingincrement-non-integer.js
new file mode 100644
index 0000000000..84a7edf182
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingincrement-non-integer.js
@@ -0,0 +1,26 @@
+// |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.duration.prototype.round
+description: Rounding for roundingIncrement option
+info: |
+ sec-temporal-totemporalroundingincrement:
+ 3. Let _integerIncrement_ be truncate(ℝ(_increment_)).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(0, 0, 0, 1);
+const options = {
+ smallestUnit: "days",
+ roundingMode: "expand",
+ relativeTo: new Temporal.PlainDate(2000, 1, 1),
+};
+const result = instance.round({ ...options, roundingIncrement: 2.5 });
+TemporalHelpers.assertDuration(result, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, "roundingIncrement 2.5 truncates to 2");
+const result2 = instance.round({ ...options, roundingIncrement: 1e9 + 0.5 });
+TemporalHelpers.assertDuration(result2, 0, 0, 0, 1e9, 0, 0, 0, 0, 0, 0, "roundingIncrement 1e9 + 0.5 truncates to 1e9");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingincrement-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingincrement-out-of-range.js
new file mode 100644
index 0000000000..68f0e24bdb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingincrement-out-of-range.js
@@ -0,0 +1,22 @@
+// |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.duration.prototype.round
+description: RangeError thrown when roundingIncrement option out of range
+info: |
+ sec-temporal-totemporalroundingincrement:
+ 4. If _integerIncrement_ < 1 or _integerIncrement_ > 10<sup>9</sup>, throw a *RangeError* exception.
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(1);
+const options = { smallestUnit: "years", relativeTo: new Temporal.PlainDate(2000, 1, 1) };
+assert.throws(RangeError, () => instance.round({ ...options, roundingIncrement: -Infinity }));
+assert.throws(RangeError, () => instance.round({ ...options, roundingIncrement: -1 }));
+assert.throws(RangeError, () => instance.round({ ...options, roundingIncrement: 0 }));
+assert.throws(RangeError, () => instance.round({ ...options, roundingIncrement: 1e9 + 1 }));
+assert.throws(RangeError, () => instance.round({ ...options, roundingIncrement: Infinity }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingincrement-undefined.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingincrement-undefined.js
new file mode 100644
index 0000000000..7b7c1f42d2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingincrement-undefined.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.duration.prototype.round
+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.duration.prototype.round step 18:
+ 18. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(0, 0, 0, 4, 12, 34, 56, 987, 654, 321);
+
+const explicit = duration.round({ smallestUnit: 'second', roundingIncrement: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 4, 12, 34, 57, 0, 0, 0, "default roundingIncrement is 1");
+
+const implicit = duration.round({ smallestUnit: 'second' });
+TemporalHelpers.assertDuration(implicit, 0, 0, 0, 4, 12, 34, 57, 0, 0, 0, "default roundingIncrement is 1");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingincrement-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingincrement-wrong-type.js
new file mode 100644
index 0000000000..1b992cdba7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingincrement-wrong-type.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.round
+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.round step 11:
+ 11. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*).
+includes: [temporalHelpers.js, compareArray.js]
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(0, 0, 0, 4, 12, 34, 56, 987, 654, 321);
+
+TemporalHelpers.checkRoundingIncrementOptionWrongType(
+ (roundingIncrement) => duration.round({ smallestUnit: 'second', roundingIncrement }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 4, 12, 34, 57, 0, 0, 0, descr),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 4, 12, 34, 56, 0, 0, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-ceil.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-ceil.js
new file mode 100644
index 0000000000..334aa4eab4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-ceil.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.duration.prototype.round
+description: Tests calculations with roundingMode "ceil".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(5, 6, 7, 8, 40, 30, 20, 123, 987, 500);
+// Chosen such that 8 months forwards from relativeToForwards is the
+// same number of days as 8 months backwards from relativeToBackwards
+// (for convenience)
+const relativeToForwards = new Temporal.PlainDate(2020, 4, 1);
+const relativeToBackwards = new Temporal.PlainDate(2020, 12, 1);
+
+const expected = [
+ ["years", [6], [-5]],
+ ["months", [5, 8], [-5, -7]],
+ ["weeks", [5, 6, 9], [-5, -6, -8]],
+ ["days", [5, 7, 0, 28], [-5, -7, 0, -27]],
+ ["hours", [5, 7, 0, 27, 17], [-5, -7, 0, -27, -16]],
+ ["minutes", [5, 7, 0, 27, 16, 31], [-5, -7, 0, -27, -16, -30]],
+ ["seconds", [5, 7, 0, 27, 16, 30, 21], [-5, -7, 0, -27, -16, -30, -20]],
+ ["milliseconds", [5, 7, 0, 27, 16, 30, 20, 124], [-5, -7, 0, -27, -16, -30, -20, -123]],
+ ["microseconds", [5, 7, 0, 27, 16, 30, 20, 123, 988], [-5, -7, 0, -27, -16, -30, -20, -123, -987]],
+ ["nanoseconds", [5, 7, 0, 27, 16, 30, 20, 123, 987, 500], [-5, -7, 0, -27, -16, -30, -20, -123, -987, -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(
+ instance.round({ smallestUnit, relativeTo: relativeToForwards, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ instance.negated().round({ smallestUnit, relativeTo: relativeToBackwards, 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/Duration/prototype/round/roundingmode-expand.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-expand.js
new file mode 100644
index 0000000000..525284e679
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-expand.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.duration.prototype.round
+description: Tests calculations with roundingMode "expand".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(5, 6, 7, 8, 40, 30, 20, 123, 987, 500);
+// Chosen such that 8 months forwards from relativeToForwards is the
+// same number of days as 8 months backwards from relativeToBackwards
+// (for convenience)
+const relativeToForwards = new Temporal.PlainDate(2020, 4, 1);
+const relativeToBackwards = new Temporal.PlainDate(2020, 12, 1);
+
+const expected = [
+ ["years", [6], [-6]],
+ ["months", [5, 8], [-5, -8]],
+ ["weeks", [5, 6, 9], [-5, -6, -9]],
+ ["days", [5, 7, 0, 28], [-5, -7, 0, -28]],
+ ["hours", [5, 7, 0, 27, 17], [-5, -7, 0, -27, -17]],
+ ["minutes", [5, 7, 0, 27, 16, 31], [-5, -7, 0, -27, -16, -31]],
+ ["seconds", [5, 7, 0, 27, 16, 30, 21], [-5, -7, 0, -27, -16, -30, -21]],
+ ["milliseconds", [5, 7, 0, 27, 16, 30, 20, 124], [-5, -7, 0, -27, -16, -30, -20, -124]],
+ ["microseconds", [5, 7, 0, 27, 16, 30, 20, 123, 988], [-5, -7, 0, -27, -16, -30, -20, -123, -988]],
+ ["nanoseconds", [5, 7, 0, 27, 16, 30, 20, 123, 987, 500], [-5, -7, 0, -27, -16, -30, -20, -123, -987, -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(
+ instance.round({ smallestUnit, relativeTo: relativeToForwards, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ instance.negated().round({ smallestUnit, relativeTo: relativeToBackwards, 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/Duration/prototype/round/roundingmode-floor.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-floor.js
new file mode 100644
index 0000000000..92f68b166e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-floor.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.duration.prototype.round
+description: Tests calculations with roundingMode "floor".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(5, 6, 7, 8, 40, 30, 20, 123, 987, 500);
+// Chosen such that 8 months forwards from relativeToForwards is the
+// same number of days as 8 months backwards from relativeToBackwards
+// (for convenience)
+const relativeToForwards = new Temporal.PlainDate(2020, 4, 1);
+const relativeToBackwards = new Temporal.PlainDate(2020, 12, 1);
+
+const expected = [
+ ["years", [5], [-6]],
+ ["months", [5, 7], [-5, -8]],
+ ["weeks", [5, 6, 8], [-5, -6, -9]],
+ ["days", [5, 7, 0, 27], [-5, -7, 0, -28]],
+ ["hours", [5, 7, 0, 27, 16], [-5, -7, 0, -27, -17]],
+ ["minutes", [5, 7, 0, 27, 16, 30], [-5, -7, 0, -27, -16, -31]],
+ ["seconds", [5, 7, 0, 27, 16, 30, 20], [-5, -7, 0, -27, -16, -30, -21]],
+ ["milliseconds", [5, 7, 0, 27, 16, 30, 20, 123], [-5, -7, 0, -27, -16, -30, -20, -124]],
+ ["microseconds", [5, 7, 0, 27, 16, 30, 20, 123, 987], [-5, -7, 0, -27, -16, -30, -20, -123, -988]],
+ ["nanoseconds", [5, 7, 0, 27, 16, 30, 20, 123, 987, 500], [-5, -7, 0, -27, -16, -30, -20, -123, -987, -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(
+ instance.round({ smallestUnit, relativeTo: relativeToForwards, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ instance.negated().round({ smallestUnit, relativeTo: relativeToBackwards, 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/Duration/prototype/round/roundingmode-halfCeil.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-halfCeil.js
new file mode 100644
index 0000000000..b8a0f26669
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-halfCeil.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.duration.prototype.round
+description: Tests calculations with roundingMode "halfCeil".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(5, 6, 7, 8, 40, 30, 20, 123, 987, 500);
+// Chosen such that 8 months forwards from relativeToForwards is the
+// same number of days as 8 months backwards from relativeToBackwards
+// (for convenience)
+const relativeToForwards = new Temporal.PlainDate(2020, 4, 1);
+const relativeToBackwards = new Temporal.PlainDate(2020, 12, 1);
+
+const expected = [
+ ["years", [6], [-6]],
+ ["months", [5, 8], [-5, -8]],
+ ["weeks", [5, 6, 8], [-5, -6, -8]],
+ ["days", [5, 7, 0, 28], [-5, -7, 0, -28]],
+ ["hours", [5, 7, 0, 27, 17], [-5, -7, 0, -27, -17]],
+ ["minutes", [5, 7, 0, 27, 16, 30], [-5, -7, 0, -27, -16, -30]],
+ ["seconds", [5, 7, 0, 27, 16, 30, 20], [-5, -7, 0, -27, -16, -30, -20]],
+ ["milliseconds", [5, 7, 0, 27, 16, 30, 20, 124], [-5, -7, 0, -27, -16, -30, -20, -124]],
+ ["microseconds", [5, 7, 0, 27, 16, 30, 20, 123, 988], [-5, -7, 0, -27, -16, -30, -20, -123, -987]],
+ ["nanoseconds", [5, 7, 0, 27, 16, 30, 20, 123, 987, 500], [-5, -7, 0, -27, -16, -30, -20, -123, -987, -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(
+ instance.round({ smallestUnit, relativeTo: relativeToForwards, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ instance.negated().round({ smallestUnit, relativeTo: relativeToBackwards, 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/Duration/prototype/round/roundingmode-halfEven.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-halfEven.js
new file mode 100644
index 0000000000..8db92f4c6c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-halfEven.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.duration.prototype.round
+description: Tests calculations with roundingMode "halfEven".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(5, 6, 7, 8, 40, 30, 20, 123, 987, 500);
+// Chosen such that 8 months forwards from relativeToForwards is the
+// same number of days as 8 months backwards from relativeToBackwards
+// (for convenience)
+const relativeToForwards = new Temporal.PlainDate(2020, 4, 1);
+const relativeToBackwards = new Temporal.PlainDate(2020, 12, 1);
+
+const expected = [
+ ["years", [6], [-6]],
+ ["months", [5, 8], [-5, -8]],
+ ["weeks", [5, 6, 8], [-5, -6, -8]],
+ ["days", [5, 7, 0, 28], [-5, -7, 0, -28]],
+ ["hours", [5, 7, 0, 27, 17], [-5, -7, 0, -27, -17]],
+ ["minutes", [5, 7, 0, 27, 16, 30], [-5, -7, 0, -27, -16, -30]],
+ ["seconds", [5, 7, 0, 27, 16, 30, 20], [-5, -7, 0, -27, -16, -30, -20]],
+ ["milliseconds", [5, 7, 0, 27, 16, 30, 20, 124], [-5, -7, 0, -27, -16, -30, -20, -124]],
+ ["microseconds", [5, 7, 0, 27, 16, 30, 20, 123, 988], [-5, -7, 0, -27, -16, -30, -20, -123, -988]],
+ ["nanoseconds", [5, 7, 0, 27, 16, 30, 20, 123, 987, 500], [-5, -7, 0, -27, -16, -30, -20, -123, -987, -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(
+ instance.round({ smallestUnit, relativeTo: relativeToForwards, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ instance.negated().round({ smallestUnit, relativeTo: relativeToBackwards, 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/Duration/prototype/round/roundingmode-halfExpand.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-halfExpand.js
new file mode 100644
index 0000000000..6eb118283d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-halfExpand.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.duration.prototype.round
+description: Tests calculations with roundingMode "halfExpand".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(5, 6, 7, 8, 40, 30, 20, 123, 987, 500);
+// Chosen such that 8 months forwards from relativeToForwards is the
+// same number of days as 8 months backwards from relativeToBackwards
+// (for convenience)
+const relativeToForwards = new Temporal.PlainDate(2020, 4, 1);
+const relativeToBackwards = new Temporal.PlainDate(2020, 12, 1);
+
+const expected = [
+ ["years", [6], [-6]],
+ ["months", [5, 8], [-5, -8]],
+ ["weeks", [5, 6, 8], [-5, -6, -8]],
+ ["days", [5, 7, 0, 28], [-5, -7, 0, -28]],
+ ["hours", [5, 7, 0, 27, 17], [-5, -7, 0, -27, -17]],
+ ["minutes", [5, 7, 0, 27, 16, 30], [-5, -7, 0, -27, -16, -30]],
+ ["seconds", [5, 7, 0, 27, 16, 30, 20], [-5, -7, 0, -27, -16, -30, -20]],
+ ["milliseconds", [5, 7, 0, 27, 16, 30, 20, 124], [-5, -7, 0, -27, -16, -30, -20, -124]],
+ ["microseconds", [5, 7, 0, 27, 16, 30, 20, 123, 988], [-5, -7, 0, -27, -16, -30, -20, -123, -988]],
+ ["nanoseconds", [5, 7, 0, 27, 16, 30, 20, 123, 987, 500], [-5, -7, 0, -27, -16, -30, -20, -123, -987, -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(
+ instance.round({ smallestUnit, relativeTo: relativeToForwards, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ instance.negated().round({ smallestUnit, relativeTo: relativeToBackwards, 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/Duration/prototype/round/roundingmode-halfFloor.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-halfFloor.js
new file mode 100644
index 0000000000..a5a1c47ddc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-halfFloor.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.duration.prototype.round
+description: Tests calculations with roundingMode "halfFloor".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(5, 6, 7, 8, 40, 30, 20, 123, 987, 500);
+// Chosen such that 8 months forwards from relativeToForwards is the
+// same number of days as 8 months backwards from relativeToBackwards
+// (for convenience)
+const relativeToForwards = new Temporal.PlainDate(2020, 4, 1);
+const relativeToBackwards = new Temporal.PlainDate(2020, 12, 1);
+
+const expected = [
+ ["years", [6], [-6]],
+ ["months", [5, 8], [-5, -8]],
+ ["weeks", [5, 6, 8], [-5, -6, -8]],
+ ["days", [5, 7, 0, 28], [-5, -7, 0, -28]],
+ ["hours", [5, 7, 0, 27, 17], [-5, -7, 0, -27, -17]],
+ ["minutes", [5, 7, 0, 27, 16, 30], [-5, -7, 0, -27, -16, -30]],
+ ["seconds", [5, 7, 0, 27, 16, 30, 20], [-5, -7, 0, -27, -16, -30, -20]],
+ ["milliseconds", [5, 7, 0, 27, 16, 30, 20, 124], [-5, -7, 0, -27, -16, -30, -20, -124]],
+ ["microseconds", [5, 7, 0, 27, 16, 30, 20, 123, 987], [-5, -7, 0, -27, -16, -30, -20, -123, -988]],
+ ["nanoseconds", [5, 7, 0, 27, 16, 30, 20, 123, 987, 500], [-5, -7, 0, -27, -16, -30, -20, -123, -987, -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(
+ instance.round({ smallestUnit, relativeTo: relativeToForwards, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ instance.negated().round({ smallestUnit, relativeTo: relativeToBackwards, 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/Duration/prototype/round/roundingmode-halfTrunc.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-halfTrunc.js
new file mode 100644
index 0000000000..3b267edb6f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-halfTrunc.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.duration.prototype.round
+description: Tests calculations with roundingMode "halfTrunc".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(5, 6, 7, 8, 40, 30, 20, 123, 987, 500);
+// Chosen such that 8 months forwards from relativeToForwards is the
+// same number of days as 8 months backwards from relativeToBackwards
+// (for convenience)
+const relativeToForwards = new Temporal.PlainDate(2020, 4, 1);
+const relativeToBackwards = new Temporal.PlainDate(2020, 12, 1);
+
+const expected = [
+ ["years", [6], [-6]],
+ ["months", [5, 8], [-5, -8]],
+ ["weeks", [5, 6, 8], [-5, -6, -8]],
+ ["days", [5, 7, 0, 28], [-5, -7, 0, -28]],
+ ["hours", [5, 7, 0, 27, 17], [-5, -7, 0, -27, -17]],
+ ["minutes", [5, 7, 0, 27, 16, 30], [-5, -7, 0, -27, -16, -30]],
+ ["seconds", [5, 7, 0, 27, 16, 30, 20], [-5, -7, 0, -27, -16, -30, -20]],
+ ["milliseconds", [5, 7, 0, 27, 16, 30, 20, 124], [-5, -7, 0, -27, -16, -30, -20, -124]],
+ ["microseconds", [5, 7, 0, 27, 16, 30, 20, 123, 987], [-5, -7, 0, -27, -16, -30, -20, -123, -987]],
+ ["nanoseconds", [5, 7, 0, 27, 16, 30, 20, 123, 987, 500], [-5, -7, 0, -27, -16, -30, -20, -123, -987, -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(
+ instance.round({ smallestUnit, relativeTo: relativeToForwards, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ instance.negated().round({ smallestUnit, relativeTo: relativeToBackwards, 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/Duration/prototype/round/roundingmode-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-invalid-string.js
new file mode 100644
index 0000000000..9f46a23197
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-invalid-string.js
@@ -0,0 +1,16 @@
+// |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.duration.prototype.round
+description: RangeError thrown when roundingMode option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(0, 0, 0, 0, 12, 34, 56, 123, 987, 500);
+for (const roundingMode of ["other string", "cile", "CEIL", "ce\u0131l", "auto", "halfexpand", "floor\0"]) {
+ assert.throws(RangeError, () => duration.round({ smallestUnit: "microsecond", roundingMode }));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-trunc.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-trunc.js
new file mode 100644
index 0000000000..69bb096f63
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-trunc.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.duration.prototype.round
+description: Tests calculations with roundingMode "trunc".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(5, 6, 7, 8, 40, 30, 20, 123, 987, 500);
+// Chosen such that 8 months forwards from relativeToForwards is the
+// same number of days as 8 months backwards from relativeToBackwards
+// (for convenience)
+const relativeToForwards = new Temporal.PlainDate(2020, 4, 1);
+const relativeToBackwards = new Temporal.PlainDate(2020, 12, 1);
+
+const expected = [
+ ["years", [5], [-5]],
+ ["months", [5, 7], [-5, -7]],
+ ["weeks", [5, 6, 8], [-5, -6, -8]],
+ ["days", [5, 7, 0, 27], [-5, -7, 0, -27]],
+ ["hours", [5, 7, 0, 27, 16], [-5, -7, 0, -27, -16]],
+ ["minutes", [5, 7, 0, 27, 16, 30], [-5, -7, 0, -27, -16, -30]],
+ ["seconds", [5, 7, 0, 27, 16, 30, 20], [-5, -7, 0, -27, -16, -30, -20]],
+ ["milliseconds", [5, 7, 0, 27, 16, 30, 20, 123], [-5, -7, 0, -27, -16, -30, -20, -123]],
+ ["microseconds", [5, 7, 0, 27, 16, 30, 20, 123, 987], [-5, -7, 0, -27, -16, -30, -20, -123, -987]],
+ ["nanoseconds", [5, 7, 0, 27, 16, 30, 20, 123, 987, 500], [-5, -7, 0, -27, -16, -30, -20, -123, -987, -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(
+ instance.round({ smallestUnit, relativeTo: relativeToForwards, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ instance.negated().round({ smallestUnit, relativeTo: relativeToBackwards, 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/Duration/prototype/round/roundingmode-undefined.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-undefined.js
new file mode 100644
index 0000000000..03080d62e9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-undefined.js
@@ -0,0 +1,29 @@
+// |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.duration.prototype.round
+description: Fallback value for roundingMode option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(0, 0, 0, 0, 12, 34, 56, 123, 987, 500);
+
+const explicit1 = duration.round({ smallestUnit: "microsecond", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit1, 0, 0, 0, 0, 12, 34, 56, 123, 988, 0, "default roundingMode is halfExpand");
+const implicit1 = duration.round({ smallestUnit: "microsecond" });
+TemporalHelpers.assertDuration(implicit1, 0, 0, 0, 0, 12, 34, 56, 123, 988, 0, "default roundingMode is halfExpand");
+
+const explicit2 = duration.round({ smallestUnit: "millisecond", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit2, 0, 0, 0, 0, 12, 34, 56, 124, 0, 0, "default roundingMode is halfExpand");
+const implicit2 = duration.round({ smallestUnit: "millisecond" });
+TemporalHelpers.assertDuration(implicit2, 0, 0, 0, 0, 12, 34, 56, 124, 0, 0, "default roundingMode is halfExpand");
+
+const explicit3 = duration.round({ smallestUnit: "second", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit3, 0, 0, 0, 0, 12, 34, 56, 0, 0, 0, "default roundingMode is halfExpand");
+const implicit3 = duration.round({ smallestUnit: "second" });
+TemporalHelpers.assertDuration(implicit3, 0, 0, 0, 0, 12, 34, 56, 0, 0, 0, "default roundingMode is halfExpand");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-wrong-type.js
new file mode 100644
index 0000000000..dd705dc3f6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-wrong-type.js
@@ -0,0 +1,18 @@
+// |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.duration.prototype.round
+description: Type conversions for roundingMode option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(0, 0, 0, 0, 12, 34, 56, 123, 987, 500);
+TemporalHelpers.checkStringOptionWrongType("roundingMode", "halfExpand",
+ (roundingMode) => duration.round({ smallestUnit: "microsecond", roundingMode }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 12, 34, 56, 123, 988, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundto-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundto-invalid-string.js
new file mode 100644
index 0000000000..4b834a68fc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundto-invalid-string.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.duration.prototype.round
+description: RangeError thrown when smallestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(0, 0, 0, 0, 12, 34, 56, 123, 987, 500);
+const badValues = [
+ "era",
+ "eraYear",
+ "millisecond\0",
+ "mill\u0131second",
+ "SECOND",
+ "eras",
+ "eraYears",
+ "milliseconds\0",
+ "mill\u0131seconds",
+ "SECONDS",
+ "other string",
+];
+for (const smallestUnit of badValues) {
+ assert.throws(RangeError, () => duration.round(smallestUnit),
+ `"${smallestUnit}" is not a valid value for smallest unit`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/shell.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/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/Duration/prototype/round/smallestunit-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/smallestunit-invalid-string.js
new file mode 100644
index 0000000000..e14aadb0ed
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/smallestunit-invalid-string.js
@@ -0,0 +1,30 @@
+// |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.duration.prototype.round
+description: RangeError thrown when smallestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(0, 0, 0, 0, 12, 34, 56, 123, 987, 500);
+const badValues = [
+ "era",
+ "eraYear",
+ "millisecond\0",
+ "mill\u0131second",
+ "SECOND",
+ "eras",
+ "eraYears",
+ "milliseconds\0",
+ "mill\u0131seconds",
+ "SECONDS",
+ "other string",
+];
+for (const smallestUnit of badValues) {
+ assert.throws(RangeError, () => duration.round({ smallestUnit }),
+ `"${smallestUnit}" is not a valid value for smallest unit`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/smallestunit-plurals-accepted-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/smallestunit-plurals-accepted-string.js
new file mode 100644
index 0000000000..8e05128aab
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/smallestunit-plurals-accepted-string.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.duration.prototype.round
+description: Plural units are accepted as well for the shorthand for the smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal, arrow-function]
+---*/
+
+const duration = new Temporal.Duration(0, 0, 0, 4, 5, 6, 7, 987, 654, 321);
+const validUnits = [
+ "day",
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+];
+TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => duration.round(smallestUnit), validUnits);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/smallestunit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/smallestunit-plurals-accepted.js
new file mode 100644
index 0000000000..2dce34e1e2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/smallestunit-plurals-accepted.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.duration.prototype.round
+description: Plural units are accepted as well for the smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+const relativeTo = new Temporal.PlainDate(2000, 1, 1);
+const validUnits = [
+ "year",
+ "month",
+ "week",
+ "day",
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+];
+TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => duration.round({ smallestUnit, relativeTo }), validUnits);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/smallestunit-string-shorthand-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/smallestunit-string-shorthand-string.js
new file mode 100644
index 0000000000..8d974d1f93
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/smallestunit-string-shorthand-string.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.duration.prototype.round
+description: String as first argument is equivalent to options bag with smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal, arrow-function]
+---*/
+
+const instance = new Temporal.Duration(0, 0, 0, 4, 5, 6, 7, 987, 654, 321);
+const validUnits = [
+ "day",
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+];
+validUnits.forEach((smallestUnit) => {
+ const full = instance.round({ smallestUnit });
+ const shorthand = instance.round(smallestUnit);
+ TemporalHelpers.assertDurationsEqual(shorthand, full, `"${smallestUnit}" as first argument to round is equivalent to options bag`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/smallestunit-undefined.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/smallestunit-undefined.js
new file mode 100644
index 0000000000..0abc556c0a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/smallestunit-undefined.js
@@ -0,0 +1,18 @@
+// |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.duration.prototype.round
+description: Fallback value for smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(0, 0, 0, 0, 1, 2, 3, 123, 456, 789);
+const explicit1 = duration.round({ largestUnit: "day", smallestUnit: undefined });
+TemporalHelpers.assertDuration(explicit1, 0, 0, 0, 0, 1, 2, 3, 123, 456, 789, "default smallestUnit is nanosecond");
+const implicit1 = duration.round({ largestUnit: "day" });
+TemporalHelpers.assertDuration(implicit1, 0, 0, 0, 0, 1, 2, 3, 123, 456, 789, "default smallestUnit is nanosecond");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/smallestunit-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/smallestunit-wrong-type.js
new file mode 100644
index 0000000000..ba79e1aff1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/smallestunit-wrong-type.js
@@ -0,0 +1,18 @@
+// |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.duration.prototype.round
+description: Type conversions for smallestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(0, 0, 0, 0, 12, 34, 56, 123, 987, 500);
+TemporalHelpers.checkStringOptionWrongType("smallestUnit", "microsecond",
+ (smallestUnit) => duration.round({ smallestUnit }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 12, 34, 56, 123, 988, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/smallestunit.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/smallestunit.js
new file mode 100644
index 0000000000..95a0ba458e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/smallestunit.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.duration.prototype.round
+description: smallestUnit should be taken into account
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const d = Temporal.Duration.from({
+ days: 1,
+ hours: 2,
+ minutes: 3,
+ seconds: 4,
+ milliseconds: 5,
+ microseconds: 6,
+ nanoseconds: 7
+});
+const tests = {
+ 'day': [0, 0, 0, 1, 0, 0, 0, 0, 0, 0],
+ 'hour': [0, 0, 0, 1, 2, 0, 0, 0, 0, 0],
+ 'minute': [0, 0, 0, 1, 2, 3, 0, 0, 0, 0],
+ 'second': [0, 0, 0, 1, 2, 3, 4, 0, 0, 0],
+ 'millisecond': [0, 0, 0, 1, 2, 3, 4, 5, 0, 0],
+ 'microsecond': [0, 0, 0, 1, 2, 3, 4, 5, 6, 0],
+ 'nanosecond': [0, 0, 0, 1, 2, 3, 4, 5, 6, 7],
+};
+for (const [smallestUnit, expected] of Object.entries(tests)) {
+ TemporalHelpers.assertDuration(d.round(smallestUnit), ...expected,
+ `"${smallestUnit}" should work as argument`);
+ TemporalHelpers.assertDuration(d.round({ smallestUnit }), ...expected,
+ `"${smallestUnit}" should work in option bag`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/subclassing-ignored.js
new file mode 100644
index 0000000000..5f4450c0bd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/subclassing-ignored.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.duration.prototype.round
+description: Objects of a subclass are never created as return values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.Duration,
+ [0, 0, 0, 4, 5, 6, 7, 987, 654, 321],
+ "round",
+ [{ smallestUnit: 'seconds' }],
+ (result) => TemporalHelpers.assertDuration(result, 0, 0, 0, 4, 5, 6, 8, 0, 0, 0),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/throws-in-balance-duration-when-sign-mismatched-with-zoned-date-time.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/throws-in-balance-duration-when-sign-mismatched-with-zoned-date-time.js
new file mode 100644
index 0000000000..330e694fc7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/throws-in-balance-duration-when-sign-mismatched-with-zoned-date-time.js
@@ -0,0 +1,51 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.round
+description: >
+ BalanceDuration throws when the duration signs don't match.
+info: |
+ BalanceDuration ( days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds,
+ largestUnit [ , relativeTo ] )
+
+ ...
+ 4. If largestUnit is one of "year", "month", "week", or "day", then
+ a. Let result be ? NanosecondsToDays(nanoseconds, relativeTo).
+ b. Set days to result.[[Days]].
+ c. Set nanoseconds to result.[[Nanoseconds]].
+ ...
+ 15. Return ? CreateTimeDurationRecord(days, hours × sign, minutes × sign, seconds × sign,
+ milliseconds × sign, microseconds × sign, nanoseconds × sign).
+features: [Temporal]
+---*/
+
+let duration = Temporal.Duration.from({
+ hours: -(24 * 1),
+ nanoseconds: -1,
+});
+
+let tz = new class extends Temporal.TimeZone {
+ #getPossibleInstantsFor = 0;
+
+ getPossibleInstantsFor(dt) {
+ this.#getPossibleInstantsFor++;
+
+ if (this.#getPossibleInstantsFor === 1) {
+ return [new Temporal.Instant(-86400_000_000_000n - 2n)]
+ }
+ return super.getPossibleInstantsFor(dt);
+ }
+}("UTC");
+
+let zdt = new Temporal.ZonedDateTime(0n, tz, "iso8601");
+
+let options = {
+ relativeTo: zdt,
+ largestUnit: "days",
+};
+
+assert.throws(RangeError, () => duration.round(options));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/throws-in-unbalance-duration-relative-when-sign-mismatched.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/throws-in-unbalance-duration-relative-when-sign-mismatched.js
new file mode 100644
index 0000000000..253ce0fa1b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/throws-in-unbalance-duration-relative-when-sign-mismatched.js
@@ -0,0 +1,38 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.round
+description: >
+ UnbalanceDurationRelative throws a RangeError when duration signs don't match.
+features: [Temporal]
+---*/
+
+var duration = Temporal.Duration.from({
+ years: 1,
+ months: 0,
+ weeks: 1,
+});
+
+var cal = new class extends Temporal.Calendar {
+ called = 0;
+ dateUntil(one, two, options) {
+ ++this.called;
+ var result = super.dateUntil(one, two, options);
+ return this.called === 1 ? result.negated() : result;
+ }
+}("iso8601");
+
+var relativeTo = new Temporal.PlainDateTime(1970, 1, 1, 0, 0, 0, 0, 0, 0, cal);
+assert.sameValue(relativeTo.getCalendar(), cal);
+
+var options = {
+ smallestUnit: "days",
+ largestUnit: "month",
+ relativeTo,
+};
+
+assert.throws(RangeError, () => duration.round(options));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/timezone-getpossibleinstantsfor-iterable.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/timezone-getpossibleinstantsfor-iterable.js
new file mode 100644
index 0000000000..ee08ff07c9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/timezone-getpossibleinstantsfor-iterable.js
@@ -0,0 +1,47 @@
+// |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.duration.prototype.round
+description: An iterable returned from timeZone.getPossibleInstantsFor is consumed after each call
+info: |
+ sec-temporal.duration.prototype.round steps 19, 21, and 24:
+ 19. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_).
+ 21. Let _roundResult_ be ? RoundDuration(_unbalanceResult_.[[Years]], [...], _unbalanceResult_.[[Days]], _duration_.[[Hours]], [...], _duration_.[[Nanoseconds]], _roundingIncrement_, _smallestUnit_, _roundingMode_, _relativeTo_).
+ 24. If _relativeTo_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ a. Set _relativeTo_ to ? MoveRelativeZonedDateTime(_relativeTo_, _balanceResult_.[[Years]], _balanceResult_.[[Months]], _balanceResult_.[[Weeks]], 0).
+ sec-temporal-torelativetemporalobject step 6.d:
+ d. Let _epochNanoseconds_ be ? InterpretISODateTimeOffset(_result_.[[Year]], [...], _result_.[[Nanosecond]], _offsetNs_, _timeZone_, *"compatible"*, *"reject"*).
+ sec-temporal-interpretisodatetimeoffset step 7:
+ 7. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_).
+ sec-temporal-roundduration step 5.c–d:
+ c. If _zonedRelativeTo_ is not *undefined*, then
+ i. Let _intermediate_ be ? MoveRelativeZonedDateTime(_zonedRelativeTo_, _years_, _months_, _weeks_, _days_).
+ d. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _intermediate_).
+ sec-temporal-moverelativezoneddatetime step 1:
+ 1. Let _intermediateNs_ be ? AddZonedDateTime(_zonedDateTime_.[[Nanoseconds]], _zonedDateTime_.[[TimeZone]], _zonedDateTime_.[[Calendar]], _years_, _months_, _weeks_, _days_, 0, 0, 0, 0, 0, 0).
+ sec-temporal-nanosecondstodays step 13:
+ 13. Let _intermediateNs_ be ? AddZonedDateTime(_startNs_, _relativeTo_.[[TimeZone]], _relativeTo_.[[Calendar]], 0, 0, 0, _days_, 0, 0, 0, 0, 0, 0).
+ sec-temporal-addzoneddatetime step 8:
+ 8. Let _intermediateInstant_ be ? BuiltinTimeZoneGetInstantFor(_timeZone_, _intermediateDateTime_, *"compatible"*).
+ sec-temporal-builtintimezonegetinstantfor step 1:
+ 1. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_).
+ sec-temporal-getpossibleinstantsfor step 2:
+ 2. Let _list_ be ? IterableToList(_possibleInstants_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "2000-01-01T00:00:00", // called once on the input relativeTo object
+ "2001-02-09T00:00:00", // called once on relativeTo plus years, months, weeks, days from the receiver
+ "2001-02-10T00:00:00", // called once on the previous value plus the calendar days difference between that and the time part of the duration
+];
+
+TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => {
+ const duration = new Temporal.Duration(1, 1, 1, 1, 1, 1, 1);
+ duration.round({ smallestUnit: 'months', relativeTo: { year: 2000, month: 1, day: 1, timeZone } });
+}, expected);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/total-duration-nanoseconds-too-large-with-zoned-datetime.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/total-duration-nanoseconds-too-large-with-zoned-datetime.js
new file mode 100644
index 0000000000..f931cfe519
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/total-duration-nanoseconds-too-large-with-zoned-datetime.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.round
+description: >
+ NanosecondsToDays throws a RangeError when the number of nanoseconds is too large.
+features: [Temporal]
+---*/
+
+var duration = Temporal.Duration.from({
+ seconds: Number.MAX_SAFE_INTEGER,
+});
+
+var zonedDateTime = new Temporal.ZonedDateTime(0n, "UTC");
+
+var options = {
+ smallestUnit: "day",
+ largestUnit: "day",
+ relativeTo: zonedDateTime,
+};
+
+assert.throws(RangeError, () => duration.round(options));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/year-zero.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/year-zero.js
new file mode 100644
index 0000000000..22ba1c3558
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/year-zero.js
@@ -0,0 +1,20 @@
+// |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.duration.prototype.round
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const instance = new Temporal.Duration(1, 0, 0, 0, 24);
+
+let relativeTo = "-000000-11-04T00:00";
+assert.throws(
+ RangeError,
+ () => { instance.round({ largestUnit: "years", relativeTo }); },
+ "reject minus zero as extended year"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/zero-day-length.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/zero-day-length.js
new file mode 100644
index 0000000000..454d3c779d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/zero-day-length.js
@@ -0,0 +1,43 @@
+// |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.duration.prototype.round
+description: A malicious time zone resulting a day length of zero is handled correctly
+info: |
+ Based on a test by André Bargull.
+
+ RoundDuration step 6:
+ d. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _intermediate_).
+ e. Set _days_ to _days_ + _result_.[[Days]] + _result_.[[Nanoseconds]] / _result_.[[DayLength]].
+
+ NanosecondsToDays steps 19-23:
+ 19. If _days_ < 0 and _sign_ = 1, throw a *RangeError* exception.
+ 20. If _days_ > 0 and _sign_ = -1, throw a *RangeError* exception.
+ 21. If _nanoseconds_ < 0, then
+ a. Assert: sign is -1.
+ 22. If _nanoseconds_ > 0 and _sign_ = -1, throw a *RangeError* exception.
+ 23. Assert: The inequality abs(_nanoseconds_) < abs(_dayLengthNs_) holds.
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(0, 0, 0, 0, -24, 0, 0, 0, 0, -1);
+
+const tz = new class extends Temporal.TimeZone {
+ #getPossibleInstantsForCalls = 0;
+
+ getPossibleInstantsFor(dt) {
+ this.#getPossibleInstantsForCalls++;
+
+ if (this.#getPossibleInstantsForCalls <= 2) {
+ return [new Temporal.Instant(-86400_000_000_000n - 2n)]
+ }
+ return super.getPossibleInstantsFor(dt);
+ }
+}("UTC");
+
+const relativeTo = new Temporal.ZonedDateTime(0n, tz, "iso8601");
+assert.throws(RangeError, () => instance.round({ relativeTo, smallestUnit: "days" }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/zero-year-month-week-length.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/zero-year-month-week-length.js
new file mode 100644
index 0000000000..0751687ccc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/zero-year-month-week-length.js
@@ -0,0 +1,34 @@
+// |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.duration.prototype.round
+description: >
+ A malicious calendar resulting in a year, month, or week length of zero is
+ handled correctly
+info: |
+ RoundDuration
+ 10.z. If _oneYearDays_ = 0, throw a *RangeError* exception.
+ ...
+ 11.z. If _oneMonthDays_ = 0, throw a *RangeError* exception.
+ ...
+ 12.s. If _oneWeekDays_ = 0, throw a *RangeError* exception.
+features: [Temporal]
+---*/
+
+const cal = new class extends Temporal.Calendar {
+ dateAdd(date, duration, options) {
+ // Called several times, last call sets oneYear/Month/WeekDays to 0
+ return new Temporal.PlainDate(1970, 1, 1);
+ }
+}("iso8601");
+
+const instance = new Temporal.Duration(1, 0, 0, 0, 0, 0, 0, 0, 0, 1);
+const relativeTo = new Temporal.ZonedDateTime(0n, "UTC", cal);
+
+assert.throws(RangeError, () => instance.round({ relativeTo, smallestUnit: "years" }), "zero year length handled correctly");
+assert.throws(RangeError, () => instance.round({ relativeTo, smallestUnit: "months" }), "zero month length handled correctly");
+assert.throws(RangeError, () => instance.round({ relativeTo, smallestUnit: "weeks" }), "zero week length handled correctly");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/seconds/branding.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/seconds/branding.js
new file mode 100644
index 0000000000..8437008114
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/seconds/branding.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-get-temporal.duration.prototype.seconds
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const seconds = Object.getOwnPropertyDescriptor(Temporal.Duration.prototype, "seconds").get;
+
+assert.sameValue(typeof seconds, "function");
+
+assert.throws(TypeError, () => seconds.call(undefined), "undefined");
+assert.throws(TypeError, () => seconds.call(null), "null");
+assert.throws(TypeError, () => seconds.call(true), "true");
+assert.throws(TypeError, () => seconds.call(""), "empty string");
+assert.throws(TypeError, () => seconds.call(Symbol()), "symbol");
+assert.throws(TypeError, () => seconds.call(1), "1");
+assert.throws(TypeError, () => seconds.call({}), "plain object");
+assert.throws(TypeError, () => seconds.call(Temporal.Duration), "Temporal.Duration");
+assert.throws(TypeError, () => seconds.call(Temporal.Duration.prototype), "Temporal.Duration.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/seconds/browser.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/seconds/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/seconds/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/seconds/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/seconds/prop-desc.js
new file mode 100644
index 0000000000..7cedceb0d9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/seconds/prop-desc.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-get-temporal.duration.prototype.seconds
+description: The "seconds" property of Temporal.Duration.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.Duration.prototype, "seconds");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/seconds/shell.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/seconds/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/seconds/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/shell.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/sign/branding.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/sign/branding.js
new file mode 100644
index 0000000000..39d42d77a1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/sign/branding.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-get-temporal.duration.prototype.sign
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const sign = Object.getOwnPropertyDescriptor(Temporal.Duration.prototype, "sign").get;
+
+assert.sameValue(typeof sign, "function");
+
+assert.throws(TypeError, () => sign.call(undefined), "undefined");
+assert.throws(TypeError, () => sign.call(null), "null");
+assert.throws(TypeError, () => sign.call(true), "true");
+assert.throws(TypeError, () => sign.call(""), "empty string");
+assert.throws(TypeError, () => sign.call(Symbol()), "symbol");
+assert.throws(TypeError, () => sign.call(1), "1");
+assert.throws(TypeError, () => sign.call({}), "plain object");
+assert.throws(TypeError, () => sign.call(Temporal.Duration), "Temporal.Duration");
+assert.throws(TypeError, () => sign.call(Temporal.Duration.prototype), "Temporal.Duration.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/sign/browser.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/sign/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/sign/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/sign/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/sign/prop-desc.js
new file mode 100644
index 0000000000..aad5c3a145
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/sign/prop-desc.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-get-temporal.duration.prototype.sign
+description: The "sign" property of Temporal.Duration.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.Duration.prototype, "sign");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/sign/shell.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/sign/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/sign/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/argument-duration-max.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/argument-duration-max.js
new file mode 100644
index 0000000000..138f6af4d2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/argument-duration-max.js
@@ -0,0 +1,45 @@
+// |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.duration.prototype.subtract
+description: Maximum allowed duration
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration();
+
+const maxCases = [
+ ["P104249991374DT7H36M31.999999999S", "string with max days"],
+ [{ days: 104249991374, nanoseconds: 27391999999999 }, "property bag with max days"],
+ ["PT2501999792983H36M31.999999999S", "string with max hours"],
+ [{ hours: 2501999792983, nanoseconds: 2191999999999 }, "property bag with max hours"],
+ ["PT150119987579016M31.999999999S", "string with max minutes"],
+ [{ minutes: 150119987579016, nanoseconds: 31999999999 }, "property bag with max minutes"],
+ ["PT9007199254740991.999999999S", "string with max seconds"],
+ [{ seconds: 9007199254740991, nanoseconds: 999999999 }, "property bag with max seconds"],
+];
+
+for (const [arg, descr] of maxCases) {
+ const result = instance.subtract(arg);
+ assert.sameValue(result.with({ years: 0, months: 0, weeks: 0 }).total("seconds"), -9007199254740991.999999999, `operation succeeds with ${descr}`);
+}
+
+const minCases = [
+ ["-P104249991374DT7H36M31.999999999S", "string with min days"],
+ [{ days: -104249991374, nanoseconds: -27391999999999 }, "property bag with min days"],
+ ["-PT2501999792983H36M31.999999999S", "string with min hours"],
+ [{ hours: -2501999792983, nanoseconds: -2191999999999 }, "property bag with min hours"],
+ ["-PT150119987579016M31.999999999S", "string with min minutes"],
+ [{ minutes: -150119987579016, nanoseconds: -31999999999 }, "property bag with min minutes"],
+ ["-PT9007199254740991.999999999S", "string with min seconds"],
+ [{ seconds: -9007199254740991, nanoseconds: -999999999 }, "property bag with min seconds"],
+];
+
+for (const [arg, descr] of minCases) {
+ const result = instance.subtract(arg);
+ assert.sameValue(result.with({ years: 0, months: 0, weeks: 0 }).total("seconds"), 9007199254740991.999999999, `operation succeeds with ${descr}`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/argument-duration-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/argument-duration-out-of-range.js
new file mode 100644
index 0000000000..83a188f5fc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/argument-duration-out-of-range.js
@@ -0,0 +1,75 @@
+// |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.duration.prototype.subtract
+description: Duration-like argument that is out of range
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration();
+
+const cases = [
+ // 2^32 = 4294967296
+ ["P4294967296Y", "string with years > max"],
+ [{ years: 4294967296 }, "property bag with years > max"],
+ ["-P4294967296Y", "string with years < min"],
+ [{ years: -4294967296 }, "property bag with years < min"],
+ ["P4294967296M", "string with months > max"],
+ [{ months: 4294967296 }, "property bag with months > max"],
+ ["-P4294967296M", "string with months < min"],
+ [{ months: -4294967296 }, "property bag with months < min"],
+ ["P4294967296W", "string with weeks > max"],
+ [{ weeks: 4294967296 }, "property bag with weeks > max"],
+ ["-P4294967296W", "string with weeks < min"],
+ [{ weeks: -4294967296 }, "property bag with weeks < min"],
+
+ // ceil(max safe integer / 86400) = 104249991375
+ ["P104249991375D", "string with days > max"],
+ [{ days: 104249991375 }, "property bag with days > max"],
+ ["P104249991374DT24H", "string where hours balance into days > max"],
+ [{ days: 104249991374, hours: 24 }, "property bag where hours balance into days > max"],
+ ["-P104249991375D", "string with days < min"],
+ [{ days: -104249991375 }, "property bag with days < min"],
+ ["-P104249991374DT24H", "string where hours balance into days < min"],
+ [{ days: -104249991374, hours: -24 }, "property bag where hours balance into days < min"],
+
+ // ceil(max safe integer / 3600) = 2501999792984
+ ["PT2501999792984H", "string with hours > max"],
+ [{ hours: 2501999792984 }, "property bag with hours > max"],
+ ["PT2501999792983H60M", "string where minutes balance into hours > max"],
+ [{ hours: 2501999792983, minutes: 60 }, "property bag where minutes balance into hours > max"],
+ ["-PT2501999792984H", "string with hours < min"],
+ [{ hours: -2501999792984 }, "property bag with hours < min"],
+ ["-PT2501999792983H60M", "string where minutes balance into hours < min"],
+ [{ hours: -2501999792983, minutes: -60 }, "property bag where minutes balance into hours < min"],
+
+ // ceil(max safe integer / 60) = 150119987579017
+ ["PT150119987579017M", "string with minutes > max"],
+ [{ minutes: 150119987579017 }, "property bag with minutes > max"],
+ ["PT150119987579016M60S", "string where seconds balance into minutes > max"],
+ [{ minutes: 150119987579016, seconds: 60 }, "property bag where seconds balance into minutes > max"],
+ ["-PT150119987579017M", "string with minutes < min"],
+ [{ minutes: -150119987579017 }, "property bag with minutes < min"],
+ ["-PT150119987579016M60S", "string where seconds balance into minutes < min"],
+ [{ minutes: -150119987579016, seconds: -60 }, "property bag where seconds balance into minutes < min"],
+
+ // 2^53 = 9007199254740992
+ ["PT9007199254740992S", "string with seconds > max"],
+ [{ seconds: 9007199254740992 }, "property bag with seconds > max"],
+ [{ seconds: 9007199254740991, milliseconds: 1000 }, "property bag where milliseconds balance into seconds > max"],
+ [{ seconds: 9007199254740991, microseconds: 1000000 }, "property bag where microseconds balance into seconds > max"],
+ [{ seconds: 9007199254740991, nanoseconds: 1000000000 }, "property bag where nanoseconds balance into seconds > max"],
+ ["-PT9007199254740992S", "string with seconds < min"],
+ [{ seconds: -9007199254740992 }, "property bag with seconds < min"],
+ [{ seconds: -9007199254740991, milliseconds: -1000 }, "property bag where milliseconds balance into seconds < min"],
+ [{ seconds: -9007199254740991, microseconds: -1000000 }, "property bag where microseconds balance into seconds < min"],
+ [{ seconds: -9007199254740991, nanoseconds: -1000000000 }, "property bag where nanoseconds balance into seconds < min"],
+];
+
+for (const [arg, descr] of cases) {
+ assert.throws(RangeError, () => instance.subtract(arg), `${descr} is out of range`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/argument-duration-precision-exact-numerical-values.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/argument-duration-precision-exact-numerical-values.js
new file mode 100644
index 0000000000..7c78199dfe
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/argument-duration-precision-exact-numerical-values.js
@@ -0,0 +1,79 @@
+// |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.duration.prototype.subtract
+description: >
+ Duration-like argument performs the range check with minimal floating point
+ precision loss
+features: [Temporal]
+---*/
+
+// Based on a test case by André Bargull
+
+const instance = new Temporal.Duration();
+
+const balanceFailCases = [
+ [
+ {
+ milliseconds: 4503599627370497_000, // ℝ(𝔽(4503599627370497000)) = 4503599627370497024
+ microseconds: 4503599627370495_000000, // ℝ(𝔽(4503599627370495000000)) = 4503599627370494951424
+ },
+ // 4503599627370497024 / 1000 + 4503599627370494951424 / 1000000 is
+ // 9007199254740991.975424, which is below the limit of 2**53
+ "case where floating point inaccuracy brings total below limit, positive"
+ ],
+ [
+ {
+ milliseconds: -4503599627370497_000,
+ microseconds: -4503599627370495_000000,
+ },
+ "case where floating point inaccuracy brings total below limit, negative"
+ ],
+];
+
+// Adding a duration, even to a zero duration, causes rebalancing to the current
+// largestUnit. These cases will not fail when converting the property bag to a
+// duration, but they will fail during balancing after the addition when storing
+// the resulting duration, because:
+// 9007199254740991.975424 seconds balances into 9007199254740991975 ms, 424 µs
+// ℝ(𝔽(9007199254740991975)) ms = 9007199254740992000 ms
+// which is once again above the limit due to floating point inaccuracy.
+
+for (const [arg, descr] of balanceFailCases) {
+ assert.throws(RangeError, () => instance.subtract(arg), descr + ': ℝ(𝔽(x)) operation after balancing brings total over limit')
+}
+
+// These cases will balance to a largestUnit of seconds, which will not be
+// inaccurate.
+
+const balanceSuccessCases = [
+ [
+ {
+ seconds: 2,
+ milliseconds: 4503599627370496_500, // ℝ(𝔽(4503599627370496500)) = 4503599627370496512
+ microseconds: 4503599627370493_500000, // ℝ(𝔽(4503599627370493500000)) = 4503599627370493378560
+ },
+ // 1 + 4503599627370496512 / 1000 + 4503599627370493378560 / 1000000 is
+ // 9007199254740991.89056, which is below the limit of 2**53
+ "-PT9007199254740991.89056S",
+ "case where floating point inaccuracy brings total below limit, positive"
+ ],
+ [
+ {
+ seconds: -2,
+ milliseconds: -4503599627370496_500,
+ microseconds: -4503599627370493_500000,
+ },
+ "PT9007199254740991.89056S",
+ "case where floating point inaccuracy brings total below limit, negative"
+ ],
+];
+
+for (const [arg, string, descr] of balanceSuccessCases) {
+ const result = instance.subtract(arg);
+ assert.sameValue(result.toString(), string, descr);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/argument-invalid-property.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/argument-invalid-property.js
new file mode 100644
index 0000000000..a07a246d93
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/argument-invalid-property.js
@@ -0,0 +1,31 @@
+// |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.duration.prototype.subtract
+description: temporalDurationLike object must contain at least one correctly spelled property
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(0, 0, 0, 1, 2, 3, 4, 987, 654, 321);
+
+assert.throws(
+ TypeError,
+ () => instance.subtract({}),
+ "Throws TypeError if no property is present"
+);
+
+assert.throws(
+ TypeError,
+ () => instance.subtract({ nonsense: true }),
+ "Throws TypeError if no recognized property is present"
+);
+
+assert.throws(
+ TypeError,
+ () => instance.subtract({ sign: 1 }),
+ "Sign property is not recognized"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/argument-mixed-sign.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/argument-mixed-sign.js
new file mode 100644
index 0000000000..ca22e029fa
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/argument-mixed-sign.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.duration.prototype.subtract
+description: Positive and negative values in the temporalDurationLike argument are not acceptable
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(0, 0, 0, 1, 2, 3, 4, 987, 654, 321);
+
+assert.throws(
+ RangeError,
+ () => instance.subtract({ hours: 1, minutes: -30 }),
+ `mixed positive and negative values always throw`
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/argument-not-object.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/argument-not-object.js
new file mode 100644
index 0000000000..c9ae577a55
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/argument-not-object.js
@@ -0,0 +1,22 @@
+// |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.duration.prototype.subtract
+description: Passing a primitive other than string to subtract() throws
+features: [Symbol, Temporal]
+---*/
+
+const instance = new Temporal.Duration(0, 0, 0, 1, 2, 3, 4, 987, 654, 321);
+assert.throws(TypeError, () => instance.subtract(undefined), "undefined");
+assert.throws(TypeError, () => instance.subtract(null), "null");
+assert.throws(TypeError, () => instance.subtract(true), "boolean");
+assert.throws(RangeError, () => instance.subtract(""), "empty string");
+assert.throws(TypeError, () => instance.subtract(Symbol()), "Symbol");
+assert.throws(TypeError, () => instance.subtract(7), "number");
+assert.throws(TypeError, () => instance.subtract(7n), "bigint");
+assert.throws(TypeError, () => instance.subtract([]), "array");
+assert.throws(TypeError, () => instance.subtract(() => {}), "function");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/argument-singular-properties.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/argument-singular-properties.js
new file mode 100644
index 0000000000..eada533695
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/argument-singular-properties.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.duration.prototype.subtract
+description: Singular properties in the property bag are always ignored
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(0, 0, 0, 1, 2, 3, 4, 987, 654, 321);
+
+[
+ { year: 1 },
+ { month: 2 },
+ { week: 3 },
+ { day: 4 },
+ { hour: 5 },
+ { minute: 6 },
+ { second: 7 },
+ { millisecond: 8 },
+ { microsecond: 9 },
+ { nanosecond: 10 },
+].forEach((badObject) => {
+ assert.throws(TypeError, () => instance.subtract(badObject),
+ "Throw TypeError if temporalDurationLike is not valid");
+});
+
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/argument-string-fractional-units-rounding-mode.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/argument-string-fractional-units-rounding-mode.js
new file mode 100644
index 0000000000..0b1af87ac2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/argument-string-fractional-units-rounding-mode.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.duration.prototype.subtract
+description: Strings with fractional duration units are rounded with the correct rounding mode
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const blank = new Temporal.Duration();
+
+TemporalHelpers.assertDuration(blank.subtract("PT1.03125H"), 0, 0, 0, 0, -1, -1, -52, -500, 0, 0,
+ "positive fractional units rounded with correct rounding mode");
+TemporalHelpers.assertDuration(blank.subtract("-PT1.03125H"), 0, 0, 0, 0, 1, 1, 52, 500, 0, 0,
+ "negative fractional units rounded with correct rounding mode");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/argument-string-negative-fractional-units.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/argument-string-negative-fractional-units.js
new file mode 100644
index 0000000000..2fe54f7ec8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/argument-string-negative-fractional-units.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.duration.prototype.subtract
+description: Strings with fractional duration units are treated with the correct sign
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration();
+
+const resultHours = instance.subtract("-PT24.567890123H");
+TemporalHelpers.assertDuration(resultHours, 0, 0, 0, 0, 24, 34, 4, 404, 442, 800, "negative fractional hours");
+
+const resultMinutes = instance.subtract("-PT1440.567890123M");
+TemporalHelpers.assertDuration(resultMinutes, 0, 0, 0, 0, 0, 1440, 34, 73, 407, 380, "negative fractional minutes");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/argument-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/argument-string.js
new file mode 100644
index 0000000000..4f0b6e27a0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/argument-string.js
@@ -0,0 +1,17 @@
+// |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.duration.prototype.subtract
+description: String arguments are supported.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const duration = Temporal.Duration.from({ days: 3, hours: 1, minutes: 10 });
+const result = duration.subtract('P1DT5M');
+TemporalHelpers.assertDuration(result, 0, 0, 0, 2, 1, 5, 0, 0, 0, 0, "String argument should be supported");
+assert.throws(RangeError, () => duration.subtract("2DT5M"), "Invalid string argument should throw");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/balance-negative-result.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/balance-negative-result.js
new file mode 100644
index 0000000000..2731e748ac
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/balance-negative-result.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.duration.prototype.subtract
+description: A negative duration result is balanced correctly by the modulo operation in NanosecondsToDays
+info: |
+ sec-temporal-nanosecondstodays step 6:
+ 6. If Type(_relativeTo_) is not Object or _relativeTo_ does not have an [[InitializedTemporalZonedDateTime]] internal slot, then
+ a. Return the new Record { ..., [[Nanoseconds]]: abs(_nanoseconds_) modulo _dayLengthNs_ × _sign_, ... }.
+ sec-temporal-balanceduration step 4:
+ 4. If _largestUnit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*, then
+ a. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _relativeTo_).
+ sec-temporal-addduration steps 5–6:
+ 5. If _relativeTo_ is *undefined*, then
+ ...
+ b. Let _result_ be ! BalanceDuration(_d1_ + _d2_, _h1_ + _h2_, _min1_ + _min2_, _s1_ + _s2_, _ms1_ + _ms2_, _mus1_ + _mus2_, _ns1_ + _ns2_, _largestUnit_).
+ ...
+ 6. Else if _relativeTo_ has an [[InitializedTemporalPlainDateTime]] internal slot, then
+ ...
+ n. Let _result_ be ! BalanceDuration(_dateDifference_.[[Days]], _h1_ + _h2_, _min1_ + _min2_, _s1_ + _s2_, _ms1_ + _ms2_, _mus1_ + _mus2_, _ns1_ + _ns2_, _largestUnit_).
+ sec-temporal.duration.prototype.subtract step 6:
+ 6. Let _result_ be ? AddDuration(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], −_other_.[[Years]], −_other_.[[Months]], −_other_.[[Weeks]], −_other_.[[Days]], −_other_.[[Hours]], −_other_.[[Minutes]], −_other_.[[Seconds]], −_other_.[[Milliseconds]], −_other_.[[Microseconds]], −_other_.[[Nanoseconds]], _relativeTo_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const duration1 = new Temporal.Duration(0, 0, 0, 0, -60);
+const duration2 = new Temporal.Duration(0, 0, 0, -1);
+
+const resultNotRelative = duration1.subtract(duration2);
+TemporalHelpers.assertDuration(resultNotRelative, 0, 0, 0, -1, -12, 0, 0, 0, 0, 0);
+
+const relativeTo = new Temporal.PlainDateTime(2000, 1, 1);
+const resultRelative = duration1.subtract(duration2, { relativeTo });
+TemporalHelpers.assertDuration(resultRelative, 0, 0, 0, -1, -12, 0, 0, 0, 0, 0);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/balance-negative-time-units.js
new file mode 100644
index 0000000000..93537f5844
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/balance-negative-time-units.js
@@ -0,0 +1,63 @@
+// |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.duration.prototype.subtract
+description: Negative time fields in relativeTo 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-differenceisodatetime step 2:
+ 2. Let _timeDifference_ be ? DifferenceTime(_h1_, _min1_, _s1_, _ms1_, _mus1_, _ns1_, _h2_, _min2_, _s2_, _ms2_, _mus2_, _ns2_).
+ sec-temporal-differencezoneddatetime step 7:
+ 7. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _calendar_, _largestUnit_, _options_).
+ sec-temporal-addduration step 7.g.i:
+ i. Let _result_ be ? DifferenceZonedDateTime(_relativeTo_.[[Nanoseconds]], _endNs_, _timeZone_, _calendar_, _largestUnit_).
+ sec-temporal.duration.prototype.subtract step 6:
+ 6. Let _result_ be ? AddDuration(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], −_other_.[[Years]], −_other_.[[Months]], −_other_.[[Weeks]], −_other_.[[Days]], −_other_.[[Hours]], −_other_.[[Minutes]], −_other_.[[Seconds]], −_other_.[[Milliseconds]], −_other_.[[Microseconds]], −_other_.[[Nanoseconds]], _relativeTo_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(0, 0, 0, 0, 1, 1, 1, 1, 1, 1);
+
+const timeZone = new Temporal.TimeZone("UTC");
+const relativeTo = new Temporal.ZonedDateTime(830998861_000_000_000n, timeZone);
+// This code path is encountered if largestUnit is years, months, weeks, or days
+// and relativeTo is a ZonedDateTime
+const options = { largestUnit: "days", relativeTo };
+
+const result1 = duration.subtract(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, 2), options);
+TemporalHelpers.assertDuration(result1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 999, "nanoseconds balance");
+
+const result2 = duration.subtract(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 2), options);
+TemporalHelpers.assertDuration(result2, 0, 0, 0, 0, 1, 1, 1, 0, 999, 1, "microseconds balance");
+
+const result3 = duration.subtract(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 2), options);
+TemporalHelpers.assertDuration(result3, 0, 0, 0, 0, 1, 1, 0, 999, 1, 1, "milliseconds balance");
+
+const result4 = duration.subtract(new Temporal.Duration(0, 0, 0, 0, 0, 0, 2), options);
+TemporalHelpers.assertDuration(result4, 0, 0, 0, 0, 1, 0, 59, 1, 1, 1, "seconds balance");
+
+const result5 = duration.subtract(new Temporal.Duration(0, 0, 0, 0, 0, 2), options);
+TemporalHelpers.assertDuration(result5, 0, 0, 0, 0, 0, 59, 1, 1, 1, 1, "minutes balance");
+
+// This one is different because hours are later balanced again in BalanceDuration
+const result6 = duration.subtract(new Temporal.Duration(0, 0, 0, 0, 2), options);
+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/Duration/prototype/subtract/basic.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/basic.js
new file mode 100644
index 0000000000..132a9eba69
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/basic.js
@@ -0,0 +1,69 @@
+// |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.duration.prototype.subtract
+description: Basic behavior
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const duration = Temporal.Duration.from({ days: 3, hours: 1, minutes: 10 });
+TemporalHelpers.assertDuration(duration.subtract({ days: 1, minutes: 5 }),
+ 0, 0, 0, 2, 1, 5, 0, 0, 0, 0);
+TemporalHelpers.assertDuration(duration.subtract(duration),
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+TemporalHelpers.assertDuration(duration.subtract({ days: 3 }),
+ 0, 0, 0, 0, 1, 10, 0, 0, 0, 0);
+TemporalHelpers.assertDuration(duration.subtract({ minutes: 10 }),
+ 0, 0, 0, 3, 1, 0, 0, 0, 0, 0);
+TemporalHelpers.assertDuration(duration.subtract({ minutes: 15 }),
+ 0, 0, 0, 3, 0, 55, 0, 0, 0, 0);
+TemporalHelpers.assertDuration(duration.subtract({ seconds: 30 }),
+ 0, 0, 0, 3, 1, 9, 30, 0, 0, 0);
+TemporalHelpers.assertDuration(Temporal.Duration.from('P2DT1H5M').subtract({ days: -1, minutes: -5 }),
+ 0, 0, 0, 3, 1, 10, 0, 0, 0, 0);
+TemporalHelpers.assertDuration(new Temporal.Duration().subtract({ days: -3, hours: -1, minutes: -10 }),
+ 0, 0, 0, 3, 1, 10, 0, 0, 0, 0);
+TemporalHelpers.assertDuration(Temporal.Duration.from('PT1H10M').subtract({ days: -3 }),
+ 0, 0, 0, 3, 1, 10, 0, 0, 0, 0);
+TemporalHelpers.assertDuration(Temporal.Duration.from('P3DT1H').subtract({ minutes: -10 }),
+ 0, 0, 0, 3, 1, 10, 0, 0, 0, 0);
+TemporalHelpers.assertDuration(Temporal.Duration.from('P3DT55M').subtract({ minutes: -15 }),
+ 0, 0, 0, 3, 1, 10, 0, 0, 0, 0);
+TemporalHelpers.assertDuration(Temporal.Duration.from('P3DT1H9M30S').subtract({ seconds: -30 }),
+ 0, 0, 0, 3, 1, 10, 0, 0, 0, 0);
+const d = Temporal.Duration.from({
+ minutes: 100,
+ seconds: 100,
+ milliseconds: 2000,
+ microseconds: 2000,
+ nanoseconds: 2000
+});
+const less = Temporal.Duration.from({
+ minutes: 10,
+ seconds: 10,
+ milliseconds: 500,
+ microseconds: 500,
+ nanoseconds: 500
+});
+TemporalHelpers.assertDuration(d.subtract(less),
+ 0, 0, 0, 0, 0, 91, 31, 501, 501, 500);
+const tenDays = Temporal.Duration.from('P10D');
+const tenMinutes = Temporal.Duration.from('PT10M');
+TemporalHelpers.assertDuration(tenDays.subtract({ days: 15 }),
+ 0, 0, 0, -5, 0, 0, 0, 0, 0, 0);
+TemporalHelpers.assertDuration(tenMinutes.subtract({ minutes: 15 }),
+ 0, 0, 0, 0, 0, -5, 0, 0, 0, 0);
+const d1 = Temporal.Duration.from({ hours: 1, seconds: 60 });
+TemporalHelpers.assertDuration(d1.subtract({ minutes: 122 }),
+ 0, 0, 0, 0, -1, -1, 0, 0, 0, 0);
+const d2 = Temporal.Duration.from({ hours: 1, seconds: 3721 });
+TemporalHelpers.assertDuration(d2.subtract({ minutes: 61, nanoseconds: 3722000000001 }),
+ 0, 0, 0, 0, 0, -1, -1, 0, 0, -1);
+TemporalHelpers.assertDuration(duration.subtract({ month: 1, days: 1 }),
+ 0, 0, 0, 2, 1, 10, 0, 0, 0, 0,
+ "incorrectly-spelled properties are ignored");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/branding.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/branding.js
new file mode 100644
index 0000000000..0e722f155c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/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.duration.prototype.subtract
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const subtract = Temporal.Duration.prototype.subtract;
+
+assert.sameValue(typeof subtract, "function");
+
+const args = [new Temporal.Duration(0, 0, 0, 0, 5)];
+
+assert.throws(TypeError, () => subtract.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => subtract.apply(null, args), "null");
+assert.throws(TypeError, () => subtract.apply(true, args), "true");
+assert.throws(TypeError, () => subtract.apply("", args), "empty string");
+assert.throws(TypeError, () => subtract.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => subtract.apply(1, args), "1");
+assert.throws(TypeError, () => subtract.apply({}, args), "plain object");
+assert.throws(TypeError, () => subtract.apply(Temporal.Duration, args), "Temporal.Duration");
+assert.throws(TypeError, () => subtract.apply(Temporal.Duration.prototype, args), "Temporal.Duration.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/browser.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/builtin.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/builtin.js
new file mode 100644
index 0000000000..183001827d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/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.duration.prototype.subtract
+description: >
+ Tests that Temporal.Duration.prototype.subtract
+ 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.Duration.prototype.subtract),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Duration.prototype.subtract),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Duration.prototype.subtract),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Duration.prototype.subtract.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/calendar-dateadd-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/calendar-dateadd-called-with-options-undefined.js
new file mode 100644
index 0000000000..5f5155e292
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/calendar-dateadd-called-with-options-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.duration.prototype.subtract
+description: >
+ BuiltinTimeZoneGetInstantFor calls Calendar.dateAdd with undefined as the
+ options value
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarDateAddUndefinedOptions();
+const timeZone = TemporalHelpers.oneShiftTimeZone(new Temporal.Instant(0n), 3600e9);
+const instance = new Temporal.Duration(1, 1, 1, 1);
+instance.subtract(new Temporal.Duration(-1, -1, -1, -1), { relativeTo: new Temporal.ZonedDateTime(0n, timeZone, calendar) });
+assert.sameValue(calendar.dateAddCallCount, 3);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/calendar-dateadd-called-with-plaindate-instance.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/calendar-dateadd-called-with-plaindate-instance.js
new file mode 100644
index 0000000000..64c324ffb9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/calendar-dateadd-called-with-plaindate-instance.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.duration.prototype.subtract
+description: >
+ relativeTo parameters that are not ZonedDateTime or undefined, are always
+ converted to PlainDate for observable calendar calls
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarDateAddPlainDateInstance();
+const instance = new Temporal.Duration(1, 1, 1, 1);
+const relativeTo = new Temporal.PlainDate(2000, 1, 1, calendar);
+calendar.specificPlainDate = relativeTo;
+instance.subtract(new Temporal.Duration(-1, -1, -1, -1), { relativeTo });
+assert(calendar.dateAddCallCount > 0, "assertions in calendar.dateAdd() should have been tested");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/calendar-dateadd.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/calendar-dateadd.js
new file mode 100644
index 0000000000..536a501138
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/calendar-dateadd.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.duration.prototype.subtract
+description: Duration.prototype.subtract should call dateAdd with the appropriate values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+const expected = [
+ {
+ plainDate: [1920, 5, "M05", 3],
+ duration: [2, 0, 0, 4, 0, 0, 0, 0, 0, 0],
+ },
+ {
+ plainDate: [1922, 5, "M05", 7],
+ duration: [0, -10, 0, 0, 0, 0, 0, 0, 0, 0],
+ },
+];
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ dateAdd(plainDate, duration, options) {
+ TemporalHelpers.assertPlainDate(plainDate, ...expected[calls].plainDate,
+ `plainDate argument ${calls}`);
+ TemporalHelpers.assertDuration(duration, ...expected[calls].duration,
+ `duration argument ${calls}`);
+ assert.sameValue(options, undefined, "options argument");
+ ++calls;
+ return super.dateAdd(plainDate, duration, options);
+ }
+}
+const relativeTo = new Temporal.PlainDate(1920, 5, 3, new CustomCalendar());
+const duration = new Temporal.Duration(2, 0, 0, 4, 2);
+const result = duration.subtract({ months: 10, hours: 14 }, { relativeTo });
+TemporalHelpers.assertDuration(result, 1, 2, 0, 3, 12, 0, 0, 0, 0, 0, "result");
+assert.sameValue(calls, 2, "should have called dateAdd");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/calendar-dateuntil-called-with-singular-largestunit.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/calendar-dateuntil-called-with-singular-largestunit.js
new file mode 100644
index 0000000000..fddeed95ea
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/calendar-dateuntil-called-with-singular-largestunit.js
@@ -0,0 +1,81 @@
+// |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.duration.prototype.subtract
+description: The options object passed to calendar.dateUntil has a largestUnit property with its value in the singular form
+info: |
+ sec-temporal.duration.prototype.subtract step 6:
+ 6. Let _result_ be ? AddDuration(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], −_other_.[[Years]], −_other_.[[Months]], −_other_.[[Weeks]], −_other_.[[Days]], −_other_.[[Hours]], −_other_.[[Minutes]], −_other_.[[Seconds]], −_other_.[[Milliseconds]], −_other_.[[Microseconds]], −_other_.[[Nanoseconds]], _relativeTo_).
+ sec-temporal-addduration steps 6-7:
+ 6. If _relativeTo_ has an [[InitializedTemporalPlainDateTime]] internal slot, then
+ ...
+ j. Let _dateLargestUnit_ be ! LargerOfTwoTemporalUnits(*"day"*, _largestUnit_).
+ k. Let _differenceOptions_ be ! OrdinaryObjectCreate(*null*).
+ l. Perform ! CreateDataPropertyOrThrow(_differenceOptions_, *"largestUnit"*, _dateLargestUnit_).
+ m. Let _dateDifference_ be ? CalendarDateUntil(_calendar_, _datePart_, _end_, _differenceOptions_).
+ ...
+ 7. Else,
+ a. Assert: _relativeTo_ has an [[IntializedTemporalZonedDateTime]] internal slot.
+ ...
+ f. If _largestUnit_ is not one of *"year"*, *"month"*, *"week"*, or *"day"*, then
+ ...
+ g. Else,
+ i. Let _result_ be ? DifferenceZonedDateTime(_relativeTo_.[[Nanoseconds]], _endNs_, _timeZone_, _calendar_, _largestUnit_).
+ sec-temporal-differencezoneddatetime steps 7 and 11:
+ 7. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _calendar_, _largestUnit_, _options_).
+ 11. Let _result_ be ? NanosecondsToDays(_timeRemainderNs_, _intermediate_).
+ sec-temporal-nanosecondstodays step 11:
+ 11. 1. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _relativeTo_.[[Calendar]], *"day"*).
+ sec-temporal-differenceisodatetime steps 9–11:
+ 9. Let _dateLargestUnit_ be ! LargerOfTwoTemporalUnits(*"day"*, _largestUnit_).
+ 10. Let _untilOptions_ be ? MergeLargestUnitOption(_options_, _dateLargestUnit_).
+ 11. Let _dateDifference_ be ? CalendarDateUntil(_calendar_, _date1_, _date2_, _untilOptions_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkCalendarDateUntilLargestUnitSingular(
+ (calendar, largestUnit, index) => {
+ const one = new Temporal.Duration(...[...Array(index).fill(0), ...Array(10 - index).fill(1)]);
+ const two = new Temporal.Duration(...[...Array(index).fill(0), ...Array(10 - index).fill(2)]);
+ const relativeTo = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+ two.subtract(one, { relativeTo });
+ },
+ {
+ years: ["year"],
+ months: ["month"],
+ weeks: ["week"],
+ days: [],
+ hours: [],
+ minutes: [],
+ seconds: [],
+ milliseconds: [],
+ microseconds: [],
+ nanoseconds: []
+ }
+);
+
+TemporalHelpers.checkCalendarDateUntilLargestUnitSingular(
+ (calendar, largestUnit, index) => {
+ const one = new Temporal.Duration(...[...Array(index).fill(0), ...Array(10 - index).fill(1)]);
+ const two = new Temporal.Duration(...[...Array(index).fill(0), ...Array(10 - index).fill(2)]);
+ const relativeTo = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC", calendar);
+ two.subtract(one, { relativeTo });
+ },
+ {
+ years: ["year"],
+ months: ["month"],
+ weeks: ["week"],
+ days: [],
+ hours: [],
+ minutes: [],
+ seconds: [],
+ milliseconds: [],
+ microseconds: [],
+ nanoseconds: []
+ }
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/calendar-fields-iterable.js
new file mode 100644
index 0000000000..93361f9bdb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/calendar-fields-iterable.js
@@ -0,0 +1,35 @@
+// |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.duration.prototype.subtract
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.duration.prototype.subtract step 5:
+ 5. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_).
+ sec-temporal-torelativetemporalobject step 4.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+const duration1 = new Temporal.Duration(1);
+const duration2 = new Temporal.Duration(0, 12);
+duration1.subtract(duration2, { relativeTo: { year: 2000, month: 1, day: 1, calendar } });
+
+assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/calendar-temporal-object.js
new file mode 100644
index 0000000000..cdea5e6cd2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/calendar-temporal-object.js
@@ -0,0 +1,30 @@
+// |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.duration.prototype.subtract
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.duration.prototype.subtract step 5:
+ 5. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_).
+ sec-temporal-torelativetemporalobject step 4.b:
+ b. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const duration1 = new Temporal.Duration(1);
+ const duration2 = new Temporal.Duration(0, 12);
+ duration1.subtract(duration2, { relativeTo: { year: 2000, month: 1, day: 1, calendar: temporalObject } });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..464996155a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/constructor-in-calendar-fields.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.duration.prototype.subtract
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const relativeTo = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar, timeZone: 'Europe/Paris' };
+const instance = new Temporal.Duration(1, 0, 0, 1);
+
+assert.throws(RangeError, () => instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/duplicate-calendar-fields.js
new file mode 100644
index 0000000000..a5bad8a4d3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/duplicate-calendar-fields.js
@@ -0,0 +1,19 @@
+// |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.duration.prototype.subtract
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['day'], ['month'], ['monthCode'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const relativeTo = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar, timeZone: 'Europe/Paris' };
+ const instance = new Temporal.Duration(1, 0, 0, 1);
+
+ assert.throws(RangeError, () => instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo }));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..b258dfcde0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/infinity-throws-rangeerror.js
@@ -0,0 +1,34 @@
+// |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.
+
+/*---
+description: Temporal.Duration.prototype.subtract throws a RangeError if any value in a property bag is Infinity
+esid: sec-temporal.duration.prototype.subtract
+features: [Temporal]
+---*/
+
+const fields = ['years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds', 'milliseconds', 'microseconds', 'nanoseconds'];
+
+const instance = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+const relativeTo = new Temporal.PlainDateTime(2000, 1, 1);
+
+fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.subtract({ [field]: Infinity }, { relativeTo }));
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return Infinity;
+ }
+};
+
+fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => instance.subtract({ [field]: obj }, { relativeTo }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/intermediate-instant-too-large-with-zoneddatetime.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/intermediate-instant-too-large-with-zoneddatetime.js
new file mode 100644
index 0000000000..260e6ad0d2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/intermediate-instant-too-large-with-zoneddatetime.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.subtract
+description: >
+ AddZonedDateTime throws a RangeError when the intermediate instant is too large.
+features: [Temporal]
+---*/
+
+const plainDate = new Temporal.PlainDate(1970, 1, 1);
+const zonedDateTime = new Temporal.ZonedDateTime(0n, "UTC", "iso8601");
+
+var duration1 = Temporal.Duration.from({days: 1, seconds: Number.MAX_SAFE_INTEGER - 86400});
+var duration2 = Temporal.Duration.from({days: -1, seconds: -Number.MAX_SAFE_INTEGER + 86400});
+
+var options = {relativeTo: zonedDateTime};
+
+assert.throws(RangeError, () => duration1.subtract(duration2, options));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/length.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/length.js
new file mode 100644
index 0000000000..9ab4234fe1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/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.duration.prototype.subtract
+description: Temporal.Duration.prototype.subtract.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.Duration.prototype.subtract, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/name.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/name.js
new file mode 100644
index 0000000000..3bdde302d8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/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.duration.prototype.subtract
+description: Temporal.Duration.prototype.subtract.name is "subtract".
+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.Duration.prototype.subtract, "name", {
+ value: "subtract",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/nanoseconds-is-number-max-safe-integer.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/nanoseconds-is-number-max-safe-integer.js
new file mode 100644
index 0000000000..0c7bfb1cc9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/nanoseconds-is-number-max-safe-integer.js
@@ -0,0 +1,60 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.subtract
+description: >
+ AddDuration computes on exact mathematical number values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainDate = new Temporal.PlainDate(1970, 1, 1);
+const zonedDateTime = new Temporal.ZonedDateTime(0n, "UTC", "iso8601");
+
+// Largest temporal unit is "day".
+const duration1 = Temporal.Duration.from({nanoseconds: Number.MAX_SAFE_INTEGER});
+const duration2 = Temporal.Duration.from({nanoseconds: -2, days: -1});
+const nanos = BigInt(Number.MAX_SAFE_INTEGER) + 2n;
+
+TemporalHelpers.assertDuration(
+ duration1.subtract(duration2),
+ 0, 0, 0,
+ 1 + Number((nanos / (24n * 60n * 60n * 1_000_000_000n))),
+ Number((nanos / (60n * 60n * 1_000_000_000n)) % 24n),
+ Number((nanos / (60n * 1_000_000_000n)) % 60n),
+ Number((nanos / 1_000_000_000n) % 60n),
+ Number((nanos / 1_000_000n) % 1000n),
+ Number((nanos / 1000n) % 1000n),
+ Number(nanos % 1000n),
+ "duration1.subtract(duration2)"
+);
+
+TemporalHelpers.assertDuration(
+ duration1.subtract(duration2, {relativeTo: plainDate}),
+ 0, 0, 0,
+ 1 + Number((nanos / (24n * 60n * 60n * 1_000_000_000n))),
+ Number((nanos / (60n * 60n * 1_000_000_000n)) % 24n),
+ Number((nanos / (60n * 1_000_000_000n)) % 60n),
+ Number((nanos / 1_000_000_000n) % 60n),
+ Number((nanos / 1_000_000n) % 1000n),
+ Number((nanos / 1000n) % 1000n),
+ Number(nanos % 1000n),
+ "duration1.subtract(duration2, {relativeTo: plainDate})"
+);
+
+TemporalHelpers.assertDuration(
+ duration1.subtract(duration2, {relativeTo: zonedDateTime}),
+ 0, 0, 0,
+ 1 + Number((nanos / (24n * 60n * 60n * 1_000_000_000n))),
+ Number((nanos / (60n * 60n * 1_000_000_000n)) % 24n),
+ Number((nanos / (60n * 1_000_000_000n)) % 60n),
+ Number((nanos / 1_000_000_000n) % 60n),
+ Number((nanos / 1_000_000n) % 1000n),
+ Number((nanos / 1000n) % 1000n),
+ Number(nanos % 1000n),
+ "duration1.subtract(duration2, {relativeTo: zonedDateTime})"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/negative-infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/negative-infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..6ba4df2899
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/negative-infinity-throws-rangeerror.js
@@ -0,0 +1,34 @@
+// |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.
+
+/*---
+description: Temporal.Duration.prototype.subtract throws a RangeError if any value in a property bag is -Infinity
+esid: sec-temporal.duration.prototype.subtract
+features: [Temporal]
+---*/
+
+const fields = ['years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds', 'milliseconds', 'microseconds', 'nanoseconds'];
+
+const instance = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+const relativeTo = new Temporal.PlainDateTime(2000, 1, 1);
+
+fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.subtract({ [field]: -Infinity }, { relativeTo }));
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return -Infinity;
+ }
+};
+
+fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => instance.subtract({ [field]: obj }, { relativeTo }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/non-integer-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/non-integer-throws-rangeerror.js
new file mode 100644
index 0000000000..63d7aa5af3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/non-integer-throws-rangeerror.js
@@ -0,0 +1,29 @@
+// |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.duration.prototype.subtract
+description: A non-integer value for any recognized property in the property bag, throws a RangeError
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(0, 0, 0, 1, 2, 3, 4, 987, 654, 321);
+const fields = [
+ "years",
+ "months",
+ "weeks",
+ "days",
+ "hours",
+ "minutes",
+ "seconds",
+ "milliseconds",
+ "microseconds",
+ "nanoseconds",
+];
+fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.subtract({ [field]: 1.5 }));
+ assert.throws(RangeError, () => instance.subtract({ [field]: -1.5 }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/normalized-time-duration-to-days-loop-arbitrarily.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/normalized-time-duration-to-days-loop-arbitrarily.js
new file mode 100644
index 0000000000..8873380037
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/normalized-time-duration-to-days-loop-arbitrarily.js
@@ -0,0 +1,78 @@
+// |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.duration.prototype.subtract
+description: >
+ NormalizedTimeDurationToDays can loop arbitrarily up to max safe integer
+info: |
+ NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ , precalculatedPlainDatetime ] )
+ ...
+ 21. Repeat, while done is false,
+ a. Let oneDayFarther be ? AddDaysToZonedDateTime(relativeResult.[[Instant]],
+ relativeResult.[[DateTime]], timeZoneRec, zonedRelativeTo.[[Calendar]], sign).
+ b. Set dayLengthNs to NormalizedTimeDurationFromEpochNanosecondsDifference(oneDayFarther.[[EpochNanoseconds]],
+ relativeResult.[[EpochNanoseconds]]).
+ c. Let oneDayLess be ? SubtractNormalizedTimeDuration(norm, dayLengthNs).
+ c. If NormalizedTimeDurationSign(oneDayLess) × sign ≥ 0, then
+ i. Set norm to oneDayLess.
+ ii. Set relativeResult to oneDayFarther.
+ iii. Set days to days + sign.
+ d. Else,
+ i. Set done to true.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calls = [];
+const duration = Temporal.Duration.from({ days: 1 });
+
+function createRelativeTo(count) {
+ const dayLengthNs = 86400000000000n;
+ const dayInstant = new Temporal.Instant(dayLengthNs);
+ const substitutions = [];
+ const timeZone = new Temporal.TimeZone("UTC");
+ // Return constant value for first _count_ calls
+ TemporalHelpers.substituteMethod(
+ timeZone,
+ "getPossibleInstantsFor",
+ substitutions
+ );
+ substitutions.length = count;
+ let i = 0;
+ for (i = 0; i < substitutions.length; i++) {
+ // (this value)
+ substitutions[i] = [dayInstant];
+ }
+ // Record calls in calls[]
+ TemporalHelpers.observeMethod(calls, timeZone, "getPossibleInstantsFor");
+ return new Temporal.ZonedDateTime(0n, timeZone);
+}
+
+let zdt = createRelativeTo(50);
+calls.splice(0); // Reset calls list after ZonedDateTime construction
+duration.subtract(duration, {
+ relativeTo: zdt,
+});
+assert.sameValue(
+ calls.length,
+ 50 + 1,
+ "Expected duration.subtract to call getPossibleInstantsFor correct number of times"
+);
+
+zdt = createRelativeTo(100);
+calls.splice(0); // Reset calls list after previous loop + ZonedDateTime construction
+duration.subtract(duration, {
+ relativeTo: zdt,
+});
+assert.sameValue(
+ calls.length,
+ 100 + 1,
+ "Expected duration.subtract to call getPossibleInstantsFor correct number of times"
+);
+
+zdt = createRelativeTo(107);
+assert.throws(RangeError, () => duration.subtract(duration, { relativeTo: zdt }), "107-2 days > 2⁵³ ns");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/not-a-constructor.js
new file mode 100644
index 0000000000..b8529386ac
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/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.duration.prototype.subtract
+description: >
+ Temporal.Duration.prototype.subtract 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.Duration.prototype.subtract();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Duration.prototype.subtract), false,
+ "isConstructor(Temporal.Duration.prototype.subtract)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/options-object.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/options-object.js
new file mode 100644
index 0000000000..45a6a900ae
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/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.duration.prototype.subtract
+description: Empty or a function object may be used as options
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(0, 0, 0, 0, 1);
+
+const result1 = instance.subtract({ hours: 1 }, {});
+TemporalHelpers.assertDuration(
+ result1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "options may be an empty plain object"
+);
+
+const result2 = instance.subtract({ hours: 1 }, () => {});
+TemporalHelpers.assertDuration(
+ result2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "options may be a function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/options-undefined.js
new file mode 100644
index 0000000000..5ddc386ee7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/options-undefined.js
@@ -0,0 +1,35 @@
+// |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.duration.prototype.subtract
+description: Verify that undefined options are handled correctly.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const duration1 = new Temporal.Duration(1);
+const duration2 = new Temporal.Duration(0, 24);
+const duration3 = new Temporal.Duration(0, 0, 0, 1);
+const duration4 = new Temporal.Duration(0, 0, 0, 0, 48);
+
+assert.throws(RangeError, () => duration1.subtract(duration2), "no options with years");
+TemporalHelpers.assertDuration(duration3.subtract(duration4),
+ 0, 0, 0, /* days = */ -1, 0, 0, 0, 0, 0, 0,
+ "no options with days");
+
+const optionValues = [
+ [undefined, "undefined"],
+ [{}, "plain object"],
+ [() => {}, "lambda"],
+];
+for (const [options, description] of optionValues) {
+ assert.throws(RangeError, () => duration1.subtract(duration2, options),
+ `options ${description} with years`);
+ TemporalHelpers.assertDuration(duration3.subtract(duration4, options),
+ 0, 0, 0, /* days = */ -1, 0, 0, 0, 0, 0, 0,
+ `options ${description} with days`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/options-wrong-type.js
new file mode 100644
index 0000000000..2f27bda919
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/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.duration.prototype.subtract
+description: TypeError thrown when options argument is a primitive
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const badOptions = [
+ null,
+ true,
+ "some string",
+ Symbol(),
+ 1,
+ 2n,
+];
+
+const instance = new Temporal.Duration(0, 0, 0, 0, 1);
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.subtract({ hours: 1 }, value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/order-of-operations.js
new file mode 100644
index 0000000000..988835838f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/order-of-operations.js
@@ -0,0 +1,493 @@
+// |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.duration.prototype.subtract
+description: Properties on an object passed to subtract() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ // ToTemporalDurationRecord
+ "get fields.days",
+ "get fields.days.valueOf",
+ "call fields.days.valueOf",
+ "get fields.hours",
+ "get fields.hours.valueOf",
+ "call fields.hours.valueOf",
+ "get fields.microseconds",
+ "get fields.microseconds.valueOf",
+ "call fields.microseconds.valueOf",
+ "get fields.milliseconds",
+ "get fields.milliseconds.valueOf",
+ "call fields.milliseconds.valueOf",
+ "get fields.minutes",
+ "get fields.minutes.valueOf",
+ "call fields.minutes.valueOf",
+ "get fields.months",
+ "get fields.months.valueOf",
+ "call fields.months.valueOf",
+ "get fields.nanoseconds",
+ "get fields.nanoseconds.valueOf",
+ "call fields.nanoseconds.valueOf",
+ "get fields.seconds",
+ "get fields.seconds.valueOf",
+ "call fields.seconds.valueOf",
+ "get fields.weeks",
+ "get fields.weeks.valueOf",
+ "call fields.weeks.valueOf",
+ "get fields.years",
+ "get fields.years.valueOf",
+ "call fields.years.valueOf",
+ // ToRelativeTemporalObject
+ "get options.relativeTo",
+];
+const actual = [];
+
+const simpleFields = TemporalHelpers.propertyBagObserver(actual, {
+ years: 0,
+ months: 0,
+ weeks: 0,
+ days: 1,
+ hours: 1,
+ minutes: 1,
+ seconds: 1,
+ milliseconds: 1,
+ microseconds: 1,
+ nanoseconds: 1,
+}, "fields");
+
+function createOptionsObserver(relativeTo = undefined) {
+ return TemporalHelpers.propertyBagObserver(actual, { relativeTo }, "options");
+}
+
+// basic order of observable operations, without any calendar units:
+const simpleInstance = new Temporal.Duration(0, 0, 0, 1, 1, 1, 1, 1, 1, 1);
+simpleInstance.subtract(simpleFields, createOptionsObserver());
+assert.compareArray(actual, expected, "order of operations");
+actual.splice(0); // clear
+
+const expectedOpsForPlainRelativeTo = expected.concat([
+ // ToRelativeTemporalObject
+ "get options.relativeTo.calendar",
+ "has options.relativeTo.calendar.dateAdd",
+ "has options.relativeTo.calendar.dateFromFields",
+ "has options.relativeTo.calendar.dateUntil",
+ "has options.relativeTo.calendar.day",
+ "has options.relativeTo.calendar.dayOfWeek",
+ "has options.relativeTo.calendar.dayOfYear",
+ "has options.relativeTo.calendar.daysInMonth",
+ "has options.relativeTo.calendar.daysInWeek",
+ "has options.relativeTo.calendar.daysInYear",
+ "has options.relativeTo.calendar.fields",
+ "has options.relativeTo.calendar.id",
+ "has options.relativeTo.calendar.inLeapYear",
+ "has options.relativeTo.calendar.mergeFields",
+ "has options.relativeTo.calendar.month",
+ "has options.relativeTo.calendar.monthCode",
+ "has options.relativeTo.calendar.monthDayFromFields",
+ "has options.relativeTo.calendar.monthsInYear",
+ "has options.relativeTo.calendar.weekOfYear",
+ "has options.relativeTo.calendar.year",
+ "has options.relativeTo.calendar.yearMonthFromFields",
+ "has options.relativeTo.calendar.yearOfWeek",
+ "get options.relativeTo.calendar.dateFromFields",
+ "get options.relativeTo.calendar.fields",
+ "call options.relativeTo.calendar.fields",
+ // PrepareTemporalFields
+ "get options.relativeTo.day",
+ "get options.relativeTo.day.valueOf",
+ "call options.relativeTo.day.valueOf",
+ "get options.relativeTo.hour",
+ "get options.relativeTo.microsecond",
+ "get options.relativeTo.millisecond",
+ "get options.relativeTo.minute",
+ "get options.relativeTo.month",
+ "get options.relativeTo.month.valueOf",
+ "call options.relativeTo.month.valueOf",
+ "get options.relativeTo.monthCode",
+ "get options.relativeTo.monthCode.toString",
+ "call options.relativeTo.monthCode.toString",
+ "get options.relativeTo.nanosecond",
+ "get options.relativeTo.offset",
+ "get options.relativeTo.second",
+ "get options.relativeTo.timeZone",
+ "get options.relativeTo.year",
+ "get options.relativeTo.year.valueOf",
+ "call options.relativeTo.year.valueOf",
+ // InterpretTemporalDateTimeFields
+ "call options.relativeTo.calendar.dateFromFields",
+ // lookup in AddDurationToOrSubtractDurationFromDuration
+ "get options.relativeTo.calendar.dateAdd",
+ "get options.relativeTo.calendar.dateUntil",
+ // AddDuration
+ "call options.relativeTo.calendar.dateAdd",
+ "call options.relativeTo.calendar.dateAdd",
+ "call options.relativeTo.calendar.dateUntil",
+]);
+
+const instance = new Temporal.Duration(1, 2, 1, 4, 5, 6, 7, 987, 654, 321);
+
+const fields = TemporalHelpers.propertyBagObserver(actual, {
+ years: 1,
+ months: 1,
+ weeks: 1,
+ days: 1,
+ hours: 1,
+ minutes: 1,
+ seconds: 1,
+ milliseconds: 1,
+ microseconds: 1,
+ nanoseconds: 1,
+}, "fields");
+
+const plainRelativeTo = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2000,
+ month: 1,
+ monthCode: "M01",
+ day: 1,
+ calendar: TemporalHelpers.calendarObserver(actual, "options.relativeTo.calendar"),
+}, "options.relativeTo");
+
+instance.subtract(fields, createOptionsObserver(plainRelativeTo));
+assert.compareArray(actual, expectedOpsForPlainRelativeTo, "order of operations with PlainDate relativeTo");
+actual.splice(0); // clear
+
+const expectedOpsForPlainRelativeToNoCalendarOperations = [
+ // ToTemporalDurationRecord
+ "get fields.days",
+ "get fields.days.valueOf",
+ "call fields.days.valueOf",
+ "get fields.hours",
+ "get fields.hours.valueOf",
+ "call fields.hours.valueOf",
+ "get fields.microseconds",
+ "get fields.microseconds.valueOf",
+ "call fields.microseconds.valueOf",
+ "get fields.milliseconds",
+ "get fields.milliseconds.valueOf",
+ "call fields.milliseconds.valueOf",
+ "get fields.minutes",
+ "get fields.minutes.valueOf",
+ "call fields.minutes.valueOf",
+ "get fields.months",
+ "get fields.nanoseconds",
+ "get fields.nanoseconds.valueOf",
+ "call fields.nanoseconds.valueOf",
+ "get fields.seconds",
+ "get fields.seconds.valueOf",
+ "call fields.seconds.valueOf",
+ "get fields.weeks",
+ "get fields.years",
+ // ToRelativeTemporalObject
+ "get options.relativeTo",
+ "get options.relativeTo.calendar",
+ "has options.relativeTo.calendar.dateAdd",
+ "has options.relativeTo.calendar.dateFromFields",
+ "has options.relativeTo.calendar.dateUntil",
+ "has options.relativeTo.calendar.day",
+ "has options.relativeTo.calendar.dayOfWeek",
+ "has options.relativeTo.calendar.dayOfYear",
+ "has options.relativeTo.calendar.daysInMonth",
+ "has options.relativeTo.calendar.daysInWeek",
+ "has options.relativeTo.calendar.daysInYear",
+ "has options.relativeTo.calendar.fields",
+ "has options.relativeTo.calendar.id",
+ "has options.relativeTo.calendar.inLeapYear",
+ "has options.relativeTo.calendar.mergeFields",
+ "has options.relativeTo.calendar.month",
+ "has options.relativeTo.calendar.monthCode",
+ "has options.relativeTo.calendar.monthDayFromFields",
+ "has options.relativeTo.calendar.monthsInYear",
+ "has options.relativeTo.calendar.weekOfYear",
+ "has options.relativeTo.calendar.year",
+ "has options.relativeTo.calendar.yearMonthFromFields",
+ "has options.relativeTo.calendar.yearOfWeek",
+ "get options.relativeTo.calendar.dateFromFields",
+ "get options.relativeTo.calendar.fields",
+ "call options.relativeTo.calendar.fields",
+ // PrepareTemporalFields
+ "get options.relativeTo.day",
+ "get options.relativeTo.day.valueOf",
+ "call options.relativeTo.day.valueOf",
+ "get options.relativeTo.hour",
+ "get options.relativeTo.microsecond",
+ "get options.relativeTo.millisecond",
+ "get options.relativeTo.minute",
+ "get options.relativeTo.month",
+ "get options.relativeTo.month.valueOf",
+ "call options.relativeTo.month.valueOf",
+ "get options.relativeTo.monthCode",
+ "get options.relativeTo.monthCode.toString",
+ "call options.relativeTo.monthCode.toString",
+ "get options.relativeTo.nanosecond",
+ "get options.relativeTo.offset",
+ "get options.relativeTo.second",
+ "get options.relativeTo.timeZone",
+ "get options.relativeTo.year",
+ "get options.relativeTo.year.valueOf",
+ "call options.relativeTo.year.valueOf",
+ // InterpretTemporalDateTimeFields
+ "call options.relativeTo.calendar.dateFromFields",
+ // lookup in AddDurationToOrSubtractDurationFromDuration
+ "get options.relativeTo.calendar.dateAdd",
+ "get options.relativeTo.calendar.dateUntil",
+];
+
+const noCalendarInstance = new Temporal.Duration(0, 0, 0, 4, 5, 6, 7, 987, 654, 321);
+
+const noCalendarFields = TemporalHelpers.propertyBagObserver(actual, {
+ days: 1,
+ hours: 1,
+ minutes: 1,
+ seconds: 1,
+ milliseconds: 1,
+ microseconds: 1,
+ nanoseconds: 1,
+}, "fields");
+
+noCalendarInstance.subtract(noCalendarFields, createOptionsObserver(plainRelativeTo));
+assert.compareArray(actual, expectedOpsForPlainRelativeToNoCalendarOperations, "order of operations with PlainDate relativeTo and no calendar units");
+actual.splice(0); // clear
+
+const expectedOpsForZonedRelativeTo = expected.concat([
+ // ToRelativeTemporalObject
+ "get options.relativeTo.calendar",
+ "has options.relativeTo.calendar.dateAdd",
+ "has options.relativeTo.calendar.dateFromFields",
+ "has options.relativeTo.calendar.dateUntil",
+ "has options.relativeTo.calendar.day",
+ "has options.relativeTo.calendar.dayOfWeek",
+ "has options.relativeTo.calendar.dayOfYear",
+ "has options.relativeTo.calendar.daysInMonth",
+ "has options.relativeTo.calendar.daysInWeek",
+ "has options.relativeTo.calendar.daysInYear",
+ "has options.relativeTo.calendar.fields",
+ "has options.relativeTo.calendar.id",
+ "has options.relativeTo.calendar.inLeapYear",
+ "has options.relativeTo.calendar.mergeFields",
+ "has options.relativeTo.calendar.month",
+ "has options.relativeTo.calendar.monthCode",
+ "has options.relativeTo.calendar.monthDayFromFields",
+ "has options.relativeTo.calendar.monthsInYear",
+ "has options.relativeTo.calendar.weekOfYear",
+ "has options.relativeTo.calendar.year",
+ "has options.relativeTo.calendar.yearMonthFromFields",
+ "has options.relativeTo.calendar.yearOfWeek",
+ "get options.relativeTo.calendar.dateFromFields",
+ "get options.relativeTo.calendar.fields",
+ "call options.relativeTo.calendar.fields",
+ // PrepareTemporalFields
+ "get options.relativeTo.day",
+ "get options.relativeTo.day.valueOf",
+ "call options.relativeTo.day.valueOf",
+ "get options.relativeTo.hour",
+ "get options.relativeTo.hour.valueOf",
+ "call options.relativeTo.hour.valueOf",
+ "get options.relativeTo.microsecond",
+ "get options.relativeTo.microsecond.valueOf",
+ "call options.relativeTo.microsecond.valueOf",
+ "get options.relativeTo.millisecond",
+ "get options.relativeTo.millisecond.valueOf",
+ "call options.relativeTo.millisecond.valueOf",
+ "get options.relativeTo.minute",
+ "get options.relativeTo.minute.valueOf",
+ "call options.relativeTo.minute.valueOf",
+ "get options.relativeTo.month",
+ "get options.relativeTo.month.valueOf",
+ "call options.relativeTo.month.valueOf",
+ "get options.relativeTo.monthCode",
+ "get options.relativeTo.monthCode.toString",
+ "call options.relativeTo.monthCode.toString",
+ "get options.relativeTo.nanosecond",
+ "get options.relativeTo.nanosecond.valueOf",
+ "call options.relativeTo.nanosecond.valueOf",
+ "get options.relativeTo.offset",
+ "get options.relativeTo.offset.toString",
+ "call options.relativeTo.offset.toString",
+ "get options.relativeTo.second",
+ "get options.relativeTo.second.valueOf",
+ "call options.relativeTo.second.valueOf",
+ "get options.relativeTo.timeZone",
+ "get options.relativeTo.year",
+ "get options.relativeTo.year.valueOf",
+ "call options.relativeTo.year.valueOf",
+ // InterpretTemporalDateTimeFields
+ "call options.relativeTo.calendar.dateFromFields",
+ // ToRelativeTemporalObject again
+ "has options.relativeTo.timeZone.getOffsetNanosecondsFor",
+ "has options.relativeTo.timeZone.getPossibleInstantsFor",
+ "has options.relativeTo.timeZone.id",
+ // InterpretISODateTimeOffset
+ "get options.relativeTo.timeZone.getOffsetNanosecondsFor",
+ "get options.relativeTo.timeZone.getPossibleInstantsFor",
+ "call options.relativeTo.timeZone.getPossibleInstantsFor",
+ "call options.relativeTo.timeZone.getOffsetNanosecondsFor",
+ // lookup in AddDurationToOrSubtractDurationFromDuration
+ "get options.relativeTo.calendar.dateAdd",
+ "get options.relativeTo.calendar.dateUntil",
+ // AddDuration
+ "call options.relativeTo.timeZone.getOffsetNanosecondsFor",
+ // AddDuration → AddZonedDateTime 1
+ "call options.relativeTo.calendar.dateAdd",
+ "call options.relativeTo.timeZone.getPossibleInstantsFor",
+ // AddDuration → AddZonedDateTime 2
+ "call options.relativeTo.timeZone.getOffsetNanosecondsFor",
+ "call options.relativeTo.calendar.dateAdd",
+ "call options.relativeTo.timeZone.getPossibleInstantsFor",
+ // AddDuration → DifferenceZonedDateTime
+ "call options.relativeTo.timeZone.getOffsetNanosecondsFor",
+ // AddDuration → DifferenceZonedDateTime → DifferenceISODateTime
+ "call options.relativeTo.calendar.dateUntil",
+ // AddDuration → DifferenceZonedDateTime → AddZonedDateTime
+ "call options.relativeTo.calendar.dateAdd",
+ "call options.relativeTo.timeZone.getPossibleInstantsFor",
+ // AddDuration → DifferenceZonedDateTime → NanosecondsToDays
+ "call options.relativeTo.timeZone.getOffsetNanosecondsFor",
+ "call options.relativeTo.timeZone.getOffsetNanosecondsFor",
+ // AddDuration → DifferenceZonedDateTime → NanosecondsToDays → AddZonedDateTime 1
+ "call options.relativeTo.timeZone.getPossibleInstantsFor",
+ // AddDuration → DifferenceZonedDateTime → NanosecondsToDays → AddZonedDateTime 2
+ "call options.relativeTo.timeZone.getPossibleInstantsFor",
+]);
+
+const zonedRelativeTo = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2000,
+ month: 1,
+ monthCode: "M01",
+ day: 1,
+ hour: 0,
+ minute: 0,
+ second: 0,
+ millisecond: 0,
+ microsecond: 0,
+ nanosecond: 0,
+ offset: "+00:00",
+ calendar: TemporalHelpers.calendarObserver(actual, "options.relativeTo.calendar"),
+ timeZone: TemporalHelpers.timeZoneObserver(actual, "options.relativeTo.timeZone"),
+}, "options.relativeTo");
+
+instance.subtract(fields, createOptionsObserver(zonedRelativeTo));
+assert.compareArray(actual, expectedOpsForZonedRelativeTo, "order of operations with ZonedDateTime relativeTo");
+actual.splice(0); // clear
+
+const expectedOpsForZonedRelativeToNoDaysOperations = [
+ // ToTemporalDurationRecord
+ "get fields.days",
+ "get fields.hours",
+ "get fields.hours.valueOf",
+ "call fields.hours.valueOf",
+ "get fields.microseconds",
+ "get fields.microseconds.valueOf",
+ "call fields.microseconds.valueOf",
+ "get fields.milliseconds",
+ "get fields.milliseconds.valueOf",
+ "call fields.milliseconds.valueOf",
+ "get fields.minutes",
+ "get fields.minutes.valueOf",
+ "call fields.minutes.valueOf",
+ "get fields.months",
+ "get fields.nanoseconds",
+ "get fields.nanoseconds.valueOf",
+ "call fields.nanoseconds.valueOf",
+ "get fields.seconds",
+ "get fields.seconds.valueOf",
+ "call fields.seconds.valueOf",
+ "get fields.weeks",
+ "get fields.years",
+ // ToRelativeTemporalObject
+ "get options.relativeTo",
+ "get options.relativeTo.calendar",
+ "has options.relativeTo.calendar.dateAdd",
+ "has options.relativeTo.calendar.dateFromFields",
+ "has options.relativeTo.calendar.dateUntil",
+ "has options.relativeTo.calendar.day",
+ "has options.relativeTo.calendar.dayOfWeek",
+ "has options.relativeTo.calendar.dayOfYear",
+ "has options.relativeTo.calendar.daysInMonth",
+ "has options.relativeTo.calendar.daysInWeek",
+ "has options.relativeTo.calendar.daysInYear",
+ "has options.relativeTo.calendar.fields",
+ "has options.relativeTo.calendar.id",
+ "has options.relativeTo.calendar.inLeapYear",
+ "has options.relativeTo.calendar.mergeFields",
+ "has options.relativeTo.calendar.month",
+ "has options.relativeTo.calendar.monthCode",
+ "has options.relativeTo.calendar.monthDayFromFields",
+ "has options.relativeTo.calendar.monthsInYear",
+ "has options.relativeTo.calendar.weekOfYear",
+ "has options.relativeTo.calendar.year",
+ "has options.relativeTo.calendar.yearMonthFromFields",
+ "has options.relativeTo.calendar.yearOfWeek",
+ "get options.relativeTo.calendar.dateFromFields",
+ "get options.relativeTo.calendar.fields",
+ "call options.relativeTo.calendar.fields",
+ // PrepareTemporalFields
+ "get options.relativeTo.day",
+ "get options.relativeTo.day.valueOf",
+ "call options.relativeTo.day.valueOf",
+ "get options.relativeTo.hour",
+ "get options.relativeTo.hour.valueOf",
+ "call options.relativeTo.hour.valueOf",
+ "get options.relativeTo.microsecond",
+ "get options.relativeTo.microsecond.valueOf",
+ "call options.relativeTo.microsecond.valueOf",
+ "get options.relativeTo.millisecond",
+ "get options.relativeTo.millisecond.valueOf",
+ "call options.relativeTo.millisecond.valueOf",
+ "get options.relativeTo.minute",
+ "get options.relativeTo.minute.valueOf",
+ "call options.relativeTo.minute.valueOf",
+ "get options.relativeTo.month",
+ "get options.relativeTo.month.valueOf",
+ "call options.relativeTo.month.valueOf",
+ "get options.relativeTo.monthCode",
+ "get options.relativeTo.monthCode.toString",
+ "call options.relativeTo.monthCode.toString",
+ "get options.relativeTo.nanosecond",
+ "get options.relativeTo.nanosecond.valueOf",
+ "call options.relativeTo.nanosecond.valueOf",
+ "get options.relativeTo.offset",
+ "get options.relativeTo.offset.toString",
+ "call options.relativeTo.offset.toString",
+ "get options.relativeTo.second",
+ "get options.relativeTo.second.valueOf",
+ "call options.relativeTo.second.valueOf",
+ "get options.relativeTo.timeZone",
+ "get options.relativeTo.year",
+ "get options.relativeTo.year.valueOf",
+ "call options.relativeTo.year.valueOf",
+ // InterpretTemporalDateTimeFields
+ "call options.relativeTo.calendar.dateFromFields",
+ // ToRelativeTemporalObject again
+ "has options.relativeTo.timeZone.getOffsetNanosecondsFor",
+ "has options.relativeTo.timeZone.getPossibleInstantsFor",
+ "has options.relativeTo.timeZone.id",
+ // InterpretISODateTimeOffset
+ "get options.relativeTo.timeZone.getOffsetNanosecondsFor",
+ "get options.relativeTo.timeZone.getPossibleInstantsFor",
+ "call options.relativeTo.timeZone.getPossibleInstantsFor",
+ "call options.relativeTo.timeZone.getOffsetNanosecondsFor",
+ // lookup in AddDurationToOrSubtractDurationFromDuration
+ "get options.relativeTo.calendar.dateAdd",
+ "get options.relativeTo.calendar.dateUntil",
+];
+
+const noDaysInstance = new Temporal.Duration(0, 0, 0, 0, 5, 6, 7, 987, 654, 321);
+
+const noDaysFields = TemporalHelpers.propertyBagObserver(actual, {
+ hours: 1,
+ minutes: 1,
+ seconds: 1,
+ milliseconds: 1,
+ microseconds: 1,
+ nanoseconds: 1,
+}, "fields");
+
+noDaysInstance.subtract(noDaysFields, createOptionsObserver(zonedRelativeTo));
+assert.compareArray(actual, expectedOpsForZonedRelativeToNoDaysOperations, "order of operations with ZonedDateTime relativeTo and no units above days");
+actual.splice(0); // clear
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/precision-exact-mathematical-values.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/precision-exact-mathematical-values.js
new file mode 100644
index 0000000000..6d7c8116dd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/precision-exact-mathematical-values.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.subtract
+description: >
+ AddDuration computes on exact mathematical number values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+// Largest temporal unit is "microsecond".
+let duration1 = Temporal.Duration.from({microseconds: Number.MAX_SAFE_INTEGER + 1, nanoseconds: 0});
+let duration2 = Temporal.Duration.from({microseconds: -1, nanoseconds: -1000});
+
+TemporalHelpers.assertDuration(
+ duration1.subtract(duration2),
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 9007199254740994,
+ 0,
+ "duration1.subtract(duration2)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/precision-no-floating-point-loss.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/precision-no-floating-point-loss.js
new file mode 100644
index 0000000000..8c9fa64550
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/precision-no-floating-point-loss.js
@@ -0,0 +1,52 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.subtract
+description: >
+ BalanceDuration computes floating-point values that are the same as exact math
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainDate = new Temporal.PlainDate(1970, 1, 1);
+const zonedDateTime = new Temporal.ZonedDateTime(0n, "UTC", "iso8601");
+
+// Largest temporal unit is "day".
+const duration1 = Temporal.Duration.from({seconds: 4503599627370495, nanoseconds: 499_999_999});
+const duration2 = Temporal.Duration.from({seconds: -4503599627370495 + 86400, nanoseconds: -499_999_999, days: -1});
+const nanos = 4503599627370495_499_999_999n * 2n;
+
+TemporalHelpers.assertDuration(
+ duration1.subtract(duration2),
+ 0, 0, 0,
+ Number((nanos / (24n * 60n * 60n * 1_000_000_000n))),
+ Number((nanos / (60n * 60n * 1_000_000_000n)) % 24n),
+ Number((nanos / (60n * 1_000_000_000n)) % 60n),
+ Number((nanos / 1_000_000_000n) % 60n),
+ Number((nanos / 1_000_000n) % 1000n),
+ Number((nanos / 1000n) % 1000n),
+ Number(nanos % 1000n),
+ "duration1.subtract(duration2)"
+);
+
+TemporalHelpers.assertDuration(
+ duration1.subtract(duration2, {relativeTo: plainDate}),
+ 0, 0, 0,
+ Number((nanos / (24n * 60n * 60n * 1_000_000_000n))),
+ Number((nanos / (60n * 60n * 1_000_000_000n)) % 24n),
+ Number((nanos / (60n * 1_000_000_000n)) % 60n),
+ Number((nanos / 1_000_000_000n) % 60n),
+ Number((nanos / 1_000_000n) % 1000n),
+ Number((nanos / 1000n) % 1000n),
+ Number(nanos % 1000n),
+ "duration1.subtract(duration2, {relativeTo: plainDate})"
+);
+
+// Throws a RangeError because the intermediate instant is too large.
+assert.throws(RangeError, () => {
+ duration1.subtract(duration2, {relativeTo: zonedDateTime});
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/prop-desc.js
new file mode 100644
index 0000000000..e972e0b3fa
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/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.duration.prototype.subtract
+description: The "subtract" property of Temporal.Duration.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Duration.prototype.subtract,
+ "function",
+ "`typeof Duration.prototype.subtract` is `function`"
+);
+
+verifyProperty(Temporal.Duration.prototype, "subtract", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/proto-in-calendar-fields.js
new file mode 100644
index 0000000000..15e34c9622
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/proto-in-calendar-fields.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.duration.prototype.subtract
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const relativeTo = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar, timeZone: 'Europe/Paris' };
+const instance = new Temporal.Duration(1, 0, 0, 1);
+
+assert.throws(RangeError, () => instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/read-time-fields-before-datefromfields.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/read-time-fields-before-datefromfields.js
new file mode 100644
index 0000000000..012f928360
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/read-time-fields-before-datefromfields.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.duration.prototype.subtract
+description: The time fields are read from the object before being passed to dateFromFields().
+info: |
+ sec-temporal.duration.prototype.subtract step 5:
+ 5. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_).
+ sec-temporal-torelativetemporalobject step 4.g:
+ g. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+ sec-temporal-interprettemporaldatetimefields steps 1–2:
+ 1. Let _timeResult_ be ? ToTemporalTimeRecord(_fields_).
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarMakeInvalidGettersTime();
+const duration1 = new Temporal.Duration(1);
+const duration2 = new Temporal.Duration(0, 12);
+duration1.subtract(duration2, { relativeTo: { year: 2000, month: 1, day: 1, calendar } });
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..f30ec2dfa5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-infinity-throws-rangeerror.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.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.duration.prototype.subtract
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+const base = { year: 2000, month: 5, day: 2, hour: 15, minute: 30, second: 45, millisecond: 987, microsecond: 654, nanosecond: 321 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.subtract(instance, { relativeTo: { ...base, [prop]: inf } }), `${prop} property cannot be ${inf} in relativeTo`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.subtract(instance, { relativeTo: { ...base, [prop]: obj } }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-leap-second.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-leap-second.js
new file mode 100644
index 0000000000..ed4fd73cf1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-leap-second.js
@@ -0,0 +1,46 @@
+// |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.duration.prototype.subtract
+description: Leap second is constrained in both an ISO string and a property bag
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(1, 0, 0, 1);
+
+let relativeTo = "2016-12-31T23:59:60";
+const result1 = instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo });
+TemporalHelpers.assertDuration(
+ result1,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "leap second is a valid ISO string for PlainDate relativeTo"
+);
+
+relativeTo = "2016-12-31T23:59:60+00:00[UTC]";
+const result2 = instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo });
+TemporalHelpers.assertDuration(
+ result2,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "leap second is a valid ISO string for ZonedDateTime relativeTo"
+);
+
+relativeTo = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+const result3 = instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo });
+TemporalHelpers.assertDuration(
+ result3,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "second: 60 is valid in a property bag for PlainDate relativeTo"
+);
+
+relativeTo = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60, timeZone: "UTC" };
+const result4 = instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo });
+TemporalHelpers.assertDuration(
+ result4,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "second: 60 is valid in a property bag for ZonedDateTime relativeTo"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-month.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-month.js
new file mode 100644
index 0000000000..856843edc1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-month.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.duration.prototype.subtract
+description: relativeTo with months.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const oneMonth = new Temporal.Duration(0, 1);
+const days30 = new Temporal.Duration(0, 0, 0, 30);
+TemporalHelpers.assertDuration(oneMonth.subtract(days30, { relativeTo: Temporal.PlainDate.from('2018-02-01') }),
+ 0, 0, 0, -2, 0, 0, 0, 0, 0, 0, "February");
+TemporalHelpers.assertDuration(oneMonth.subtract(days30, { relativeTo: Temporal.PlainDate.from('2018-03-01') }),
+ 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, "March");
+TemporalHelpers.assertDuration(oneMonth.subtract(days30, { relativeTo: Temporal.PlainDate.from('2018-04-01') }),
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "April");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-number.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-number.js
new file mode 100644
index 0000000000..3d5a10675d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-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.duration.prototype.subtract
+description: A number cannot be used in place of a relativeTo
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(1, 0, 0, 1);
+
+const numbers = [
+ 1,
+ 20191101,
+ -20191101,
+ 1234567890,
+];
+
+for (const relativeTo of numbers) {
+ assert.throws(
+ TypeError,
+ () => instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo }),
+ `A number (${relativeTo}) is not a valid ISO string for relativeTo`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-order.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-order.js
new file mode 100644
index 0000000000..2cccdbfc9d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-order.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.duration.prototype.subtract
+description: relativeTo with years.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const d1 = new Temporal.Duration(0, 2, 1, 4);
+const d2 = new Temporal.Duration(0, 1, 1, 1);
+const relativeTo = new Temporal.PlainDate(2000, 1, 1);
+TemporalHelpers.assertDuration(d1.subtract(d2, { relativeTo }),
+ 0, 1, 0, 3, 0, 0, 0, 0, 0, 0,
+ "first this is resolved against relativeTo, then the argument against relativeTo + this");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-ambiguous-wall-clock-time.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-ambiguous-wall-clock-time.js
new file mode 100644
index 0000000000..8b5afecd52
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-ambiguous-wall-clock-time.js
@@ -0,0 +1,92 @@
+// |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.duration.prototype.subtract
+description: >
+ Correct time zone calls are made when converting a ZonedDateTime-like
+ relativeTo property bag denoting an ambiguous wall-clock time
+includes: [temporalHelpers.js, compareArray.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+
+const dstTimeZone = TemporalHelpers.springForwardFallBackTimeZone();
+const dstTimeZoneObserver = TemporalHelpers.timeZoneObserver(actual, "timeZone", {
+ getOffsetNanosecondsFor: dstTimeZone.getOffsetNanosecondsFor.bind(dstTimeZone),
+ getPossibleInstantsFor: dstTimeZone.getPossibleInstantsFor.bind(dstTimeZone),
+});
+const calendar = TemporalHelpers.calendarObserver(actual, "calendar");
+
+const instance = new Temporal.Duration(1, 0, 0, 1);
+
+let relativeTo = { year: 2000, month: 4, day: 2, hour: 2, minute: 30, timeZone: dstTimeZoneObserver, calendar };
+instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo });
+
+const expected = [
+ // GetTemporalCalendarSlotValueWithISODefault
+ "has calendar.dateAdd",
+ "has calendar.dateFromFields",
+ "has calendar.dateUntil",
+ "has calendar.day",
+ "has calendar.dayOfWeek",
+ "has calendar.dayOfYear",
+ "has calendar.daysInMonth",
+ "has calendar.daysInWeek",
+ "has calendar.daysInYear",
+ "has calendar.fields",
+ "has calendar.id",
+ "has calendar.inLeapYear",
+ "has calendar.mergeFields",
+ "has calendar.month",
+ "has calendar.monthCode",
+ "has calendar.monthDayFromFields",
+ "has calendar.monthsInYear",
+ "has calendar.weekOfYear",
+ "has calendar.year",
+ "has calendar.yearMonthFromFields",
+ "has calendar.yearOfWeek",
+ // lookup
+ "get calendar.dateFromFields",
+ "get calendar.fields",
+ // CalendarFields
+ "call calendar.fields",
+ // InterpretTemporalDateTimeFields
+ "call calendar.dateFromFields",
+ // ToTemporalTimeZoneSlotValue
+ "has timeZone.getOffsetNanosecondsFor",
+ "has timeZone.getPossibleInstantsFor",
+ "has timeZone.id",
+ // lookup
+ "get timeZone.getOffsetNanosecondsFor",
+ "get timeZone.getPossibleInstantsFor",
+ // InterpretISODateTimeOffset
+ "call timeZone.getPossibleInstantsFor",
+];
+
+const expectedSpringForward = expected.concat([
+ // DisambiguatePossibleInstants
+ "call timeZone.getOffsetNanosecondsFor",
+ "call timeZone.getOffsetNanosecondsFor",
+ "call timeZone.getPossibleInstantsFor",
+]);
+assert.compareArray(
+ actual.slice(0, expectedSpringForward.length), // ignore operations after ToRelativeTemporalObject
+ expectedSpringForward,
+ "order of operations converting property bag at skipped wall-clock time"
+);
+actual.splice(0); // clear
+
+relativeTo = { year: 2000, month: 10, day: 29, hour: 1, minute: 30, timeZone: dstTimeZoneObserver, calendar };
+instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo });
+
+assert.compareArray(
+ actual.slice(0, expected.length), // ignore operations after ToRelativeTemporalObject
+ expected,
+ "order of operations converting property bag at repeated wall-clock time"
+);
+actual.splice(0); // clear
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..5f4c665b64
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-builtin-calendar-no-array-iteration.js
@@ -0,0 +1,25 @@
+// |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.duration.prototype.subtract
+description: >
+ Calling the method with a relativeTo property bag with a builtin calendar
+ causes no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const timeZone = "UTC";
+const instance = new Temporal.Duration(1, 0, 0, 1);
+const relativeTo = { year: 2000, month: 5, day: 2, hour: 21, minute: 43, second: 5, timeZone, calendar: "iso8601" };
+instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo });
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-calendar-datefromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..6edd8b6090
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-calendar-datefromfields-called-with-null-prototype-fields.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.duration.prototype.subtract
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.Duration(1, 0, 0, 1);
+const relativeTo = { year: 2000, month: 5, day: 2, calendar };
+instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo });
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-calendar-number.js
new file mode 100644
index 0000000000..bb89e364d4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-calendar-number.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.duration.prototype.subtract
+description: A number as calendar in relativeTo property bag is invalid
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(1, 0, 0, 1);
+
+const numbers = [
+ 1,
+ 19970327,
+ -19970327,
+ 1234567890,
+];
+
+for (const calendar of numbers) {
+ const relativeTo = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ TypeError,
+ () => instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo }),
+ `A number (${calendar}) is not a valid ISO string for relativeTo.calendar`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-calendar-string.js
new file mode 100644
index 0000000000..05ee3e7dc8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-calendar-string.js
@@ -0,0 +1,29 @@
+// |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.duration.prototype.subtract
+description: >
+ Builtin dateFromFields method is not observably called when the property bag
+ has a string-valued calendar property
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dateFromFieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dateFromFields");
+Object.defineProperty(Temporal.Calendar.prototype, "dateFromFields", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("dateFromFields should not be looked up");
+ },
+});
+
+const instance = new Temporal.Duration(1, 0, 0, 1);
+const relativeTo = { year: 2000, month: 5, day: 2, calendar: "iso8601" };
+instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo });
+
+Object.defineProperty(Temporal.Calendar.prototype, "dateFromFields", dateFromFieldsOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-calendar-wrong-type.js
new file mode 100644
index 0000000000..b63e5d8f43
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-calendar-wrong-type.js
@@ -0,0 +1,48 @@
+// |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.duration.prototype.subtract
+description: >
+ Appropriate error thrown when relativeTo.calendar cannot be converted to a
+ calendar object or string
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.Duration(1, 0, 0, 1);
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [calendar, description] of primitiveTests) {
+ const relativeTo = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ typeof calendar === 'string' ? RangeError : TypeError,
+ () => instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo }),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.PlainDate, "Temporal.PlainDate, object"],
+ [Temporal.PlainDate.prototype, "Temporal.PlainDate.prototype, object"],
+ [Temporal.ZonedDateTime, "Temporal.ZonedDateTime, object"],
+ [Temporal.ZonedDateTime.prototype, "Temporal.ZonedDateTime.prototype, object"],
+];
+
+for (const [calendar, description] of typeErrorTests) {
+ const relativeTo = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(TypeError, () => instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo }), `${description} is not a valid property bag and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-getpossibleinstantsfor-called-with-iso8601-calendar.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-getpossibleinstantsfor-called-with-iso8601-calendar.js
new file mode 100644
index 0000000000..5e0af179fc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-getpossibleinstantsfor-called-with-iso8601-calendar.js
@@ -0,0 +1,59 @@
+// |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.duration.prototype.subtract
+description: >
+ Time zone's getPossibleInstantsFor is called with a PlainDateTime with the
+ built-in ISO 8601 calendar
+features: [Temporal]
+info: |
+ DisambiguatePossibleInstants:
+ 2. Let _n_ be _possibleInstants_'s length.
+ ...
+ 5. Assert: _n_ = 0.
+ ...
+ 19. If _disambiguation_ is *"earlier"*, then
+ ...
+ c. Let _earlierDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*).
+ d. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _earlierDateTime_).
+ ...
+ 20. Assert: _disambiguation_ is *"compatible"* or *"later"*.
+ ...
+ 23. Let _laterDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*).
+ 24. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _laterDateTime_).
+---*/
+
+class SkippedDateTime extends Temporal.TimeZone {
+ constructor() {
+ super("UTC");
+ this.calls = 0;
+ }
+
+ getPossibleInstantsFor(dateTime) {
+ // Calls occur in pairs. For the first one return no possible instants so
+ // that DisambiguatePossibleInstants will call it again
+ if (this.calls++ % 2 == 0) {
+ return [];
+ }
+
+ assert.sameValue(
+ dateTime.getISOFields().calendar,
+ "iso8601",
+ "getPossibleInstantsFor called with dateTime with built-in ISO 8601 calendar"
+ );
+ return super.getPossibleInstantsFor(dateTime);
+ }
+}
+
+const nonBuiltinISOCalendar = new Temporal.Calendar("iso8601");
+const timeZone = new SkippedDateTime();
+const relativeTo = { year: 2000, month: 5, day: 2, timeZone, calendar: nonBuiltinISOCalendar };
+
+const instance = new Temporal.Duration(1, 0, 0, 1);
+instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo });
+
+assert.sameValue(timeZone.calls, 6, "getPossibleInstantsFor should have been called 6 times");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-invalid-offset-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-invalid-offset-string.js
new file mode 100644
index 0000000000..0d93a59001
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-invalid-offset-string.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.duration.prototype.subtract
+description: relativeTo property bag with offset property is rejected if offset is in the wrong format
+features: [Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.Duration(1, 0, 0, 1);
+
+const badOffsets = [
+ "00:00", // missing sign
+ "+0", // too short
+ "-000:00", // too long
+ 0, // must be a string
+ null, // must be a string
+ true, // must be a string
+ 1000n, // must be a string
+];
+badOffsets.forEach((offset) => {
+ const relativeTo = { year: 2021, month: 10, day: 28, offset, timeZone };
+ assert.throws(
+ typeof(offset) === 'string' ? RangeError : TypeError,
+ () => instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24),
+ { relativeTo }), `"${offset} is not a valid offset string`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-no-time-units.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-no-time-units.js
new file mode 100644
index 0000000000..aa8652d534
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-no-time-units.js
@@ -0,0 +1,18 @@
+// |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.duration.prototype.subtract
+description: Missing time units in relativeTo property bag default to 0
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(1, 0, 0, 1);
+
+let relativeTo = { year: 2000, month: 1, day: 1 };
+const result = instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo });
+TemporalHelpers.assertDuration(result, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "missing time units default to 0");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..12ce56cd60
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-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.duration.prototype.subtract
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+ const other = new Temporal.Duration(0, 3);
+ assert.throws(RangeError, () => duration.subtract(other, { relativeTo: { year: 2000, month: 5, day: 2, hour: 12, timeZone } }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..b4b0d429c5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-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.duration.prototype.subtract
+description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable
+features: [BigInt, Symbol, Temporal, arrow-function]
+---*/
+
+[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => {
+ const timeZone = new Temporal.TimeZone("UTC");
+ const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+ const other = new Temporal.Duration(0, 3);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => duration.subtract(other, { relativeTo: { year: 2000, month: 5, day: 2, hour: 12, timeZone } }),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..04531f166e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-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.duration.prototype.subtract
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+ const other = new Temporal.Duration(0, 3);
+ assert.throws(RangeError, () => duration.subtract(other, { relativeTo: { year: 2000, month: 5, day: 2, hour: 12, timeZone } }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..fc14e496dd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-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.duration.prototype.subtract
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+ const other = new Temporal.Duration(0, 3);
+ assert.throws(TypeError, () => duration.subtract(other, { relativeTo: { year: 2000, month: 5, day: 2, hour: 12, timeZone } }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-string-datetime.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-string-datetime.js
new file mode 100644
index 0000000000..30d06a6d40
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-string-datetime.js
@@ -0,0 +1,66 @@
+// |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.duration.prototype.subtract
+description: Conversion of ISO date-time strings to Temporal.TimeZone instances
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(1);
+
+let timeZone = "2021-08-19T17:30";
+assert.throws(RangeError, () => instance.subtract(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } }), "bare date-time string is not a time zone");
+
+[
+ "2021-08-19T17:30-07:00:01",
+ "2021-08-19T17:30-07:00:00",
+ "2021-08-19T17:30-07:00:00.1",
+ "2021-08-19T17:30-07:00:00.0",
+ "2021-08-19T17:30-07:00:00.01",
+ "2021-08-19T17:30-07:00:00.00",
+ "2021-08-19T17:30-07:00:00.001",
+ "2021-08-19T17:30-07:00:00.000",
+ "2021-08-19T17:30-07:00:00.0001",
+ "2021-08-19T17:30-07:00:00.0000",
+ "2021-08-19T17:30-07:00:00.00001",
+ "2021-08-19T17:30-07:00:00.00000",
+ "2021-08-19T17:30-07:00:00.000001",
+ "2021-08-19T17:30-07:00:00.000000",
+ "2021-08-19T17:30-07:00:00.0000001",
+ "2021-08-19T17:30-07:00:00.0000000",
+ "2021-08-19T17:30-07:00:00.00000001",
+ "2021-08-19T17:30-07:00:00.00000000",
+ "2021-08-19T17:30-07:00:00.000000001",
+ "2021-08-19T17:30-07:00:00.000000000",
+].forEach((timeZone) => {
+ assert.throws(
+ RangeError,
+ () => instance.subtract(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } }),
+ `ISO string ${timeZone} with a sub-minute offset is not a valid time zone`
+ );
+});
+
+// The following are all valid strings so should not throw:
+
+[
+ "2021-08-19T17:30Z",
+ "2021-08-19T1730Z",
+ "2021-08-19T17:30-07:00",
+ "2021-08-19T1730-07:00",
+ "2021-08-19T17:30-0700",
+ "2021-08-19T1730-0700",
+ "2021-08-19T17:30[UTC]",
+ "2021-08-19T1730[UTC]",
+ "2021-08-19T17:30Z[UTC]",
+ "2021-08-19T1730Z[UTC]",
+ "2021-08-19T17:30-07:00[UTC]",
+ "2021-08-19T1730-07:00[UTC]",
+ "2021-08-19T17:30-0700[UTC]",
+ "2021-08-19T1730-0700[UTC]",
+].forEach((timeZone) => {
+ instance.subtract(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-string-leap-second.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-string-leap-second.js
new file mode 100644
index 0000000000..f0f7a0fa43
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-string-leap-second.js
@@ -0,0 +1,22 @@
+// |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.duration.prototype.subtract
+description: Leap second is a valid ISO string for TimeZone
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(1);
+let timeZone = "2016-12-31T23:59:60+00:00[UTC]";
+
+// A string with a leap second is a valid ISO string, so the following
+// operation should not throw
+
+instance.subtract(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } });
+
+timeZone = "2021-08-19T17:30:45.123456789+23:59[+23:59:60]";
+assert.throws(RangeError, () => instance.subtract(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } }), "leap second in time zone name not valid");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-string-year-zero.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-string-year-zero.js
new file mode 100644
index 0000000000..24447c0c96
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-string-year-zero.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.duration.prototype.subtract
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+const instance = new Temporal.Duration(1);
+invalidStrings.forEach((timeZone) => {
+ assert.throws(
+ RangeError,
+ () => instance.subtract(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } }),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-string.js
new file mode 100644
index 0000000000..d3812057ef
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-string.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.duration.prototype.subtract
+description: Time zone IDs are valid input for a time zone
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getPossibleInstantsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getPossibleInstantsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be looked up");
+ },
+});
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+
+const instance = new Temporal.Duration(1);
+
+// The following are all valid strings so should not throw:
+
+["UTC", "+01:00"].forEach((timeZone) => {
+ instance.subtract(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } });
+});
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", getPossibleInstantsForOriginal);
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-wrong-type.js
new file mode 100644
index 0000000000..c06331faa0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-wrong-type.js
@@ -0,0 +1,42 @@
+// |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.duration.prototype.subtract
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or object for TimeZone
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.Duration(1);
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [19761118, "number that would convert to a valid ISO string in other contexts"],
+ [1n, "bigint"],
+];
+
+for (const [timeZone, description] of primitiveTests) {
+ assert.throws(
+ typeof timeZone === 'string' ? RangeError : TypeError,
+ () => instance.subtract(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } }),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "object not implementing time zone protocol"],
+ [new Temporal.Calendar("iso8601"), "calendar instance"],
+];
+
+for (const [timeZone, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.subtract(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } }), `${description} is not a valid object and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-required.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-required.js
new file mode 100644
index 0000000000..39d382b20b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-required.js
@@ -0,0 +1,36 @@
+// |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.duration.prototype.subtract
+description: relativeTo is required if the largest unit is at least weeks.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const d = Temporal.Duration.from({ hours: 1 });
+const dy = Temporal.Duration.from({ years: 1, hours: 1 });
+const dm = Temporal.Duration.from({ months: 1, hours: 1 });
+const dw = Temporal.Duration.from({ weeks: 1, hours: 1 });
+assert.throws(RangeError, () => d.subtract(dy));
+assert.throws(RangeError, () => d.subtract(dm));
+assert.throws(RangeError, () => d.subtract(dw));
+assert.throws(RangeError, () => dy.subtract(d));
+assert.throws(RangeError, () => dm.subtract(d));
+assert.throws(RangeError, () => dw.subtract(d));
+const relativeTo = Temporal.PlainDate.from("2000-01-01");
+TemporalHelpers.assertDuration(d.subtract(dy, { relativeTo }),
+ -1, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+TemporalHelpers.assertDuration(d.subtract(dm, { relativeTo }),
+ 0, -1, 0, 0, 0, 0, 0, 0, 0, 0);
+TemporalHelpers.assertDuration(d.subtract(dw, { relativeTo }),
+ 0, 0, -1, 0, 0, 0, 0, 0, 0, 0);
+TemporalHelpers.assertDuration(dy.subtract(d, { relativeTo }),
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+TemporalHelpers.assertDuration(dm.subtract(d, { relativeTo }),
+ 0, 1, 0, 0, 0, 0, 0, 0, 0, 0);
+TemporalHelpers.assertDuration(dw.subtract(d, { relativeTo }),
+ 0, 0, 1, 0, 0, 0, 0, 0, 0, 0);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-string-datetime.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-string-datetime.js
new file mode 100644
index 0000000000..e319feac95
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-string-datetime.js
@@ -0,0 +1,41 @@
+// |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.duration.prototype.subtract
+description: >
+ Conversion of ISO date-time strings as relativeTo option to
+ Temporal.ZonedDateTime or Temporal.PlainDateTime instances
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(1, 0, 0, 1);
+
+let relativeTo = "2019-11-01T00:00";
+const result1 = instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo });
+TemporalHelpers.assertDuration(result1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "bare date-time string is a plain relativeTo");
+
+relativeTo = "2019-11-01T00:00-07:00";
+const result2 = instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo });
+TemporalHelpers.assertDuration(result2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "date-time + offset is a plain relativeTo");
+
+relativeTo = "2019-11-01T00:00[-07:00]";
+const result3 = instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo });
+TemporalHelpers.assertDuration(result3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "date-time + IANA annotation is a zoned relativeTo");
+
+relativeTo = "2019-11-01T00:00Z[-07:00]";
+const result4 = instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo });
+TemporalHelpers.assertDuration(result4, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "date-time + Z + IANA annotation is a zoned relativeTo");
+
+relativeTo = "2019-11-01T00:00+00:00[UTC]";
+const result5 = instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo });
+TemporalHelpers.assertDuration(result5, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "date-time + offset + IANA annotation is a zoned relativeTo");
+
+relativeTo = "2019-11-01T00:00Z";
+assert.throws(RangeError, () => instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo }), "date-time + Z throws without an IANA annotation");
+relativeTo = "2019-11-01T00:00+04:15[UTC]";
+assert.throws(RangeError, () => instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo }), "date-time + offset + IANA annotation throws if wall time and exact time mismatch");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-string-invalid.js
new file mode 100644
index 0000000000..5e36878e19
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-string-invalid.js
@@ -0,0 +1,16 @@
+// |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.duration.prototype.subtract
+description: RangeError thrown if relativeTo is a string with the wrong format
+features: [Temporal]
+---*/
+
+['bad string', '15:30:45.123456', 'iso8601', 'UTC', 'P1YT1H'].forEach((relativeTo) => {
+ const duration = new Temporal.Duration(1, 0, 0, 41);
+ assert.throws(RangeError, () => duration.subtract(new Temporal.Duration(0, 0, 0, 10), { relativeTo }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-string-plaindatetime.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-string-plaindatetime.js
new file mode 100644
index 0000000000..4502100eb0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-string-plaindatetime.js
@@ -0,0 +1,18 @@
+// |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.duration.prototype.subtract
+description: The relativeTo option accepts a PlainDateTime-like ISO 8601 string
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+['2000-01-01', '2000-01-01T00:00', '2000-01-01T00:00[u-ca=iso8601]'].forEach((relativeTo) => {
+ const duration = new Temporal.Duration(1, 0, 0, 41);
+ const result = duration.subtract(new Temporal.Duration(0, 0, 0, 10), { relativeTo });
+ TemporalHelpers.assertDuration(result, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-string-zoneddatetime-wrong-offset.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-string-zoneddatetime-wrong-offset.js
new file mode 100644
index 0000000000..5ff40953fa
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-string-zoneddatetime-wrong-offset.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.duration.prototype.subtract
+description: Throws if a ZonedDateTime-like relativeTo string has the wrong UTC offset
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(1, 0, 0, 1);
+const relativeTo = "2000-01-01T00:00+05:30[UTC]";
+assert.throws(
+ RangeError,
+ () => instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo }),
+ "subtract should throw RangeError on a string with UTC offset mismatch"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-string-zoneddatetime.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-string-zoneddatetime.js
new file mode 100644
index 0000000000..748b49641c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-string-zoneddatetime.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.duration.prototype.subtract
+description: The relativeTo option accepts a ZonedDateTime-like ISO 8601 string
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ '2000-01-01[UTC]',
+ '2000-01-01T00:00[UTC]',
+ '2000-01-01T00:00+00:00[UTC]',
+ '2000-01-01T00:00+00:00[UTC][u-ca=iso8601]',
+].forEach((relativeTo) => {
+ const duration = new Temporal.Duration(1, 0, 0, 41);
+ const result = duration.subtract(new Temporal.Duration(0, 0, 0, 10), { relativeTo });
+ TemporalHelpers.assertDuration(result, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-sub-minute-offset.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-sub-minute-offset.js
new file mode 100644
index 0000000000..0e9f5db5d3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-sub-minute-offset.js
@@ -0,0 +1,29 @@
+// |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.duration.prototype.subtract
+description: relativeTo string accepts trailing zeroes in sub-minute UTC offset
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(1, 0, 0, 1);
+
+let result;
+let relativeTo;
+const action = (relativeTo) => instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo });
+
+relativeTo = "1970-01-01T00:00-00:45:00[-00:45]";
+result = action(relativeTo);
+TemporalHelpers.assertDateDuration(result, 1, 0, 0, 0, "ISO string offset accepted with zero seconds (string)");
+
+relativeTo = { year: 1970, month: 1, day: 1, offset: "+00:45:00.000000000", timeZone: "+00:45" };
+result = action(relativeTo);
+TemporalHelpers.assertDateDuration(result, 1, 0, 0, 0, "ISO string offset accepted with zero seconds (property bag)");
+
+relativeTo = "1970-01-01T00:00+00:44:30.123456789[+00:45]";
+assert.throws(RangeError, () => action(relativeTo), "rounding is not accepted between ISO offset and time zone");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-wrong-type.js
new file mode 100644
index 0000000000..a54dc5fcd2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-wrong-type.js
@@ -0,0 +1,50 @@
+// |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.duration.prototype.subtract
+description: >
+ Appropriate error thrown when relativeTo cannot be converted to a valid
+ relativeTo string or property bag
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone('UTC');
+const instance = new Temporal.Duration(1, 0, 0, 1);
+
+const primitiveTests = [
+ [undefined, 'undefined'],
+ [null, 'null'],
+ [true, 'boolean'],
+ ['', 'empty string'],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, 'bigint']
+];
+
+for (const [relativeTo, description] of primitiveTests) {
+ assert.throws(
+ typeof relativeTo === 'string' || typeof relativeTo === 'undefined' ? RangeError : TypeError,
+ () => instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo }),
+ `${description} does not convert to a valid ISO string (first argument)`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), 'symbol'],
+ [{}, 'plain object'],
+ [Temporal.PlainDate, 'Temporal.PlainDate, object'],
+ [Temporal.PlainDate.prototype, 'Temporal.PlainDate.prototype, object'],
+ [Temporal.ZonedDateTime, 'Temporal.ZonedDateTime, object'],
+ [Temporal.ZonedDateTime.prototype, 'Temporal.ZonedDateTime.prototype, object']
+];
+
+for (const [relativeTo, description] of typeErrorTests) {
+ assert.throws(
+ TypeError,
+ () => instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo }),
+ `${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/Duration/prototype/subtract/relativeto-year.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-year.js
new file mode 100644
index 0000000000..7b118c661f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-year.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.duration.prototype.subtract
+description: relativeTo with years.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const oneYear = new Temporal.Duration(1);
+const days365 = new Temporal.Duration(0, 0, 0, 365);
+TemporalHelpers.assertDuration(oneYear.subtract(days365, { relativeTo: Temporal.PlainDate.from("2017-01-01") }),
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "non-leap year");
+TemporalHelpers.assertDuration(oneYear.subtract(days365, { relativeTo: Temporal.PlainDate.from("2016-01-01") }),
+ 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, "leap year");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-zoneddatetime-negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-zoneddatetime-negative-epochnanoseconds.js
new file mode 100644
index 0000000000..226653944b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-zoneddatetime-negative-epochnanoseconds.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.duration.prototype.subtract
+description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch
+info: |
+ sec-temporal-getisopartsfromepoch step 1:
+ 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 10<sup>6</sup>.
+ sec-temporal-builtintimezonegetplaindatetimefor step 2:
+ 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]).
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const relativeTo = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC");
+const duration = new Temporal.Duration(0, 0, 0, 1);
+
+// This code path shows up anywhere we convert an exact time, before the Unix
+// epoch, with nonzero microseconds or nanoseconds, into a wall time; in this
+// case via relativeTo.
+
+const result = duration.subtract(duration, { relativeTo });
+TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-zoneddatetime-normalized-time-duration-to-days-range-errors.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-zoneddatetime-normalized-time-duration-to-days-range-errors.js
new file mode 100644
index 0000000000..46307bcaf9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-zoneddatetime-normalized-time-duration-to-days-range-errors.js
@@ -0,0 +1,144 @@
+// |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.duration.prototype.subtract
+description: >
+ Abstract operation NormalizedTimeDurationToDays can throw four different
+ RangeErrors.
+info: |
+ NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ , precalculatedPlainDateTime ] )
+ 22. If days < 0 and sign = 1, throw a RangeError exception.
+ 23. If days > 0 and sign = -1, throw a RangeError exception.
+ ...
+ 25. If NormalizedTimeDurationSign(_norm_) = 1 and sign = -1, throw a RangeError exception.
+ ...
+ 28. If dayLength ≥ 2⁵³, throw a RangeError exception.
+features: [Temporal, BigInt]
+includes: [temporalHelpers.js]
+---*/
+
+const dayNs = 86_400_000_000_000;
+const dayDuration = Temporal.Duration.from({ days: 1 });
+const epochInstant = new Temporal.Instant(0n);
+
+function timeZoneSubstituteValues(
+ getPossibleInstantsFor,
+ getOffsetNanosecondsFor
+) {
+ const tz = new Temporal.TimeZone("UTC");
+ TemporalHelpers.substituteMethod(
+ tz,
+ "getPossibleInstantsFor",
+ getPossibleInstantsFor
+ );
+ TemporalHelpers.substituteMethod(
+ tz,
+ "getOffsetNanosecondsFor",
+ getOffsetNanosecondsFor
+ );
+ return tz;
+}
+
+// Step 22: days < 0 and sign = 1
+let zdt = new Temporal.ZonedDateTime(
+ -1n, // Set DifferenceZonedDateTime _ns1_
+ timeZoneSubstituteValues(
+ [
+ TemporalHelpers.SUBSTITUTE_SKIP, // Behave normally for first call, AddDuration step 15
+ [epochInstant], // Returned in AddDuration step 16, setting _endNs_ -> DifferenceZonedDateTime _ns2_
+ [epochInstant], // Returned in step 16, setting _relativeResult_
+ ],
+ [
+ // Behave normally in 3 calls made prior to NormalizedTimeDurationToDays
+ TemporalHelpers.SUBSTITUTE_SKIP,
+ TemporalHelpers.SUBSTITUTE_SKIP,
+ TemporalHelpers.SUBSTITUTE_SKIP,
+ dayNs - 1, // Returned in step 8, setting _startDateTime_
+ -dayNs + 1, // Returned in step 9, setting _endDateTime_
+ ]
+ )
+);
+assert.throws(RangeError, () =>
+ // Subtracting day from day sets largestUnit to 'day', avoids having any week/month/year components in difference
+ dayDuration.subtract(dayDuration, {
+ relativeTo: zdt,
+ })
+);
+
+// Step 23: days > 0 and sign = -1
+zdt = new Temporal.ZonedDateTime(
+ 1n, // Set DifferenceZonedDateTime _ns1_
+ timeZoneSubstituteValues(
+ [
+ TemporalHelpers.SUBSTITUTE_SKIP, // Behave normally for first call, AddDuration step 15
+ [epochInstant], // Returned in AddDuration step 16, setting _endNs_ -> DifferenceZonedDateTime _ns2_
+ [epochInstant], // Returned in step 16, setting _relativeResult_
+ ],
+ [
+ // Behave normally in 3 calls made prior to NanosecondsToDays
+ TemporalHelpers.SUBSTITUTE_SKIP,
+ TemporalHelpers.SUBSTITUTE_SKIP,
+ TemporalHelpers.SUBSTITUTE_SKIP,
+ -dayNs + 1, // Returned in step 8, setting _startDateTime_
+ dayNs - 1, // Returned in step 9, setting _endDateTime_
+ ]
+ )
+);
+assert.throws(RangeError, () =>
+ // Subtracting day from day sets largestUnit to 'day', avoids having any week/month/year components in difference
+ dayDuration.subtract(dayDuration, {
+ relativeTo: zdt,
+ })
+);
+
+// Step 25: nanoseconds > 0 and sign = -1
+zdt = new Temporal.ZonedDateTime(
+ 0n, // Set DifferenceZonedDateTime _ns1_
+ timeZoneSubstituteValues(
+ [
+ TemporalHelpers.SUBSTITUTE_SKIP, // Behave normally for first call, AddDuration step 15
+ [new Temporal.Instant(-1n)], // Returned in AddDuration step 16, setting _endNs_ -> DifferenceZonedDateTime _ns2_
+ [new Temporal.Instant(-2n)], // Returned in step 16, setting _relativeResult_
+ [new Temporal.Instant(-4n)], // Returned in step 21.a, setting _oneDayFarther_
+ ],
+ [
+ // Behave normally in 3 calls made prior to NanosecondsToDays
+ TemporalHelpers.SUBSTITUTE_SKIP,
+ TemporalHelpers.SUBSTITUTE_SKIP,
+ TemporalHelpers.SUBSTITUTE_SKIP,
+ dayNs - 1, // Returned in step 8, setting _startDateTime_
+ -dayNs + 1, // Returned in step 9, setting _endDateTime_
+ ]
+ )
+);
+assert.throws(RangeError, () =>
+ // Subtracting day from day sets largestUnit to 'day', avoids having any week/month/year components in difference
+ dayDuration.subtract(dayDuration, {
+ relativeTo: zdt,
+ })
+);
+
+// Step 28: day length is an unsafe integer
+zdt = new Temporal.ZonedDateTime(
+ 0n,
+ timeZoneSubstituteValues(
+ [
+ TemporalHelpers.SUBSTITUTE_SKIP, // Behave normally for AddDuration step 15
+ TemporalHelpers.SUBSTITUTE_SKIP, // Behave normally for AddDuration step 16
+ TemporalHelpers.SUBSTITUTE_SKIP, // Behave normally for step 16, setting _relativeResult_
+ // Returned in step 21.a, making _oneDayFarther_ 2^53 ns later than _relativeResult_
+ [new Temporal.Instant(2n ** 53n - 3n * BigInt(dayNs))],
+ ],
+ []
+ )
+);
+const twoDaysDuration = new Temporal.Duration(0, 0, 0, 2);
+assert.throws(RangeError, () =>
+ dayDuration.subtract(twoDaysDuration, {
+ relativeTo: zdt,
+ }),
+ "Should throw RangeError when time zone calculates an outrageous day length"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..78f63bce37
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.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.duration.prototype.subtract
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+ const other = new Temporal.Duration(0, 3);
+ const relativeTo = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => duration.subtract(other, { relativeTo }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..bc7523b3b9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.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.duration.prototype.subtract
+description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable
+features: [BigInt, Symbol, Temporal, arrow-function]
+---*/
+
+[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => {
+ const timeZone = new Temporal.TimeZone("UTC");
+ const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+ const other = new Temporal.Duration(0, 3);
+ const relativeTo = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => duration.subtract(other, { relativeTo }),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..c9b6b5f5d2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.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.duration.prototype.subtract
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+ const other = new Temporal.Duration(0, 3);
+ const relativeTo = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => duration.subtract(other, { relativeTo }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..a4f5756849
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,29 @@
+// |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.duration.prototype.subtract
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+ const other = new Temporal.Duration(0, 3);
+ const relativeTo = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => duration.subtract(other, { relativeTo }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/result-out-of-range-1.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/result-out-of-range-1.js
new file mode 100644
index 0000000000..4fe4021308
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/result-out-of-range-1.js
@@ -0,0 +1,31 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.subtract
+description: >
+ BalanceDuration throws a RangeError when the result is too large.
+features: [Temporal]
+---*/
+
+const plainDate = new Temporal.PlainDate(1970, 1, 1);
+const zonedDateTime = new Temporal.ZonedDateTime(0n, "UTC", "iso8601");
+
+// Largest temporal unit is "second".
+const duration1 = Temporal.Duration.from({seconds: Number.MAX_SAFE_INTEGER});
+const duration2 = Temporal.Duration.from({seconds: -Number.MAX_SAFE_INTEGER});
+
+assert.throws(RangeError, () => {
+ duration1.subtract(duration2);
+});
+
+assert.throws(RangeError, () => {
+ duration1.subtract(duration2, {relativeTo: plainDate});
+});
+
+assert.throws(RangeError, () => {
+ duration1.subtract(duration2, {relativeTo: zonedDateTime});
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/result-out-of-range-2.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/result-out-of-range-2.js
new file mode 100644
index 0000000000..31f68e71d4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/result-out-of-range-2.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.subtract
+description: >
+ BalanceDuration throws a RangeError when the result is too large.
+features: [Temporal]
+---*/
+
+// Math.trunc(Number.MAX_SAFE_INTEGER / 86400) === 104249991374
+var duration1 = Temporal.Duration.from({days: 104249991374});
+var duration2 = Temporal.Duration.from({days: -104249991374});
+
+assert.throws(RangeError, () => duration1.subtract(duration2));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/shell.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/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/Duration/prototype/subtract/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/subclassing-ignored.js
new file mode 100644
index 0000000000..f0e9178957
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/subclassing-ignored.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.duration.prototype.subtract
+description: Objects of a subclass are never created as return values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.Duration,
+ [0, 0, 0, 4, 5, 6, 7, 987, 654, 321],
+ "subtract",
+ [{ nanoseconds: 1 }],
+ (result) => TemporalHelpers.assertDuration(result, 0, 0, 0, 4, 5, 6, 7, 987, 654, 320),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/timezone-getpossibleinstantsfor-iterable.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/timezone-getpossibleinstantsfor-iterable.js
new file mode 100644
index 0000000000..3681c19761
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/timezone-getpossibleinstantsfor-iterable.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.duration.prototype.subtract
+description: An iterable returned from timeZone.getPossibleInstantsFor is consumed after each call
+info: |
+ sec-temporal.duration.prototype.subtract steps 5–6:
+ 5. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_).
+ 6. Let _result_ be ? AddDuration(_duration_.[[Years]], [...], _duration_.[[Nanoseconds]], −_other_.[[Years]], [...], −_other_.[[Nanoseconds]], _relativeTo_).
+ sec-temporal-torelativetemporalobject step 6.d:
+ d. Let _epochNanoseconds_ be ? InterpretISODateTimeOffset(_result_.[[Year]], [...], _result_.[[Nanosecond]], _offsetNs_, _timeZone_, *"compatible"*, *"reject"*).
+ sec-temporal-interpretisodatetimeoffset step 7:
+ 7. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_).
+ sec-temporal-addduration steps 7.d–e and 7.g.i:
+ d. Let _intermediateNs_ be ? AddZonedDateTime(_relativeTo_.[[Nanoseconds]], _timeZone_, _calendar_, _y1_, [...], _ns1_).
+ e. Let _endNs_ be ? AddZonedDateTime(_intermediateNs_, _timeZone_, _calendar_, _y2_, [...], _ns2_).
+ [...]
+ i. Let _result_ be ? DifferenceZonedDateTime(_relativeTo_.[[Nanoseconds]], _endNs_, _timeZone_, _calendar_, _largestUnit_).
+ sec-temporal-differencezoneddatetime step 8:
+ 8. Let _intermediateNs_ be ? AddZonedDateTime(_ns1_, _timeZone_, _calendar_, _dateDifference_.[[Years]], _dateDifference_.[[Months]], _dateDifference_.[[Weeks]], 0, 0, 0, 0, 0, 0, 0).
+ sec-temporal-addzoneddatetime step 8:
+ 8. Let _intermediateInstant_ be ? BuiltinTimeZoneGetInstantFor(_timeZone_, _intermediateDateTime_, *"compatible"*).
+ sec-temporal-builtintimezonegetinstantfor step 1:
+ 1. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_).
+ sec-temporal-getpossibleinstantsfor step 2:
+ 2. Let _list_ be ? IterableToList(_possibleInstants_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "2000-01-01T09:00:00", // called once on the input relativeTo object
+ "2001-01-01T09:00:00", // called once on relativeTo plus the receiver
+ "1999-12-01T09:00:00", // called once on relativeTo plus the receiver minus the argument
+ "1999-12-01T09:00:00", // called once on relativeTo plus the years, months, and weeks from the difference of relativeTo minus endNs
+];
+
+TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => {
+ const duration1 = new Temporal.Duration(1);
+ const duration2 = new Temporal.Duration(0, 13);
+ duration1.subtract(duration2, { relativeTo: { year: 2000, month: 1, day: 1, hour: 9, timeZone } });
+}, expected);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/year-zero.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/year-zero.js
new file mode 100644
index 0000000000..964377ee64
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/year-zero.js
@@ -0,0 +1,20 @@
+// |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.duration.prototype.subtract
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const instance = new Temporal.Duration(1, 0, 0, 1);
+
+let relativeTo = "-000000-11-04T00:00";
+assert.throws(
+ RangeError,
+ () => { instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo }); },
+ "reject minus zero as extended year"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/balance-subseconds.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/balance-subseconds.js
new file mode 100644
index 0000000000..03b0b0f6d0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/balance-subseconds.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.duration.prototype.tojson
+description: Balancing from subsecond units to seconds happens correctly
+features: [Temporal]
+---*/
+
+const pos = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 999, 999999, 999999999);
+assert.sameValue(pos.toJSON(), "PT2.998998999S");
+
+const neg = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, -999, -999999, -999999999);
+assert.sameValue(neg.toJSON(), "-PT2.998998999S");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/basic.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/basic.js
new file mode 100644
index 0000000000..de59acd3c3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/basic.js
@@ -0,0 +1,243 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.tojson
+description: Temporal.Duration.prototype.toJSON will return correct iso8601 string for the given duration.
+info: |
+ 1. Let duration be the this value.
+ 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]).
+ 3. Return ! TemporalDurationToString(duration.[[Years]], duration.[[Months]], duration.[[Weeks]], duration.[[Days]], duration.[[Hours]], duration.[[Minutes]], duration.[[Seconds]], duration.[[Milliseconds]], duration.[[Microseconds]], duration.[[Nanoseconds]], "auto").
+features: [Temporal]
+---*/
+
+let d = new Temporal.Duration();
+assert.sameValue(d.toJSON(), "PT0S", "zero duration");
+
+d = new Temporal.Duration(1);
+assert.sameValue(d.toJSON(), "P1Y", "positive small years");
+d = new Temporal.Duration(-1);
+assert.sameValue(d.toJSON(), "-P1Y", "negative small years");
+d = new Temporal.Duration(1234567890);
+assert.sameValue(d.toJSON(), "P1234567890Y", "positive large years");
+d = new Temporal.Duration(-1234567890);
+assert.sameValue(d.toJSON(), "-P1234567890Y", "negative large years");
+
+d = new Temporal.Duration(1, 2);
+assert.sameValue(d.toJSON(), "P1Y2M", "positive years and months");
+d = new Temporal.Duration(-1, -2);
+assert.sameValue(d.toJSON(), "-P1Y2M", "negative years and months");
+d = new Temporal.Duration(0, 2);
+assert.sameValue(d.toJSON(), "P2M", "positive small months");
+d = new Temporal.Duration(0,-2);
+assert.sameValue(d.toJSON(), "-P2M", "negative small months");
+d = new Temporal.Duration(0, 1234567890);
+assert.sameValue(d.toJSON(), "P1234567890M", "positive large months");
+d = new Temporal.Duration(0,-1234567890);
+assert.sameValue(d.toJSON(), "-P1234567890M", "negative large months");
+
+d = new Temporal.Duration(1, 2, 3);
+assert.sameValue(d.toJSON(), "P1Y2M3W", "positive years, months, weeks");
+d = new Temporal.Duration(-1, -2, -3);
+assert.sameValue(d.toJSON(), "-P1Y2M3W", "negative years, months, weeks");
+d = new Temporal.Duration(0, 0, 3);
+assert.sameValue(d.toJSON(), "P3W", "positive small weeks");
+d = new Temporal.Duration(0, 0, -3);
+assert.sameValue(d.toJSON(), "-P3W", "negative small weeks");
+d = new Temporal.Duration(1, 0, 3);
+assert.sameValue(d.toJSON(), "P1Y3W", "positive years and weeks");
+d = new Temporal.Duration(-1, 0, -3);
+assert.sameValue(d.toJSON(), "-P1Y3W", "negative years and weeks");
+d = new Temporal.Duration(0, 2, 3);
+assert.sameValue(d.toJSON(), "P2M3W", "positive months and weeks");
+d = new Temporal.Duration(0, -2, -3);
+assert.sameValue(d.toJSON(), "-P2M3W", "negative months and weeks");
+d = new Temporal.Duration(0, 0, 1234567890);
+assert.sameValue(d.toJSON(), "P1234567890W", "positive large weeks");
+d = new Temporal.Duration(0, 0, -1234567890);
+assert.sameValue(d.toJSON(), "-P1234567890W", "negative large weeks");
+
+d = new Temporal.Duration(1, 2, 3, 4);
+assert.sameValue(d.toJSON(), "P1Y2M3W4D", "positive years, months, weeks, days");
+d = new Temporal.Duration(-1, -2, -3, -4);
+assert.sameValue(d.toJSON(), "-P1Y2M3W4D", "negative years, months, weeks, days");
+d = new Temporal.Duration(0, 0, 0, 1234567890);
+assert.sameValue(d.toJSON(), "P1234567890D", "positive large days");
+d = new Temporal.Duration(0, 0, 0, -1234567890);
+assert.sameValue(d.toJSON(), "-P1234567890D", "negative large days");
+d = new Temporal.Duration(0, 0, 0, 4);
+assert.sameValue(d.toJSON(), "P4D", "positive small days");
+d = new Temporal.Duration(0, 0, 0, -4);
+assert.sameValue(d.toJSON(), "-P4D", "negative small days");
+d = new Temporal.Duration(1, 0, 0, 4);
+assert.sameValue(d.toJSON(), "P1Y4D", "positive years and days");
+d = new Temporal.Duration(-1, 0, 0, -4);
+assert.sameValue(d.toJSON(), "-P1Y4D", "negative years and days");
+d = new Temporal.Duration(0, 2, 0, 4);
+assert.sameValue(d.toJSON(), "P2M4D", "positive months and days");
+d = new Temporal.Duration(0, -2, 0, -4);
+assert.sameValue(d.toJSON(), "-P2M4D", "negative months and days");
+d = new Temporal.Duration(0, 0, 3, 4);
+assert.sameValue(d.toJSON(), "P3W4D", "positive weeks and days");
+d = new Temporal.Duration(0, 0, -3, -4);
+assert.sameValue(d.toJSON(), "-P3W4D", "negative weeks and days");
+
+d = new Temporal.Duration(0, 0, 0, 0, 5);
+assert.sameValue(d.toJSON(), "PT5H", "positive hours");
+d = new Temporal.Duration(0, 0, 0, 0, -5);
+assert.sameValue(d.toJSON(), "-PT5H", "negative hours");
+d = new Temporal.Duration(1, 0, 0, 0, 5);
+assert.sameValue(d.toJSON(), "P1YT5H", "positive years and hours");
+d = new Temporal.Duration(-1, 0, 0, 0, -5);
+assert.sameValue(d.toJSON(), "-P1YT5H", "negative years and hours");
+d = new Temporal.Duration(0, 2, 0, 0, 5);
+assert.sameValue(d.toJSON(), "P2MT5H", "positive months and hours");
+d = new Temporal.Duration(0, -2, 0, 0, -5);
+assert.sameValue(d.toJSON(), "-P2MT5H", "negative months and hours");
+
+d = new Temporal.Duration(0, 0, 0, 0, 0, 6);
+assert.sameValue(d.toJSON(), "PT6M", "positive minutes");
+d = new Temporal.Duration(0, 0, 0, 0, 0, -6);
+assert.sameValue(d.toJSON(), "-PT6M", "negative minutes");
+d = new Temporal.Duration(0, 0, 0, 0, 5, 6);
+assert.sameValue(d.toJSON(), "PT5H6M", "positive hours and minutes");
+d = new Temporal.Duration(0, 0, 0, 0, -5, -6);
+assert.sameValue(d.toJSON(), "-PT5H6M", "negative hours and minutes");
+d = new Temporal.Duration(0, 0, 3, 0, 0, 6);
+assert.sameValue(d.toJSON(), "P3WT6M", "positive weeks and minutes");
+d = new Temporal.Duration(0, 0, -3, 0, 0, -6);
+assert.sameValue(d.toJSON(), "-P3WT6M", "negative weeks and minutes");
+d = new Temporal.Duration(0, 0, 0, 4, 0, 6);
+assert.sameValue(d.toJSON(), "P4DT6M", "positive days and minutes");
+d = new Temporal.Duration(0, 0, 0, -4, 0, -6);
+assert.sameValue(d.toJSON(), "-P4DT6M", "negative days and minutes");
+
+d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 7);
+assert.sameValue(d.toJSON(), "PT7S", "positive seconds");
+d = new Temporal.Duration(0, 0, 0, 0, 0, 0, -7);
+assert.sameValue(d.toJSON(), "-PT7S", "negative seconds");
+d = new Temporal.Duration(0, 0, 0, 0, 5, 0, 7);
+assert.sameValue(d.toJSON(), "PT5H7S", "positive hours and seconds");
+d = new Temporal.Duration(0, 0, 0, 0, -5, 0, -7);
+assert.sameValue(d.toJSON(), "-PT5H7S", "negative hours and seconds");
+d = new Temporal.Duration(0, 0, 0, 0, 0, 6, 7);
+assert.sameValue(d.toJSON(), "PT6M7S", "positive minutes and seconds");
+d = new Temporal.Duration(0, 0, 0, 0, 0, -6, -7);
+assert.sameValue(d.toJSON(), "-PT6M7S", "negative minutes and seconds");
+d = new Temporal.Duration(0, 0, 0, 0, 5, 6, 7);
+assert.sameValue(d.toJSON(), "PT5H6M7S", "positive hours, minutes, seconds");
+d = new Temporal.Duration(0, 0, 0, 0, -5, -6, -7);
+assert.sameValue(d.toJSON(), "-PT5H6M7S", "negative hours, minutes, seconds");
+d = new Temporal.Duration(1, 0, 0, 0, 5, 6, 7);
+assert.sameValue(d.toJSON(), "P1YT5H6M7S", "positive years, hours, minutes, seconds");
+d = new Temporal.Duration(-1, 0, 0, 0, -5, -6, -7);
+assert.sameValue(d.toJSON(), "-P1YT5H6M7S", "negative years, hours, minutes, seconds");
+
+d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 8);
+assert.sameValue(d.toJSON(), "PT0.008S", "positive milliseconds");
+d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, -8);
+assert.sameValue(d.toJSON(), "-PT0.008S", "negative milliseconds");
+d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 80);
+assert.sameValue(d.toJSON(), "PT0.08S", "positive milliseconds multiple of 10");
+d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, -80);
+assert.sameValue(d.toJSON(), "-PT0.08S", "negative milliseconds multiple of 10");
+d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 87);
+assert.sameValue(d.toJSON(), "PT0.087S", "positive two-digit milliseconds");
+d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, -87);
+assert.sameValue(d.toJSON(), "-PT0.087S", "negative two-digit milliseconds");
+d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 876);
+assert.sameValue(d.toJSON(), "PT0.876S", "positive three-digit milliseconds");
+d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, -876);
+assert.sameValue(d.toJSON(), "-PT0.876S", "negative three-digit milliseconds");
+d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 876543);
+assert.sameValue(d.toJSON(), "PT876.543S", "positive large milliseconds");
+d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, -876543);
+assert.sameValue(d.toJSON(), "-PT876.543S", "negative large milliseconds");
+
+d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 9);
+assert.sameValue(d.toJSON(), "PT0.000009S", "positive microseconds");
+d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, -9);
+assert.sameValue(d.toJSON(), "-PT0.000009S", "negative microseconds");
+d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 90);
+assert.sameValue(d.toJSON(), "PT0.00009S", "positive microseconds multiple of 10");
+d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, -90);
+assert.sameValue(d.toJSON(), "-PT0.00009S", "negative microseconds multiple of 10");
+d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 98);
+assert.sameValue(d.toJSON(), "PT0.000098S", "positive two-digit microseconds");
+d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, -98);
+assert.sameValue(d.toJSON(), "-PT0.000098S", "negative two-digit microseconds");
+d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 900);
+assert.sameValue(d.toJSON(), "PT0.0009S", "positive microseconds multiple of 100");
+d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, -900);
+assert.sameValue(d.toJSON(), "-PT0.0009S", "negative microseconds multiple of 100");
+d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 987);
+assert.sameValue(d.toJSON(), "PT0.000987S", "positive three-digit microseconds");
+d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, -987);
+assert.sameValue(d.toJSON(), "-PT0.000987S", "negative three-digit microseconds");
+d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 987654);
+assert.sameValue(d.toJSON(), "PT0.987654S", "positive large microseconds");
+d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, -987654);
+assert.sameValue(d.toJSON(), "-PT0.987654S", "negative large microseconds");
+d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 987654321);
+assert.sameValue(d.toJSON(), "PT987.654321S", "positive larger microseconds");
+d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, -987654321);
+assert.sameValue(d.toJSON(), "-PT987.654321S", "negative larger microseconds");
+
+d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, 1);
+assert.sameValue(d.toJSON(), "PT0.000000001S", "positive nanoseconds");
+d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, -1);
+assert.sameValue(d.toJSON(), "-PT0.000000001S", "negative nanoseconds");
+d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, 10);
+assert.sameValue(d.toJSON(), "PT0.00000001S", "positive nanoseconds multiple of 10");
+d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, -10);
+assert.sameValue(d.toJSON(), "-PT0.00000001S", "negative nanoseconds multiple of 10");
+d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, 12);
+assert.sameValue(d.toJSON(), "PT0.000000012S", "positive two-digit nanoseconds");
+d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, -12);
+assert.sameValue(d.toJSON(), "-PT0.000000012S", "negative two-digit nanoseconds");
+d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, 100);
+assert.sameValue(d.toJSON(), "PT0.0000001S", "positive nanoseconds multiple of 100");
+d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, -100);
+assert.sameValue(d.toJSON(), "-PT0.0000001S", "negative nanoseconds multiple of 100");
+d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, 123);
+assert.sameValue(d.toJSON(), "PT0.000000123S", "positive three-digit nanoseconds");
+d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, -123);
+assert.sameValue(d.toJSON(), "-PT0.000000123S", "negative three-digit nanoseconds");
+d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, 123456);
+assert.sameValue(d.toJSON(), "PT0.000123456S", "positive large nanoseconds");
+d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, -123456);
+assert.sameValue(d.toJSON(), "-PT0.000123456S", "negative large nanoseconds");
+d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, 123456789);
+assert.sameValue(d.toJSON(), "PT0.123456789S", "positive larger nanoseconds");
+d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, -123456789);
+assert.sameValue(d.toJSON(), "-PT0.123456789S", "negative larger nanoseconds");
+d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, 1234567891);
+assert.sameValue(d.toJSON(), "PT1.234567891S", "positive even larger nanoseconds");
+d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, -1234567891);
+assert.sameValue(d.toJSON(), "-PT1.234567891S", "negative even larger nanoseconds");
+
+d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 4, 3, 2, 1);
+assert.sameValue(d.toJSON(), "PT4.003002001S", "positive seconds and subseconds");
+d = new Temporal.Duration(0, 0, 0, 0, 0, 0, -4, -3, -2, -1);
+assert.sameValue(d.toJSON(), "-PT4.003002001S", "negative seconds and subseconds");
+d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 4, 3, 2, 90001);
+assert.sameValue(d.toJSON(), "PT4.003092001S", "positive seconds and large subseconds");
+d = new Temporal.Duration(0, 0, 0, 0, 0, 0, -4, -3, -2, -90001);
+assert.sameValue(d.toJSON(), "-PT4.003092001S", "negative seconds and large subseconds");
+d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 4, 3, 2, 90080001);
+assert.sameValue(d.toJSON(), "PT4.093082001S", "positive seconds and larger subseconds");
+d = new Temporal.Duration(0, 0, 0, 0, 0, 0, -4, -3, -2, -90080001);
+assert.sameValue(d.toJSON(), "-PT4.093082001S", "negative seconds and larger subseconds");
+
+d = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 8, 9, 1);
+assert.sameValue(d.toJSON(), "P1Y2M3W4DT5H6M7.008009001S", "all fields positive");
+d = new Temporal.Duration(-1, -2, -3, -4, -5, -6, -7, -8, -9, -1);
+assert.sameValue(d.toJSON(), "-P1Y2M3W4DT5H6M7.008009001S", "all fields negative");
+
+d = new Temporal.Duration(1234, 2345, 3456, 4567, 5678, 6789, 7890, 890, 901, 123);
+assert.sameValue(d.toJSON(), "P1234Y2345M3456W4567DT5678H6789M7890.890901123S", "all fields large and positive");
+d = new Temporal.Duration(-1234, -2345, -3456, -4567, -5678, -6789, -7890, -890, -901, -123);
+assert.sameValue(d.toJSON(), "-P1234Y2345M3456W4567DT5678H6789M7890.890901123S", "all fields large and negative");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/branding.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/branding.js
new file mode 100644
index 0000000000..c114c2f320
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/branding.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.duration.prototype.tojson
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toJSON = Temporal.Duration.prototype.toJSON;
+
+assert.sameValue(typeof toJSON, "function");
+
+assert.throws(TypeError, () => toJSON.call(undefined), "undefined");
+assert.throws(TypeError, () => toJSON.call(null), "null");
+assert.throws(TypeError, () => toJSON.call(true), "true");
+assert.throws(TypeError, () => toJSON.call(""), "empty string");
+assert.throws(TypeError, () => toJSON.call(Symbol()), "symbol");
+assert.throws(TypeError, () => toJSON.call(1), "1");
+assert.throws(TypeError, () => toJSON.call({}), "plain object");
+assert.throws(TypeError, () => toJSON.call(Temporal.Duration), "Temporal.Duration");
+assert.throws(TypeError, () => toJSON.call(Temporal.Duration.prototype), "Temporal.Duration.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/browser.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/builtin.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/builtin.js
new file mode 100644
index 0000000000..29a37e9511
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/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.duration.prototype.tojson
+description: >
+ Tests that Temporal.Duration.prototype.toJSON
+ 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.Duration.prototype.toJSON),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Duration.prototype.toJSON),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Duration.prototype.toJSON),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Duration.prototype.toJSON.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/length.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/length.js
new file mode 100644
index 0000000000..0ce234cfca
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/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.duration.prototype.tojson
+description: Temporal.Duration.prototype.toJSON.length is 0
+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.Duration.prototype.toJSON, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/max-value.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/max-value.js
new file mode 100644
index 0000000000..3a7a45f639
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/max-value.js
@@ -0,0 +1,14 @@
+// |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.duration.prototype.tojson
+description: Balancing the maximum nanoseconds and seconds does not go out of range
+features: [Temporal]
+---*/
+
+const d = new Temporal.Duration(0, 0, 0, 0, 0, 0, /* s = */ Number.MAX_SAFE_INTEGER, 0, 0, /* ns = */ 999_999_999);
+assert.sameValue(d.toJSON(), "PT9007199254740991.999999999S", "max value ns and s does not go out of range");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/name.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/name.js
new file mode 100644
index 0000000000..83983ff0cd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/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.duration.prototype.tojson
+description: Temporal.Duration.prototype.toJSON.name is "toJSON".
+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.Duration.prototype.toJSON, "name", {
+ value: "toJSON",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/negative-components.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/negative-components.js
new file mode 100644
index 0000000000..1abb626c2f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/negative-components.js
@@ -0,0 +1,14 @@
+// |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.duration.prototype.tojson
+description: Temporal.Duration.toJSON handles negative components
+features: [Temporal]
+---*/
+const d = new Temporal.Duration(-1, -1, -1, -1, -1, -1, -1, -1, -1, -1);
+const expected = "-P1Y1M1W1DT1H1M1.001001001S";
+assert.sameValue(d.toJSON(), expected, "toJSON with negative components");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/not-a-constructor.js
new file mode 100644
index 0000000000..b06732e812
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/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.duration.prototype.tojson
+description: >
+ Temporal.Duration.prototype.toJSON 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.Duration.prototype.toJSON();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Duration.prototype.toJSON), false,
+ "isConstructor(Temporal.Duration.prototype.toJSON)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/options.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/options.js
new file mode 100644
index 0000000000..c1bd6cbfff
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/options.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.duration.prototype.tojson
+description: Temporal.Duration.prototype.toJSON does not support options, unlike toString.
+features: [Temporal]
+---*/
+
+let called = 0;
+const options = new Proxy({}, {
+ get() {
+ ++called;
+ }
+});
+const d = new Temporal.Duration(1, 2);
+assert.sameValue(d.toJSON(options), "P1Y2M");
+assert.sameValue(called, 0);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/prop-desc.js
new file mode 100644
index 0000000000..4755b35f41
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/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.duration.prototype.tojson
+description: The "toJSON" property of Temporal.Duration.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Duration.prototype.toJSON,
+ "function",
+ "`typeof Duration.prototype.toJSON` is `function`"
+);
+
+verifyProperty(Temporal.Duration.prototype, "toJSON", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/shell.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/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/Duration/prototype/toLocaleString/branding.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toLocaleString/branding.js
new file mode 100644
index 0000000000..b827628363
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toLocaleString/branding.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.duration.prototype.tolocalestring
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toLocaleString = Temporal.Duration.prototype.toLocaleString;
+
+assert.sameValue(typeof toLocaleString, "function");
+
+assert.throws(TypeError, () => toLocaleString.call(undefined), "undefined");
+assert.throws(TypeError, () => toLocaleString.call(null), "null");
+assert.throws(TypeError, () => toLocaleString.call(true), "true");
+assert.throws(TypeError, () => toLocaleString.call(""), "empty string");
+assert.throws(TypeError, () => toLocaleString.call(Symbol()), "symbol");
+assert.throws(TypeError, () => toLocaleString.call(1), "1");
+assert.throws(TypeError, () => toLocaleString.call({}), "plain object");
+assert.throws(TypeError, () => toLocaleString.call(Temporal.Duration), "Temporal.Duration");
+assert.throws(TypeError, () => toLocaleString.call(Temporal.Duration.prototype), "Temporal.Duration.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toLocaleString/browser.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toLocaleString/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toLocaleString/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toLocaleString/builtin.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toLocaleString/builtin.js
new file mode 100644
index 0000000000..cf41e34868
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toLocaleString/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.duration.prototype.tolocalestring
+description: >
+ Tests that Temporal.Duration.prototype.toLocaleString
+ 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.Duration.prototype.toLocaleString),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Duration.prototype.toLocaleString),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Duration.prototype.toLocaleString),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Duration.prototype.toLocaleString.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toLocaleString/length.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toLocaleString/length.js
new file mode 100644
index 0000000000..10df0e0aec
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toLocaleString/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.duration.prototype.tolocalestring
+description: Temporal.Duration.prototype.toLocaleString.length is 0
+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.Duration.prototype.toLocaleString, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toLocaleString/name.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toLocaleString/name.js
new file mode 100644
index 0000000000..b00f4554f4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toLocaleString/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.duration.prototype.tolocalestring
+description: Temporal.Duration.prototype.toLocaleString.name is "toLocaleString".
+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.Duration.prototype.toLocaleString, "name", {
+ value: "toLocaleString",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toLocaleString/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toLocaleString/not-a-constructor.js
new file mode 100644
index 0000000000..fe4f3129cd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toLocaleString/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.duration.prototype.tolocalestring
+description: >
+ Temporal.Duration.prototype.toLocaleString 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.Duration.prototype.toLocaleString();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Duration.prototype.toLocaleString), false,
+ "isConstructor(Temporal.Duration.prototype.toLocaleString)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toLocaleString/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toLocaleString/prop-desc.js
new file mode 100644
index 0000000000..080feb7847
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toLocaleString/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.duration.prototype.tolocalestring
+description: The "toLocaleString" property of Temporal.Duration.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Duration.prototype.toLocaleString,
+ "function",
+ "`typeof Duration.prototype.toLocaleString` is `function`"
+);
+
+verifyProperty(Temporal.Duration.prototype, "toLocaleString", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toLocaleString/shell.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toLocaleString/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toLocaleString/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/Duration/prototype/toString/balance-subseconds.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/balance-subseconds.js
new file mode 100644
index 0000000000..0145eefb7c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/balance-subseconds.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.duration.prototype.tostring
+description: Balancing from subsecond units to seconds happens correctly
+features: [Temporal]
+---*/
+
+const pos = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 999, 999999, 999999999);
+assert.sameValue(pos.toString(), "PT2.998998999S");
+
+const neg = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, -999, -999999, -999999999);
+assert.sameValue(neg.toString(), "-PT2.998998999S");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/balance.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/balance.js
new file mode 100644
index 0000000000..b55e263bd0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/balance.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.duration.prototype.tostring
+description: Verify that values are balanced correctly.
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ Temporal.Duration.from({ milliseconds: 3500 }).toString(),
+ "PT3.5S");
+assert.sameValue(
+ Temporal.Duration.from({ microseconds: 3500 }).toString(),
+ "PT0.0035S");
+assert.sameValue(
+ Temporal.Duration.from({ nanoseconds: 3500 }).toString(),
+ "PT0.0000035S");
+assert.sameValue(
+ new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 1111, 1111, 1111).toString(),
+ "PT1.112112111S");
+assert.sameValue(
+ Temporal.Duration.from({ seconds: 120, milliseconds: 3500 }).toString(),
+ "PT123.5S");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/blank-duration-precision.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/blank-duration-precision.js
new file mode 100644
index 0000000000..045365bfe5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/blank-duration-precision.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.duration.prototype.tostring
+description: >
+ Precision is handled correctly for blank durations, whether specified by
+ fractionalSecondDigits or smallestUnit
+features: [Temporal]
+---*/
+
+const blank = new Temporal.Duration();
+
+assert.sameValue(blank.toString({ fractionalSecondDigits: "auto" }), "PT0S");
+assert.sameValue(blank.toString({ fractionalSecondDigits: 0 }), "PT0S");
+assert.sameValue(blank.toString({ fractionalSecondDigits: 2 }), "PT0.00S");
+assert.sameValue(blank.toString({ fractionalSecondDigits: 9 }), "PT0.000000000S");
+
+assert.sameValue(blank.toString({ smallestUnit: "seconds" }), "PT0S");
+assert.sameValue(blank.toString({ smallestUnit: "milliseconds" }), "PT0.000S");
+assert.sameValue(blank.toString({ smallestUnit: "microseconds" }), "PT0.000000S");
+assert.sameValue(blank.toString({ smallestUnit: "nanoseconds" }), "PT0.000000000S");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/branding.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/branding.js
new file mode 100644
index 0000000000..f6af97da1a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/branding.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.duration.prototype.tostring
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toString = Temporal.Duration.prototype.toString;
+
+assert.sameValue(typeof toString, "function");
+
+assert.throws(TypeError, () => toString.call(undefined), "undefined");
+assert.throws(TypeError, () => toString.call(null), "null");
+assert.throws(TypeError, () => toString.call(true), "true");
+assert.throws(TypeError, () => toString.call(""), "empty string");
+assert.throws(TypeError, () => toString.call(Symbol()), "symbol");
+assert.throws(TypeError, () => toString.call(1), "1");
+assert.throws(TypeError, () => toString.call({}), "plain object");
+assert.throws(TypeError, () => toString.call(Temporal.Duration), "Temporal.Duration");
+assert.throws(TypeError, () => toString.call(Temporal.Duration.prototype), "Temporal.Duration.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/browser.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/builtin.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/builtin.js
new file mode 100644
index 0000000000..3004134a02
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/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.duration.prototype.tostring
+description: >
+ Tests that Temporal.Duration.prototype.toString
+ 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.Duration.prototype.toString),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Duration.prototype.toString),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Duration.prototype.toString),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Duration.prototype.toString.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-auto.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-auto.js
new file mode 100644
index 0000000000..f5b75326f1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-auto.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.duration.prototype.tostring
+description: auto value for fractionalSecondDigits option
+features: [Temporal]
+---*/
+
+const wholeSeconds = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7);
+const subSeconds = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 650);
+
+const tests = [
+ [wholeSeconds, "P1Y2M3W4DT5H6M7S"],
+ [subSeconds, "P1Y2M3W4DT5H6M7.98765S"],
+];
+
+for (const [duration, expected] of tests) {
+ assert.sameValue(duration.toString(), expected, "default is to emit seconds and drop trailing zeroes");
+ assert.sameValue(duration.toString({ fractionalSecondDigits: "auto" }), expected, "auto is the default");
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-exact-number-of-digits.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-exact-number-of-digits.js
new file mode 100644
index 0000000000..45f4e7e5be
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-exact-number-of-digits.js
@@ -0,0 +1,40 @@
+// |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.duration.prototype.tostring
+description: |
+ The fractionalSecondDigits and smallestUnit options determine the exact number
+ of digits shown after the decimal separator, no more and no less
+info: |
+ sec-temporaldurationtostring step 21:
+ 21. If any of _seconds_, _milliseconds_, _microseconds_, and _nanoseconds_ are not 0; or _years_, _months_, _weeks_, _days_, _hours_, and _minutes_ are all 0; or _precision_ is not *"auto"*; then
+features: [Temporal]
+---*/
+
+const threeYears = new Temporal.Duration(3);
+assert.sameValue(threeYears.toString({ fractionalSecondDigits: 0 }), "P3YT0S");
+assert.sameValue(threeYears.toString({ smallestUnit: 'seconds' }), "P3YT0S");
+assert.sameValue(threeYears.toString({ smallestUnit: 'milliseconds' }), "P3YT0.000S");
+assert.sameValue(threeYears.toString({ fractionalSecondDigits: 5 }), "P3YT0.00000S");
+
+const halfHour = new Temporal.Duration(0, 0, 0, 0, 0, 30);
+assert.sameValue(halfHour.toString({ fractionalSecondDigits: 0 }), "PT30M0S");
+assert.sameValue(halfHour.toString({ smallestUnit: 'seconds' }), "PT30M0S");
+assert.sameValue(halfHour.toString({ smallestUnit: 'milliseconds' }), "PT30M0.000S");
+assert.sameValue(halfHour.toString({ fractionalSecondDigits: 5 }), "PT30M0.00000S");
+
+const hundredMs = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 100);
+assert.sameValue(hundredMs.toString({ fractionalSecondDigits: 0 }), "PT0S");
+assert.sameValue(hundredMs.toString({ smallestUnit: 'seconds' }), "PT0S");
+assert.sameValue(hundredMs.toString({ smallestUnit: 'milliseconds' }), "PT0.100S");
+assert.sameValue(hundredMs.toString({ fractionalSecondDigits: 5 }), "PT0.10000S");
+
+const zero = new Temporal.Duration();
+assert.sameValue(zero.toString({ fractionalSecondDigits: 0 }), "PT0S");
+assert.sameValue(zero.toString({ smallestUnit: 'seconds' }), "PT0S");
+assert.sameValue(zero.toString({ smallestUnit: 'milliseconds' }), "PT0.000S");
+assert.sameValue(zero.toString({ fractionalSecondDigits: 5 }), "PT0.00000S");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-invalid-string.js
new file mode 100644
index 0000000000..8f7f15b05e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-invalid-string.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.duration.prototype.tostring
+description: RangeError thrown when fractionalSecondDigits option not one of the allowed string values
+info: |
+ sec-getstringornumberoption step 4:
+ 4. If _stringValues_ is not *undefined* and _stringValues_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-tosecondsstringprecision step 9:
+ 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
+ sec-temporal.duration.prototype.tostring step 4:
+ 4. Let _precision_ be ? ToSecondsStringPrecision(_options_).
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 650, 0);
+
+for (const fractionalSecondDigits of ["other string", "AUTO", "not-auto", "autos", "auto\0"]) {
+ assert.throws(RangeError, () => duration.toString({ fractionalSecondDigits }),
+ `"${fractionalSecondDigits}" is not a valid value for fractionalSecondDigits`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-nan.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-nan.js
new file mode 100644
index 0000000000..e138f58935
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-nan.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.duration.prototype.tostring
+description: RangeError thrown when fractionalSecondDigits option is NaN
+info: |
+ sec-getoption step 8.b:
+ b. If _value_ is *NaN*, throw a *RangeError* exception.
+ sec-getstringornumberoption step 2:
+ 2. Let _value_ be ? GetOption(_options_, _property_, « Number, String », *undefined*, _fallback_).
+ sec-temporal-tosecondsstringprecision step 9:
+ 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
+ sec-temporal.duration.prototype.tostring step 4:
+ 4. Let _precision_ be ? ToSecondsStringPrecision(_options_).
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 650, 0);
+assert.throws(RangeError, () => duration.toString({ fractionalSecondDigits: NaN }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-non-integer.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-non-integer.js
new file mode 100644
index 0000000000..7e9072660e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-non-integer.js
@@ -0,0 +1,32 @@
+// |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.duration.prototype.tostring
+description: Rounding for fractionalSecondDigits option
+info: |
+ sec-getstringornumberoption step 3.b:
+ b. Return floor(ℝ(_value_)).
+ sec-temporal-tosecondsstringprecision step 9:
+ 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
+ sec-temporal.duration.prototype.tostring step 4:
+ 4. Let _precision_ be ? ToSecondsStringPrecision(_options_).
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 650, 0);
+
+let string = duration.toString({ fractionalSecondDigits: 2.5 });
+assert.sameValue(string, "P1Y2M3W4DT5H6M7.98S", "fractionalSecondDigits 2.5 floors to 2");
+
+string = duration.toString({ fractionalSecondDigits: 9.7 });
+assert.sameValue(string, "P1Y2M3W4DT5H6M7.987650000S", "fractionalSecondDigits 9.7 floors to 9 and is not out of range");
+
+assert.throws(
+ RangeError,
+ () => duration.toString({ fractionalSecondDigits: -0.6 }),
+ "fractionalSecondDigits -0.6 floors to -1 and is out of range"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-number.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-number.js
new file mode 100644
index 0000000000..0fecfe6518
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-number.js
@@ -0,0 +1,31 @@
+// |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.duration.prototype.tostring
+description: Number for fractionalSecondDigits option
+features: [Temporal]
+---*/
+
+const wholeSeconds = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7);
+const subSeconds = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 650);
+
+assert.sameValue(subSeconds.toString({ fractionalSecondDigits: 0 }), "P1Y2M3W4DT5H6M7S",
+ "truncates 4 decimal places to 0");
+assert.sameValue(wholeSeconds.toString({ fractionalSecondDigits: 2 }), "P1Y2M3W4DT5H6M7.00S",
+ "pads whole seconds to 2 decimal places");
+assert.sameValue(subSeconds.toString({ fractionalSecondDigits: 2 }), "P1Y2M3W4DT5H6M7.98S",
+ "truncates 4 decimal places to 2");
+assert.sameValue(subSeconds.toString({ fractionalSecondDigits: 3 }), "P1Y2M3W4DT5H6M7.987S",
+ "truncates 4 decimal places to 3");
+assert.sameValue(subSeconds.toString({ fractionalSecondDigits: 6 }), "P1Y2M3W4DT5H6M7.987650S",
+ "pads 4 decimal places to 6");
+assert.sameValue(wholeSeconds.toString({ fractionalSecondDigits: 7 }), "P1Y2M3W4DT5H6M7.0000000S",
+ "pads whole seconds to 7 decimal places");
+assert.sameValue(subSeconds.toString({ fractionalSecondDigits: 7 }), "P1Y2M3W4DT5H6M7.9876500S",
+ "pads 4 decimal places to 7");
+assert.sameValue(subSeconds.toString({ fractionalSecondDigits: 9 }), "P1Y2M3W4DT5H6M7.987650000S",
+ "pads 4 decimal places to 9");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-out-of-range.js
new file mode 100644
index 0000000000..a6136916f7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-out-of-range.js
@@ -0,0 +1,29 @@
+// |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.duration.prototype.tostring
+description: RangeError thrown when fractionalSecondDigits option out of range
+info: |
+ sec-getstringornumberoption step 3.a:
+ a. If _value_ < _minimum_ or _value_ > _maximum_, throw a *RangeError* exception.
+ sec-temporal-tosecondsstringprecision step 9:
+ 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
+ sec-temporal.duration.prototype.tostring step 4:
+ 4. Let _precision_ be ? ToSecondsStringPrecision(_options_).
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 650, 0);
+
+assert.throws(RangeError, () => duration.toString({ fractionalSecondDigits: -Infinity }),
+ "−∞ is out of range for fractionalSecondDigits");
+assert.throws(RangeError, () => duration.toString({ fractionalSecondDigits: -1 }),
+ "−1 is out of range for fractionalSecondDigits");
+assert.throws(RangeError, () => duration.toString({ fractionalSecondDigits: 10 }),
+ "10 is out of range for fractionalSecondDigits");
+assert.throws(RangeError, () => duration.toString({ fractionalSecondDigits: Infinity }),
+ "∞ is out of range for fractionalSecondDigits");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-undefined.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-undefined.js
new file mode 100644
index 0000000000..91bad8ddd2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-undefined.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.duration.prototype.tostring
+description: Fallback value for fractionalSecondDigits option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-getstringornumberoption step 2:
+ 2. Let _value_ be ? GetOption(_options_, _property_, « Number, String », *undefined*, _fallback_).
+ sec-temporal-tosecondsstringprecision step 9:
+ 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
+ sec-temporal.duration.prototype.tostring step 4:
+ 4. Let _precision_ be ? ToSecondsStringPrecision(_options_).
+features: [Temporal]
+---*/
+
+const wholeSeconds = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7);
+const subSeconds = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 650);
+
+const tests = [
+ [wholeSeconds, "P1Y2M3W4DT5H6M7S"],
+ [subSeconds, "P1Y2M3W4DT5H6M7.98765S"],
+];
+
+for (const [duration, expected] of tests) {
+ const explicit = duration.toString({ fractionalSecondDigits: undefined });
+ assert.sameValue(explicit, expected, "default fractionalSecondDigits is auto (property present but undefined)");
+
+ const implicit = duration.toString({});
+ assert.sameValue(implicit, expected, "default fractionalSecondDigits is auto (property not present)");
+
+ const lambda = duration.toString(() => {});
+ assert.sameValue(lambda, expected, "default fractionalSecondDigits is auto (property not present, function object)");
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-wrong-type.js
new file mode 100644
index 0000000000..b7a5a4947c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-wrong-type.js
@@ -0,0 +1,50 @@
+// |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.duration.prototype.tostring
+description: Type conversions for fractionalSecondDigits option
+info: |
+ sec-getoption steps 8–9:
+ 8. Else if _type_ is Number, then
+ a. Set _value_ to ? ToNumber(value).
+ b. ...
+ 9. Else,
+ a. Set _value_ to ? ToString(value).
+ sec-getstringornumberoption step 2:
+ 2. Let _value_ be ? GetOption(_options_, _property_, « Number, String », *undefined*, _fallback_).
+ sec-temporal-tosecondsstringprecision step 9:
+ 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
+ sec-temporal.duration.prototype.tostring step 4:
+ 4. Let _precision_ be ? ToSecondsStringPrecision(_options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 650, 0);
+
+assert.throws(RangeError, () => duration.toString({ fractionalSecondDigits: null }),
+ "null is not a number and converts to the string 'null' which is not valid for fractionalSecondDigits");
+assert.throws(RangeError, () => duration.toString({ fractionalSecondDigits: true }),
+ "true is not a number and converts to the string 'true' which is not valid for fractionalSecondDigits");
+assert.throws(RangeError, () => duration.toString({ fractionalSecondDigits: false }),
+ "false is not a number and converts to the string 'false' which is not valid for fractionalSecondDigits");
+assert.throws(TypeError, () => duration.toString({ fractionalSecondDigits: Symbol() }),
+ "symbols are not numbers and cannot convert to strings");
+assert.throws(RangeError, () => duration.toString({ fractionalSecondDigits: 2n }),
+ "bigints are not numbers and convert to strings which are not valid for fractionalSecondDigits");
+assert.throws(RangeError, () => duration.toString({ fractionalSecondDigits: {} }),
+ "plain objects are not numbers and convert to strings which are not valid for fractionalSecondDigits");
+
+const expected = [
+ "get fractionalSecondDigits.toString",
+ "call fractionalSecondDigits.toString",
+];
+const actual = [];
+const observer = TemporalHelpers.toPrimitiveObserver(actual, "auto", "fractionalSecondDigits");
+const result = duration.toString({ fractionalSecondDigits: observer });
+assert.sameValue(result, "P1Y2M3W4DT5H6M7.98765S", "object with toString uses toString return value");
+assert.compareArray(actual, expected, "object with toString calls toString and not valueOf");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/length.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/length.js
new file mode 100644
index 0000000000..e7530de548
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/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.duration.prototype.tostring
+description: Temporal.Duration.prototype.toString.length is 0
+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.Duration.prototype.toString, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/max-value.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/max-value.js
new file mode 100644
index 0000000000..7f1ef0681f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/max-value.js
@@ -0,0 +1,22 @@
+// |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.duration.prototype.tostring
+description: Balancing the maximum nanoseconds and seconds does not go out of range
+features: [Temporal]
+---*/
+
+{
+ const d = new Temporal.Duration(0, 0, 0, 0, 0, 0, /* s = */ Number.MAX_SAFE_INTEGER, 0, 0, /* ns = */ 999_999_999);
+ assert.sameValue(d.toString(), "PT9007199254740991.999999999S", "max value ns and s does not go out of range");
+}
+
+// Based on a test case by André Bargull
+{
+ const d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, /* ms = */ Number.MAX_SAFE_INTEGER, /* µs = */ 2000);
+ assert.sameValue(d.toString(), "PT9007199254740.993S", "values do not lose precision intermediately");
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/name.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/name.js
new file mode 100644
index 0000000000..ab4ccb1325
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/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.duration.prototype.tostring
+description: Temporal.Duration.prototype.toString.name is "toString".
+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.Duration.prototype.toString, "name", {
+ value: "toString",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/negative-components.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/negative-components.js
new file mode 100644
index 0000000000..75e4cd96f9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/negative-components.js
@@ -0,0 +1,41 @@
+// |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.duration.prototype.tostring
+description: Temporal.Duration.toString handles negative components
+features: [Temporal]
+---*/
+assert.sameValue(
+ new Temporal.Duration(-1, -1, -1, -1, -1, -1, -1, -1, -1, -1).toString(),
+ "-P1Y1M1W1DT1H1M1.001001001S");
+assert.sameValue(
+ Temporal.Duration.from({ milliseconds: -250 }).toString(),
+ "-PT0.25S");
+assert.sameValue(
+ Temporal.Duration.from({ milliseconds: -3500 }).toString(),
+ "-PT3.5S");
+assert.sameValue(
+ Temporal.Duration.from({ microseconds: -250 }).toString(),
+ "-PT0.00025S");
+assert.sameValue(
+ Temporal.Duration.from({ microseconds: -3500 }).toString(),
+ "-PT0.0035S");
+assert.sameValue(
+ Temporal.Duration.from({ nanoseconds: -250 }).toString(),
+ "-PT0.00000025S");
+assert.sameValue(
+ Temporal.Duration.from({ nanoseconds: -3500 }).toString(),
+ "-PT0.0000035S");
+assert.sameValue(
+ new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, -1111, -1111, -1111).toString(),
+ "-PT1.112112111S");
+assert.sameValue(
+ Temporal.Duration.from({ seconds: -120, milliseconds: -3500 }).toString(),
+ "-PT123.5S");
+assert.sameValue(
+ Temporal.Duration.from({ weeks: -1, days: -1 }).toString(),
+ "-P1W1D");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/not-a-constructor.js
new file mode 100644
index 0000000000..174d07183b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/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.duration.prototype.tostring
+description: >
+ Temporal.Duration.prototype.toString 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.Duration.prototype.toString();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Duration.prototype.toString), false,
+ "isConstructor(Temporal.Duration.prototype.toString)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/options-object.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/options-object.js
new file mode 100644
index 0000000000..9744dbc388
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/options-object.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.duration.prototype.tostring
+description: Empty or a function object may be used as options
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(0, 0, 0, 0, 1);
+
+const result1 = instance.toString({});
+assert.sameValue(
+ result1, "PT1H",
+ "options may be an empty plain object"
+);
+
+const result2 = instance.toString(() => {});
+assert.sameValue(
+ result2, "PT1H",
+ "options may be a function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/options-undefined.js
new file mode 100644
index 0000000000..5e6c125bc1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/options-undefined.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.duration.prototype.tostring
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 650, 0);
+
+const explicit = duration.toString(undefined);
+assert.sameValue(explicit, "P1Y2M3W4DT5H6M7.98765S", "default precision is auto, and rounding is trunc");
+
+const implicit = duration.toString();
+assert.sameValue(implicit, "P1Y2M3W4DT5H6M7.98765S", "default precision is auto, and rounding is trunc");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/options-wrong-type.js
new file mode 100644
index 0000000000..15f3d94678
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/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.duration.prototype.tostring
+description: TypeError thrown when options argument is a primitive
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const badOptions = [
+ null,
+ true,
+ "some string",
+ Symbol(),
+ 1,
+ 2n,
+];
+
+const instance = new Temporal.Duration(0, 0, 0, 0, 1);
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.toString(value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/order-of-operations.js
new file mode 100644
index 0000000000..78280eba3a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/order-of-operations.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.duration.prototype.tostring
+description: Properties on objects passed to toString() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "get options.fractionalSecondDigits",
+ "get options.fractionalSecondDigits.toString",
+ "call options.fractionalSecondDigits.toString",
+ "get options.roundingMode",
+ "get options.roundingMode.toString",
+ "call options.roundingMode.toString",
+ "get options.smallestUnit",
+];
+const actual = [];
+
+const instance = new Temporal.Duration(1, 1, 1, 1, 1, 1, 1, 1, 1, 1);
+
+const expectedForSmallestUnit = expected.concat([
+ "get options.smallestUnit.toString",
+ "call options.smallestUnit.toString",
+]);
+
+instance.toString(
+ TemporalHelpers.propertyBagObserver(actual, {
+ fractionalSecondDigits: "auto",
+ roundingMode: "halfExpand",
+ smallestUnit: "millisecond",
+ }, "options"),
+);
+assert.compareArray(actual, expectedForSmallestUnit, "order of operations");
+actual.splice(0); // clear
+
+instance.toString(
+ TemporalHelpers.propertyBagObserver(actual, {
+ fractionalSecondDigits: "auto",
+ roundingMode: "halfExpand",
+ smallestUnit: undefined,
+ }, "options"),
+);
+assert.compareArray(actual, expected, "order of operations with smallestUnit undefined");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/precision.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/precision.js
new file mode 100644
index 0000000000..a9b2a915c4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/precision.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.duration.prototype.tostring
+description: toString() produces a fractional part of the correct length
+features: [Temporal]
+---*/
+
+const { Duration } = Temporal;
+
+const durationString = 'PT0.084000159S';
+const duration = Duration.from(durationString);
+const precisionString = duration.toString({
+ smallestUnit: 'milliseconds'
+});
+
+assert.sameValue(durationString, duration.toString());
+assert.sameValue(precisionString, "PT0.084S");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/prop-desc.js
new file mode 100644
index 0000000000..e9457ac377
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/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.duration.prototype.tostring
+description: The "toString" property of Temporal.Duration.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Duration.prototype.toString,
+ "function",
+ "`typeof Duration.prototype.toString` is `function`"
+);
+
+verifyProperty(Temporal.Duration.prototype, "toString", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/round-cross-unit-boundary.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/round-cross-unit-boundary.js
new file mode 100644
index 0000000000..55d5d49e7c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/round-cross-unit-boundary.js
@@ -0,0 +1,43 @@
+// |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.duration.prototype.tostring
+description: Rounding can cross unit boundaries up to days
+features: [Temporal]
+---*/
+
+const roundingMode = "expand";
+
+// Positive, time units
+{
+ const duration = new Temporal.Duration(0, 0, 0, 0, 1, 59, 59, 900);
+ assert.sameValue(duration.toString({ fractionalSecondDigits: 0, roundingMode }), "PT2H0S", "1:59:60 balances to 2 hours");
+}
+
+// Negative, time units
+{
+ const duration = new Temporal.Duration(0, 0, 0, 0, -1, -59, -59, -900);
+ assert.sameValue(duration.toString({ fractionalSecondDigits: 0, roundingMode }), "-PT2H0S", "-1:59:60 balances to -2 hours");
+}
+
+// Positive, date and time units
+{
+ const duration = new Temporal.Duration(1, 11, 0, 30, 23, 59, 59, 999, 999, 999);
+ assert.sameValue(duration.toString({ fractionalSecondDigits: 8, roundingMode }), "P1Y11M31DT0.00000000S", "units balance only up to days (positive)");
+}
+
+// Negative, date and time units
+{
+ const duration = new Temporal.Duration(-1, -11, 0, -30, -23, -59, -59, -999, -999, -999);
+ assert.sameValue(duration.toString({ fractionalSecondDigits: 8, roundingMode }), "-P1Y11M31DT0.00000000S", "units balance only up to days (negative)");
+}
+
+// No balancing if smallest unit is largest unit
+{
+ const duration = new Temporal.Duration(0, 0, 0, 0, 0, 0, 59, 900);
+ assert.sameValue(duration.toString({ fractionalSecondDigits: 0, roundingMode }), "PT60S", "60 seconds stays as is");
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/roundingmode-ceil.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/roundingmode-ceil.js
new file mode 100644
index 0000000000..9ea83f7c31
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/roundingmode-ceil.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.duration.prototype.tostring
+description: ceil value for roundingMode option
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 123, 987, 500);
+
+const result1 = duration.toString({ smallestUnit: "microsecond", roundingMode: "ceil" });
+assert.sameValue(result1, "P1Y2M3W4DT5H6M7.123988S",
+ "roundingMode is ceil (with 6 digits from smallestUnit)");
+
+const result2 = duration.toString({ fractionalSecondDigits: 6, roundingMode: "ceil" });
+assert.sameValue(result2, "P1Y2M3W4DT5H6M7.123988S",
+ "roundingMode is ceil (with 6 digits from fractionalSecondDigits)");
+
+const result3 = duration.toString({ smallestUnit: "millisecond", roundingMode: "ceil" });
+assert.sameValue(result3, "P1Y2M3W4DT5H6M7.124S",
+ "roundingMode is ceil (with 3 digits from smallestUnit)");
+
+const result4 = duration.toString({ fractionalSecondDigits: 3, roundingMode: "ceil" });
+assert.sameValue(result4, "P1Y2M3W4DT5H6M7.124S",
+ "roundingMode is ceil (with 3 digits from fractionalSecondDigits)");
+
+const result5 = duration.toString({ smallestUnit: "second", roundingMode: "ceil" });
+assert.sameValue(result5, "P1Y2M3W4DT5H6M8S",
+ "roundingMode is ceil (with 0 digits from smallestUnit)");
+
+const result6 = duration.toString({ fractionalSecondDigits: 0, roundingMode: "ceil" });
+assert.sameValue(result6, "P1Y2M3W4DT5H6M8S",
+ "roundingMode is ceil (with 0 digits from fractionalSecondDigits)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/roundingmode-floor.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/roundingmode-floor.js
new file mode 100644
index 0000000000..9337b0eea9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/roundingmode-floor.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.duration.prototype.tostring
+description: floor value for roundingMode option
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 123, 987, 500);
+
+const result1 = duration.toString({ smallestUnit: "microsecond", roundingMode: "floor" });
+assert.sameValue(result1, "P1Y2M3W4DT5H6M7.123987S",
+ "roundingMode is floor (with 6 digits from smallestUnit)");
+
+const result2 = duration.toString({ fractionalSecondDigits: 6, roundingMode: "floor" });
+assert.sameValue(result2, "P1Y2M3W4DT5H6M7.123987S",
+ "roundingMode is floor (with 6 digits from fractionalSecondDigits)");
+
+const result3 = duration.toString({ smallestUnit: "millisecond", roundingMode: "floor" });
+assert.sameValue(result3, "P1Y2M3W4DT5H6M7.123S",
+ "roundingMode is floor (with 3 digits from smallestUnit)");
+
+const result4 = duration.toString({ fractionalSecondDigits: 3, roundingMode: "floor" });
+assert.sameValue(result4, "P1Y2M3W4DT5H6M7.123S",
+ "roundingMode is floor (with 3 digits from fractionalSecondDigits)");
+
+const result5 = duration.toString({ smallestUnit: "second", roundingMode: "floor" });
+assert.sameValue(result5, "P1Y2M3W4DT5H6M7S",
+ "roundingMode is floor (with 0 digits from smallestUnit)");
+
+const result6 = duration.toString({ fractionalSecondDigits: 0, roundingMode: "floor" });
+assert.sameValue(result6, "P1Y2M3W4DT5H6M7S",
+ "roundingMode is floor (with 0 digits from fractionalSecondDigits)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/roundingmode-halfExpand.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/roundingmode-halfExpand.js
new file mode 100644
index 0000000000..136aaa68bd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/roundingmode-halfExpand.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.duration.prototype.tostring
+description: halfExpand value for roundingMode option
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 123, 987, 500);
+
+const result1 = duration.toString({ smallestUnit: "microsecond", roundingMode: "halfExpand" });
+assert.sameValue(result1, "P1Y2M3W4DT5H6M7.123988S",
+ "roundingMode is halfExpand (with 6 digits from smallestUnit)");
+
+const result2 = duration.toString({ fractionalSecondDigits: 6, roundingMode: "halfExpand" });
+assert.sameValue(result2, "P1Y2M3W4DT5H6M7.123988S",
+ "roundingMode is halfExpand (with 6 digits from fractionalSecondDigits)");
+
+const result3 = duration.toString({ smallestUnit: "millisecond", roundingMode: "halfExpand" });
+assert.sameValue(result3, "P1Y2M3W4DT5H6M7.124S",
+ "roundingMode is halfExpand (with 3 digits from smallestUnit)");
+
+const result4 = duration.toString({ fractionalSecondDigits: 3, roundingMode: "halfExpand" });
+assert.sameValue(result4, "P1Y2M3W4DT5H6M7.124S",
+ "roundingMode is halfExpand (with 3 digits from fractionalSecondDigits)");
+
+const result5 = duration.toString({ smallestUnit: "second", roundingMode: "halfExpand" });
+assert.sameValue(result5, "P1Y2M3W4DT5H6M7S",
+ "roundingMode is halfExpand (with 0 digits from smallestUnit)");
+
+const result6 = duration.toString({ fractionalSecondDigits: 0, roundingMode: "halfExpand" });
+assert.sameValue(result6, "P1Y2M3W4DT5H6M7S",
+ "roundingMode is halfExpand (with 0 digits from fractionalSecondDigits)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/roundingmode-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/roundingmode-invalid-string.js
new file mode 100644
index 0000000000..580537e589
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/roundingmode-invalid-string.js
@@ -0,0 +1,16 @@
+// |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.duration.prototype.tostring
+description: RangeError thrown when roundingMode option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(0, 0, 0, 0, 12, 34, 56, 123, 987, 500);
+for (const roundingMode of ["other string", "cile", "CEIL", "ce\u0131l", "auto", "halfexpand", "floor\0"]) {
+ assert.throws(RangeError, () => duration.toString({ smallestUnit: "microsecond", roundingMode }));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/roundingmode-trunc.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/roundingmode-trunc.js
new file mode 100644
index 0000000000..8a7d8888f7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/roundingmode-trunc.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.duration.prototype.tostring
+description: trunc value for roundingMode option
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 123, 987, 500);
+
+const result1 = duration.toString({ smallestUnit: "microsecond", roundingMode: "trunc" });
+assert.sameValue(result1, "P1Y2M3W4DT5H6M7.123987S",
+ "roundingMode is trunc (with 6 digits from smallestUnit)");
+
+const result2 = duration.toString({ fractionalSecondDigits: 6, roundingMode: "trunc" });
+assert.sameValue(result2, "P1Y2M3W4DT5H6M7.123987S",
+ "roundingMode is trunc (with 6 digits from fractionalSecondDigits)");
+
+const result3 = duration.toString({ smallestUnit: "millisecond", roundingMode: "trunc" });
+assert.sameValue(result3, "P1Y2M3W4DT5H6M7.123S",
+ "roundingMode is trunc (with 3 digits from smallestUnit)");
+
+const result4 = duration.toString({ fractionalSecondDigits: 3, roundingMode: "trunc" });
+assert.sameValue(result4, "P1Y2M3W4DT5H6M7.123S",
+ "roundingMode is trunc (with 3 digits from fractionalSecondDigits)");
+
+const result5 = duration.toString({ smallestUnit: "second", roundingMode: "trunc" });
+assert.sameValue(result5, "P1Y2M3W4DT5H6M7S",
+ "roundingMode is trunc (with 0 digits from smallestUnit)");
+
+const result6 = duration.toString({ fractionalSecondDigits: 0, roundingMode: "trunc" });
+assert.sameValue(result6, "P1Y2M3W4DT5H6M7S",
+ "roundingMode is trunc (with 0 digits from fractionalSecondDigits)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/roundingmode-undefined.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/roundingmode-undefined.js
new file mode 100644
index 0000000000..6f50ba7f61
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/roundingmode-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.duration.prototype.tostring
+description: Fallback value for roundingMode option
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(0, 0, 0, 0, 12, 34, 56, 123, 987, 500);
+
+const explicit1 = duration.toString({ smallestUnit: "microsecond", roundingMode: undefined });
+assert.sameValue(explicit1, "PT12H34M56.123987S", "default roundingMode is trunc");
+const implicit1 = duration.toString({ smallestUnit: "microsecond" });
+assert.sameValue(implicit1, "PT12H34M56.123987S", "default roundingMode is trunc");
+
+const explicit2 = duration.toString({ smallestUnit: "millisecond", roundingMode: undefined });
+assert.sameValue(explicit2, "PT12H34M56.123S", "default roundingMode is trunc");
+const implicit2 = duration.toString({ smallestUnit: "millisecond" });
+assert.sameValue(implicit2, "PT12H34M56.123S", "default roundingMode is trunc");
+
+const explicit3 = duration.toString({ smallestUnit: "second", roundingMode: undefined });
+assert.sameValue(explicit3, "PT12H34M56S", "default roundingMode is trunc");
+const implicit3 = duration.toString({ smallestUnit: "second" });
+assert.sameValue(implicit3, "PT12H34M56S", "default roundingMode is trunc");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/roundingmode-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/roundingmode-wrong-type.js
new file mode 100644
index 0000000000..d8a1c7959f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/roundingmode-wrong-type.js
@@ -0,0 +1,18 @@
+// |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.duration.prototype.tostring
+description: Type conversions for roundingMode option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(0, 0, 0, 0, 12, 34, 56, 123, 987, 500);
+TemporalHelpers.checkStringOptionWrongType("roundingMode", "trunc",
+ (roundingMode) => duration.toString({ smallestUnit: "microsecond", roundingMode }),
+ (result, descr) => assert.sameValue(result, "PT12H34M56.123987S", descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/shell.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/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/Duration/prototype/toString/smallestunit-fractionalseconddigits.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/smallestunit-fractionalseconddigits.js
new file mode 100644
index 0000000000..ea7f22c7db
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/smallestunit-fractionalseconddigits.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.duration.prototype.tostring
+description: fractionalSecondDigits option is not used with smallestUnit present
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 789, 999, 999);
+const tests = [
+ ["second", "P1Y2M3W4DT5H6M7S"],
+ ["millisecond", "P1Y2M3W4DT5H6M7.789S"],
+ ["microsecond", "P1Y2M3W4DT5H6M7.789999S"],
+ ["nanosecond", "P1Y2M3W4DT5H6M7.789999999S"],
+];
+
+for (const [smallestUnit, expected] of tests) {
+ const string = duration.toString({
+ smallestUnit,
+ fractionalSecondDigits: 5,
+ });
+ assert.sameValue(string, expected, `smallestUnit: "${smallestUnit}" overrides fractionalSecondDigits`);
+}
+
+assert.throws(RangeError, () => duration.toString({
+ smallestUnit: "hour",
+ fractionalSecondDigits: 5,
+}), "hour is an invalid smallestUnit but still overrides fractionalSecondDigits");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/smallestunit-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/smallestunit-invalid-string.js
new file mode 100644
index 0000000000..8e5c64764b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/smallestunit-invalid-string.js
@@ -0,0 +1,42 @@
+// |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.duration.prototype.tostring
+description: RangeError thrown when smallestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(0, 0, 0, 0, 12, 34, 56, 123, 987, 500);
+const badValues = [
+ "era",
+ "eraYear",
+ "year",
+ "month",
+ "week",
+ "day",
+ "hour",
+ "minute",
+ "millisecond\0",
+ "mill\u0131second",
+ "SECOND",
+ "eras",
+ "eraYears",
+ "years",
+ "months",
+ "weeks",
+ "days",
+ "hours",
+ "minutes",
+ "milliseconds\0",
+ "mill\u0131seconds",
+ "SECONDS",
+ "other string",
+];
+for (const smallestUnit of badValues) {
+ assert.throws(RangeError, () => duration.toString({ smallestUnit }),
+ `"${smallestUnit}" is not a valid value for smallest unit`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/smallestunit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/smallestunit-plurals-accepted.js
new file mode 100644
index 0000000000..02ffb40098
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/smallestunit-plurals-accepted.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.duration.prototype.tostring
+description: Plural units are accepted as well for the smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+const validUnits = [
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+];
+TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => duration.toString({ smallestUnit }), validUnits);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/smallestunit-undefined.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/smallestunit-undefined.js
new file mode 100644
index 0000000000..542b1481ce
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/smallestunit-undefined.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.duration.prototype.tostring
+description: Fallback value for smallestUnit option
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(0, 0, 0, 0, 12, 34, 56, 123, 987, 500);
+
+const explicit1 = duration.toString({ smallestUnit: undefined, fractionalSecondDigits: 6 });
+assert.sameValue(explicit1, "PT12H34M56.123987S", "default smallestUnit defers to fractionalSecondDigits");
+const implicit1 = duration.toString({ fractionalSecondDigits: 6 });
+assert.sameValue(implicit1, "PT12H34M56.123987S", "default smallestUnit defers to fractionalSecondDigits");
+
+const explicit2 = duration.toString({ smallestUnit: undefined, fractionalSecondDigits: 3 });
+assert.sameValue(explicit2, "PT12H34M56.123S", "default smallestUnit defers to fractionalSecondDigits");
+const implicit2 = duration.toString({ fractionalSecondDigits: 3 });
+assert.sameValue(implicit2, "PT12H34M56.123S", "default smallestUnit defers to fractionalSecondDigits");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/smallestunit-valid-units.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/smallestunit-valid-units.js
new file mode 100644
index 0000000000..ed559b86b6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/smallestunit-valid-units.js
@@ -0,0 +1,57 @@
+// |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.duration.prototype.tostring
+description: Valid units for the smallestUnit option
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+
+function test(instance, expectations, description) {
+ for (const [smallestUnit, expectedResult] of expectations) {
+ assert.sameValue(instance.toString({ smallestUnit }), expectedResult,
+ `${description} with smallestUnit "${smallestUnit}"`);
+ }
+}
+
+test(
+ duration,
+ [
+ ["seconds", "P1Y2M3W4DT5H6M7S"],
+ ["milliseconds", "P1Y2M3W4DT5H6M7.987S"],
+ ["microseconds", "P1Y2M3W4DT5H6M7.987654S"],
+ ["nanoseconds", "P1Y2M3W4DT5H6M7.987654321S"],
+ ],
+ "subseconds toString"
+);
+
+test(
+ new Temporal.Duration(1, 2, 3, 4, 5, 6, 7),
+ [
+ ["seconds", "P1Y2M3W4DT5H6M7S"],
+ ["milliseconds", "P1Y2M3W4DT5H6M7.000S"],
+ ["microseconds", "P1Y2M3W4DT5H6M7.000000S"],
+ ["nanoseconds", "P1Y2M3W4DT5H6M7.000000000S"],
+ ],
+ "whole seconds toString"
+);
+
+const notValid = [
+ "era",
+ "year",
+ "month",
+ "week",
+ "day",
+ "hour",
+ "minute",
+];
+
+notValid.forEach((smallestUnit) => {
+ assert.throws(RangeError, () => duration.toString({ smallestUnit }),
+ `"${smallestUnit}" is not a valid unit for the smallestUnit option`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/smallestunit-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/smallestunit-wrong-type.js
new file mode 100644
index 0000000000..6c52f8e529
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/smallestunit-wrong-type.js
@@ -0,0 +1,18 @@
+// |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.duration.prototype.tostring
+description: Type conversions for smallestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(0, 0, 0, 0, 12, 34, 56, 123, 987, 500);
+TemporalHelpers.checkStringOptionWrongType("smallestUnit", "microsecond",
+ (smallestUnit) => duration.toString({ smallestUnit }),
+ (result, descr) => assert.sameValue(result, "PT12H34M56.123987S", descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/throws-when-rounded-duration-is-invalid.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/throws-when-rounded-duration-is-invalid.js
new file mode 100644
index 0000000000..607f2aa203
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/throws-when-rounded-duration-is-invalid.js
@@ -0,0 +1,48 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.tostring
+description: >
+ RoundDuration throws when the rounded duration can't be represented using
+ float64-representable integers.
+info: |
+ Temporal.Duration.prototype.toString ( [ options ] )
+
+ ...
+ 7. Let result be (? RoundDuration(...)).[[DurationRecord]].
+ ...
+
+ RoundDuration ( years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds,
+ nanoseconds, increment, unit, roundingMode [ , relativeTo ] )
+
+ ...
+ 15. Else if unit is "second", then
+ a. Set seconds to RoundNumberToIncrement(fractionalSeconds, increment, roundingMode).
+ b. Set remainder to fractionalSeconds - seconds.
+ c. Set milliseconds, microseconds, and nanoseconds to 0.
+ ...
+ 19. Let duration be ? CreateDurationRecord(years, months, weeks, days, hours, minutes, seconds,
+ milliseconds, microseconds, nanoseconds).
+ ...
+
+ CreateDurationRecord ( years, months, weeks, days, hours, minutes, seconds, milliseconds,
+ microseconds, nanoseconds )
+
+ 1. If ! IsValidDuration(years, months, weeks, days, hours, minutes, seconds, milliseconds,
+ microseconds, nanoseconds) is false, throw a RangeError exception.
+ ...
+features: [Temporal]
+---*/
+
+var duration = Temporal.Duration.from({
+ seconds: Number.MAX_SAFE_INTEGER,
+ milliseconds: 999,
+});
+
+var options = {smallestUnit: "seconds", roundingMode: "ceil"};
+
+assert.throws(RangeError, () => duration.toString(options));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toStringTag/browser.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toStringTag/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toStringTag/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toStringTag/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toStringTag/prop-desc.js
new file mode 100644
index 0000000000..c8f4fb9600
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toStringTag/prop-desc.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.duration.prototype-@@tostringtag
+description: The @@toStringTag property of Temporal.Duration
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Duration.prototype, Symbol.toStringTag, {
+ value: "Temporal.Duration",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toStringTag/shell.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toStringTag/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toStringTag/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/balance-negative-result.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/balance-negative-result.js
new file mode 100644
index 0000000000..f4781725dd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/balance-negative-result.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.duration.prototype.total
+description: A negative duration result is balanced correctly by the modulo operation in NanosecondsToDays
+info: |
+ sec-temporal-nanosecondstodays step 6:
+ 6. If Type(_relativeTo_) is not Object or _relativeTo_ does not have an [[InitializedTemporalZonedDateTime]] internal slot, then
+ a. Return the new Record { ..., [[Nanoseconds]]: abs(_nanoseconds_) modulo _dayLengthNs_ × _sign_, ... }.
+ sec-temporal-balanceduration step 4:
+ 4. If _largestUnit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*, then
+ a. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _relativeTo_).
+ sec-temporal.duration.prototype.round step 9:
+ 9. Let _balanceResult_ be ? BalanceDuration(_unbalanceResult_.[[Days]], _unbalanceResult_.[[Hours]], _unbalanceResult_.[[Minutes]], _unbalanceResult_.[[Seconds]], _unbalanceResult_.[[Milliseconds]], _unbalanceResult_.[[Microseconds]], _unbalanceResult_.[[Nanoseconds]], _unit_, _intermediate_).
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(0, 0, 0, 0, -60);
+const result = duration.total({ unit: "days" });
+assert.sameValue(result, -2.5);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/balance-subseconds.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/balance-subseconds.js
new file mode 100644
index 0000000000..9e37051c5d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/balance-subseconds.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.duration.prototype.total
+description: Balancing from subsecond units to seconds happens correctly
+features: [Temporal]
+---*/
+
+const pos = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 999, 999999, 999999999);
+assert.sameValue(pos.total("seconds"), 2.998998999);
+
+const neg = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, -999, -999999, -999999999);
+assert.sameValue(neg.total("seconds"), -2.998998999);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/branding.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/branding.js
new file mode 100644
index 0000000000..0beb2f811a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/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.duration.prototype.total
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const total = Temporal.Duration.prototype.total;
+
+assert.sameValue(typeof total, "function");
+
+const args = ["hour"];
+
+assert.throws(TypeError, () => total.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => total.apply(null, args), "null");
+assert.throws(TypeError, () => total.apply(true, args), "true");
+assert.throws(TypeError, () => total.apply("", args), "empty string");
+assert.throws(TypeError, () => total.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => total.apply(1, args), "1");
+assert.throws(TypeError, () => total.apply({}, args), "plain object");
+assert.throws(TypeError, () => total.apply(Temporal.Duration, args), "Temporal.Duration");
+assert.throws(TypeError, () => total.apply(Temporal.Duration.prototype, args), "Temporal.Duration.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/browser.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/builtin.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/builtin.js
new file mode 100644
index 0000000000..ca011bad96
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/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.duration.prototype.total
+description: >
+ Tests that Temporal.Duration.prototype.total
+ 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.Duration.prototype.total),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Duration.prototype.total),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Duration.prototype.total),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Duration.prototype.total.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/calendar-dateadd-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/calendar-dateadd-called-with-options-undefined.js
new file mode 100644
index 0000000000..7185da66e6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/calendar-dateadd-called-with-options-undefined.js
@@ -0,0 +1,43 @@
+// |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.duration.prototype.total
+description: >
+ BuiltinTimeZoneGetInstantFor calls Calendar.dateAdd with undefined as the
+ options value
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarDateAddUndefinedOptions();
+const timeZone = TemporalHelpers.oneShiftTimeZone(new Temporal.Instant(0n), 3600e9);
+const relativeTo = new Temporal.ZonedDateTime(0n, timeZone, calendar);
+
+// Total of a calendar unit where larger calendar units have to be converted
+// down, to cover the path that goes through UnbalanceDateDurationRelative
+// The calls come from the path:
+// Duration.total() -> UnbalanceDateDurationRelative -> calendar.dateAdd()
+
+const instance1 = new Temporal.Duration(1, 1, 1, 1, 1);
+instance1.total({ unit: "days", relativeTo });
+assert.sameValue(calendar.dateAddCallCount, 1, "converting larger calendar units down");
+
+// Total of a calendar unit where smaller calendar units have to be converted
+// up, to cover the path that goes through MoveRelativeZonedDateTime
+// The calls come from these paths:
+// Duration.total() ->
+// MoveRelativeZonedDateTime -> AddZonedDateTime -> BuiltinTimeZoneGetInstantFor -> calendar.dateAdd()
+// BalanceDuration ->
+// AddZonedDateTime -> BuiltinTimeZoneGetInstantFor -> calendar.dateAdd()
+// RoundDuration ->
+// MoveRelativeDate -> calendar.dateAdd()
+
+calendar.dateAddCallCount = 0;
+
+const instance2 = new Temporal.Duration(0, 0, 1, 1);
+instance2.total({ unit: "weeks", relativeTo });
+assert.sameValue(calendar.dateAddCallCount, 3, "converting smaller calendar units up");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/calendar-dateadd-called-with-plaindate-instance.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/calendar-dateadd-called-with-plaindate-instance.js
new file mode 100644
index 0000000000..0afdda3671
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/calendar-dateadd-called-with-plaindate-instance.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.duration.prototype.total
+description: >
+ relativeTo parameters that are not ZonedDateTime or undefined, are always
+ converted to PlainDate for observable calendar calls
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarDateAddPlainDateInstance();
+const instance = new Temporal.Duration(1, 1, 1, 1);
+const relativeTo = new Temporal.PlainDate(2000, 1, 1, calendar);
+calendar.specificPlainDate = relativeTo;
+instance.total({ unit: "days", relativeTo });
+assert(calendar.dateAddCallCount > 0, "assertions in calendar.dateAdd() should have been tested");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/calendar-dateuntil-called-with-singular-largestunit.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/calendar-dateuntil-called-with-singular-largestunit.js
new file mode 100644
index 0000000000..0485054ae0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/calendar-dateuntil-called-with-singular-largestunit.js
@@ -0,0 +1,68 @@
+// |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.duration.prototype.total
+description: The options object passed to calendar.dateUntil has a largestUnit property with its value in the singular form
+info: |
+ sec-temporal.duration.prototype.total steps 7–11:
+ 7. Let _unbalanceResult_ be ? UnbalanceDateDurationRelative(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _unit_, _relativeTo_).
+ ...
+ 10. Let _balanceResult_ be ? BalanceDuration(_unbalanceResult_.[[Days]], _unbalanceResult_.[[Hours]], _unbalanceResult_.[[Minutes]], _unbalanceResult_.[[Seconds]], _unbalanceResult_.[[Milliseconds]], _unbalanceResult_.[[Microseconds]], _unbalanceResult_.[[Nanoseconds]], _unit_, _intermediate_).
+ 11. Let _roundResult_ be ? RoundDuration(_unbalanceResult_.[[Years]], _unbalanceResult_.[[Months]], _unbalanceResult_.[[Weeks]], _balanceResult_.[[Days]], _balanceResult_.[[Hours]], _balanceResult_.[[Minutes]], _balanceResult_.[[Seconds]], _balanceResult_.[[Milliseconds]], _balanceResult_.[[Microseconds]], _balanceResult_.[[Nanoseconds]], 1, _unit_, *"trunc"*, _relativeTo_).
+ sec-temporal-unbalancedatedurationrelative step 3:
+ 3. If _largestUnit_ is *"month"*, then
+ ...
+ g. Let _untilOptions_ be ! OrdinaryObjectCreate(*null*).
+ h. Perform ! CreateDataPropertyOrThrow(_untilOptions_, *"largestUnit"*, *"month"*).
+ i. Let _untilResult_ be ? CalendarDateUntil(_calendarRec_.[[Receiver]], _plainRelativeTo_, _later_, _untilOptions_, _calendarRec_.[[DateUntil]]).
+ sec-temporal-balanceduration step 3.a:
+ 3. If _largestUnit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*, then
+ a. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _relativeTo_).
+ sec-temporal-roundduration steps 5.d and 8.n–p:
+ 5. If _unit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*, then
+ ...
+ d. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _intermediate_).
+ ...
+ 8. If _unit_ is *"year"*, then
+ ...
+ n. Let _untilOptions_ be ! OrdinaryObjectCreate(*null*).
+ o. Perform ! CreateDataPropertyOrThrow(_untilOptions_, *"largestUnit"*, *"year"*).
+ p. Let _timePassed_ be ? CalendarDateUntil(_calendar_, _relativeTo_, _daysLater_, _untilOptions_)
+ sec-temporal-nanosecondstodays step 11:
+ 11. 1. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _relativeTo_.[[Calendar]], *"day"*).
+ sec-temporal-differenceisodatetime steps 9–11:
+ 9. Let _dateLargestUnit_ be ! LargerOfTwoTemporalUnits(*"day"*, _largestUnit_).
+ 10. Let _untilOptions_ be ? MergeLargestUnitOption(_options_, _dateLargestUnit_).
+ 11. Let _dateDifference_ be ? CalendarDateUntil(_calendar_, _date1_, _date2_, _untilOptions_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+// Check the paths that go through NanosecondsToDays: only one call in
+// RoundDuration when the unit is a calendar unit. The others all
+// have largestUnit: "day" so the difference is taken in ISO calendar space.
+
+const duration = new Temporal.Duration(0, 1, 1, 1, 1, 1, 1, 1, 1, 1);
+
+TemporalHelpers.checkCalendarDateUntilLargestUnitSingular(
+ (calendar, unit) => {
+ const relativeTo = new Temporal.ZonedDateTime(0n, "UTC", calendar);
+ duration.total({ unit, relativeTo });
+ },
+ {
+ years: ["year"],
+ months: ["month"],
+ weeks: ["week"],
+ days: [],
+ hours: [],
+ minutes: [],
+ seconds: [],
+ milliseconds: [],
+ microseconds: [],
+ nanoseconds: []
+ }
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/calendar-fields-iterable.js
new file mode 100644
index 0000000000..347d9a6a76
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/calendar-fields-iterable.js
@@ -0,0 +1,34 @@
+// |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.duration.prototype.total
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.duration.prototype.total step 4:
+ 4. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_).
+ sec-temporal-torelativetemporalobject step 4.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+const duration = new Temporal.Duration(1, 1, 1, 1, 1, 1, 1);
+duration.total({ unit: 'seconds', relativeTo: { year: 2000, month: 1, day: 1, calendar } });
+
+assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/calendar-possibly-required.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/calendar-possibly-required.js
new file mode 100644
index 0000000000..154980346c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/calendar-possibly-required.js
@@ -0,0 +1,49 @@
+// |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.duration.prototype.total
+description: Calendar required when days = 0 but years/months/weeks non-zero
+features: [Temporal]
+---*/
+
+const yearInstance = new Temporal.Duration(1999);
+const monthInstance = new Temporal.Duration(0, 49);
+const weekInstance = new Temporal.Duration(0, 0, 1);
+const dayInstance = new Temporal.Duration(0, 0, 0, 42);
+
+let relativeTo = new Temporal.PlainDate(2021, 12, 15);
+
+assert.throws(
+ RangeError,
+ () => { yearInstance.total({ unit: "days" }); },
+ "total a Duration with non-zero years fails without largest/smallest unit"
+);
+const yearResult = yearInstance.total({ unit: "days", relativeTo });
+assert.sameValue(yearResult, 730120, "year duration contains proper days");
+
+assert.throws(
+ RangeError,
+ () => { monthInstance.total({ unit: "days" }); },
+ "total a Duration with non-zero month fails without largest/smallest unit"
+);
+
+const monthResult = monthInstance.total({ unit: "days", relativeTo });
+assert.sameValue(monthResult, 1492, "month duration contains proper days");
+
+assert.throws(
+ RangeError,
+ () => { weekInstance.total({ unit: "days" }); },
+ "total a Duration with non-zero weeks fails without largest/smallest unit"
+);
+
+const weekResult = weekInstance.total({ unit: "days", relativeTo });
+assert.sameValue(weekResult, 7, "week duration contains proper days");
+
+const dayResultWithoutRelative = dayInstance.total({ unit: "days" });
+const dayResultWithRelative = dayInstance.total({ unit: "days", relativeTo });
+assert.sameValue(dayResultWithoutRelative, 42, "day duration without relative-to part contains proper days");
+assert.sameValue(dayResultWithRelative, 42, "day duration with relative-to part contains proper days");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/calendar-temporal-object.js
new file mode 100644
index 0000000000..12b0b55148
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/calendar-temporal-object.js
@@ -0,0 +1,29 @@
+// |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.duration.prototype.total
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.duration.prototype.total step 4:
+ 4. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_).
+ sec-temporal-torelativetemporalobject step 4.b:
+ b. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const duration = new Temporal.Duration(1, 1, 1, 1, 1, 1, 1);
+ duration.total({ unit: 'seconds', relativeTo: { year: 2000, month: 1, day: 1, calendar: temporalObject } });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..4caf54a64c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/constructor-in-calendar-fields.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.duration.prototype.total
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const relativeTo = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar, timeZone: 'Europe/Paris' };
+const instance = new Temporal.Duration(1, 0, 0, 0, 24);
+
+assert.throws(RangeError, () => instance.total({ unit: "days", relativeTo }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/dateuntil-field.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/dateuntil-field.js
new file mode 100644
index 0000000000..4ac47adda0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/dateuntil-field.js
@@ -0,0 +1,37 @@
+// |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.duration.prototype.total
+description: >
+ When consulting calendar.dateUntil() to calculate the number of months in a
+ year, the months property is not accessed on the result Duration
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+
+class CalendarDateUntilObservable extends Temporal.Calendar {
+ dateUntil(...args) {
+ actual.push("call dateUntil");
+ const returnValue = super.dateUntil(...args);
+ TemporalHelpers.observeProperty(actual, returnValue, "months", Infinity);
+ return returnValue;
+ }
+}
+
+const calendar = new CalendarDateUntilObservable("iso8601");
+const relativeTo = new Temporal.PlainDate(2018, 10, 12, calendar);
+
+const expected = [
+ "call dateUntil",
+];
+
+const years = new Temporal.Duration(2);
+const result = years.total({ unit: "months", relativeTo });
+assert.sameValue(result, 24, "result");
+assert.compareArray(actual, expected, "operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/duplicate-calendar-fields.js
new file mode 100644
index 0000000000..516d4f56e8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/duplicate-calendar-fields.js
@@ -0,0 +1,19 @@
+// |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.duration.prototype.total
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['day'], ['month'], ['monthCode'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const relativeTo = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar, timeZone: 'Europe/Paris' };
+ const instance = new Temporal.Duration(1, 0, 0, 0, 24);
+
+ assert.throws(RangeError, () => instance.total({ unit: "days", relativeTo }));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/duration-out-of-range-added-to-relativeto.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/duration-out-of-range-added-to-relativeto.js
new file mode 100644
index 0000000000..2abd469541
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/duration-out-of-range-added-to-relativeto.js
@@ -0,0 +1,28 @@
+// |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.duration.prototype.total
+description: RangeError thrown when calendar part of duration added to relativeTo is out of range
+features: [Temporal]
+info: |
+ RoundDuration:
+ 8.k. Let _isoResult_ be ! AddISODate(_plainRelativeTo_.[[ISOYear]]. _plainRelativeTo_.[[ISOMonth]], _plainRelativeTo_.[[ISODay]], 0, 0, 0, truncate(_fractionalDays_), *"constrain"*).
+ l. Let _wholeDaysLater_ be ? CreateTemporalDate(_isoResult_.[[Year]], _isoResult_.[[Month]], _isoResult_.[[Day]], _calendar_).
+---*/
+
+// Based on a test case by André Bargull <andre.bargull@gmail.com>
+
+const instance = new Temporal.Duration(0, 0, 0, /* days = */ 500_000_000);
+const relativeTo = new Temporal.PlainDate(2000, 1, 1);
+assert.throws(RangeError, () => instance.total({relativeTo, unit: "years"}));
+assert.throws(RangeError, () => instance.total({relativeTo, unit: "months"}));
+assert.throws(RangeError, () => instance.total({relativeTo, unit: "weeks"}));
+
+const negInstance = new Temporal.Duration(0, 0, 0, /* days = */ -500_000_000);
+assert.throws(RangeError, () => negInstance.total({relativeTo, unit: "years"}));
+assert.throws(RangeError, () => negInstance.total({relativeTo, unit: "months"}));
+assert.throws(RangeError, () => negInstance.total({relativeTo, unit: "weeks"}));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/length.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/length.js
new file mode 100644
index 0000000000..e805ba32bc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/length.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.duration.prototype.total
+description: Temporal.Duration.prototype.total.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.Duration.prototype.total, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/name.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/name.js
new file mode 100644
index 0000000000..9356d71761
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/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.duration.prototype.total
+description: Temporal.Duration.prototype.total.name is "total".
+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.Duration.prototype.total, "name", {
+ value: "total",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/normalized-time-duration-to-days-loop-arbitrarily.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/normalized-time-duration-to-days-loop-arbitrarily.js
new file mode 100644
index 0000000000..72260abd68
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/normalized-time-duration-to-days-loop-arbitrarily.js
@@ -0,0 +1,80 @@
+// |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.duration.prototype.total
+description: >
+ NormalizedTimeDurationToDays can loop arbitrarily up to max safe integer
+info: |
+ NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ , precalculatedPlainDatetime ] )
+ ...
+ 21. Repeat, while done is false,
+ a. Let oneDayFarther be ? AddDaysToZonedDateTime(relativeResult.[[Instant]],
+ relativeResult.[[DateTime]], timeZoneRec, zonedRelativeTo.[[Calendar]], sign).
+ b. Set dayLengthNs to NormalizedTimeDurationFromEpochNanosecondsDifference(oneDayFarther.[[EpochNanoseconds]],
+ relativeResult.[[EpochNanoseconds]]).
+ c. Let oneDayLess be ? SubtractNormalizedTimeDuration(norm, dayLengthNs).
+ c. If NormalizedTimeDurationSign(oneDayLess) × sign ≥ 0, then
+ i. Set norm to oneDayLess.
+ ii. Set relativeResult to oneDayFarther.
+ iii. Set days to days + sign.
+ d. Else,
+ i. Set done to true.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calls = [];
+const duration = Temporal.Duration.from({ days: 1 });
+
+function createRelativeTo(count) {
+ const dayLengthNs = 86400000000000n;
+ const dayInstant = new Temporal.Instant(dayLengthNs);
+ const substitutions = [];
+ const timeZone = new Temporal.TimeZone("UTC");
+ // Return constant value for first _count_ calls
+ TemporalHelpers.substituteMethod(
+ timeZone,
+ "getPossibleInstantsFor",
+ substitutions
+ );
+ substitutions.length = count;
+ let i = 0;
+ for (i = 0; i < substitutions.length; i++) {
+ // (this value)
+ substitutions[i] = [dayInstant];
+ }
+ // Record calls in calls[]
+ TemporalHelpers.observeMethod(calls, timeZone, "getPossibleInstantsFor");
+ return new Temporal.ZonedDateTime(0n, timeZone);
+}
+
+let zdt = createRelativeTo(50);
+calls.splice(0); // Reset calls list after ZonedDateTime construction
+duration.total({
+ unit: "day",
+ relativeTo: zdt,
+});
+assert.sameValue(
+ calls.length,
+ 50 + 2,
+ "Expected duration.total to call getPossibleInstantsFor correct number of times"
+);
+
+zdt = createRelativeTo(100);
+calls.splice(0); // Reset calls list after previous loop + ZonedDateTime construction
+duration.total({
+ unit: "day",
+ relativeTo: zdt,
+});
+assert.sameValue(
+ calls.length,
+ 100 + 2,
+ "Expected duration.total to call getPossibleInstantsFor correct number of times"
+);
+
+zdt = createRelativeTo(106);
+assert.throws(RangeError, () => duration.total({ unit: "day", relativeTo: zdt }), "106-1 days > 2⁵³ ns");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/not-a-constructor.js
new file mode 100644
index 0000000000..193622f8c9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/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.duration.prototype.total
+description: >
+ Temporal.Duration.prototype.total 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.Duration.prototype.total();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Duration.prototype.total), false,
+ "isConstructor(Temporal.Duration.prototype.total)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/options-wrong-type.js
new file mode 100644
index 0000000000..3f0e77111a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/options-wrong-type.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.duration.prototype.total
+description: TypeError thrown when options argument is missing or a non-string primitive
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const badOptions = [
+ undefined,
+ null,
+ true,
+ Symbol(),
+ 1,
+ 2n,
+];
+
+const instance = new Temporal.Duration(0, 0, 0, 0, 1);
+assert.throws(TypeError, () => instance.total(), "TypeError on missing options argument");
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.total(value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/order-of-operations.js
new file mode 100644
index 0000000000..24262e35f8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/order-of-operations.js
@@ -0,0 +1,396 @@
+// |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.duration.prototype.total
+description: Properties on objects passed to total() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "get options.relativeTo",
+ "get options.unit",
+ "get options.unit.toString",
+ "call options.unit.toString",
+];
+const actual = [];
+
+function createOptionsObserver({ unit = "nanoseconds", roundingMode = "halfExpand", roundingIncrement = 1, relativeTo = undefined } = {}) {
+ return TemporalHelpers.propertyBagObserver(actual, {
+ unit,
+ roundingMode,
+ roundingIncrement,
+ relativeTo,
+ }, "options");
+}
+
+const instance = new Temporal.Duration(0, 0, 0, 0, 2400);
+
+// basic order of observable operations, with no relativeTo
+instance.total(createOptionsObserver({ unit: "nanoseconds" }));
+assert.compareArray(actual, expected, "order of operations");
+actual.splice(0); // clear
+
+const expectedOpsForPlainRelativeTo = [
+ // ToRelativeTemporalObject
+ "get options.relativeTo",
+ "get options.relativeTo.calendar",
+ "has options.relativeTo.calendar.dateAdd",
+ "has options.relativeTo.calendar.dateFromFields",
+ "has options.relativeTo.calendar.dateUntil",
+ "has options.relativeTo.calendar.day",
+ "has options.relativeTo.calendar.dayOfWeek",
+ "has options.relativeTo.calendar.dayOfYear",
+ "has options.relativeTo.calendar.daysInMonth",
+ "has options.relativeTo.calendar.daysInWeek",
+ "has options.relativeTo.calendar.daysInYear",
+ "has options.relativeTo.calendar.fields",
+ "has options.relativeTo.calendar.id",
+ "has options.relativeTo.calendar.inLeapYear",
+ "has options.relativeTo.calendar.mergeFields",
+ "has options.relativeTo.calendar.month",
+ "has options.relativeTo.calendar.monthCode",
+ "has options.relativeTo.calendar.monthDayFromFields",
+ "has options.relativeTo.calendar.monthsInYear",
+ "has options.relativeTo.calendar.weekOfYear",
+ "has options.relativeTo.calendar.year",
+ "has options.relativeTo.calendar.yearMonthFromFields",
+ "has options.relativeTo.calendar.yearOfWeek",
+ "get options.relativeTo.calendar.dateFromFields",
+ "get options.relativeTo.calendar.fields",
+ "call options.relativeTo.calendar.fields",
+ "get options.relativeTo.day",
+ "get options.relativeTo.day.valueOf",
+ "call options.relativeTo.day.valueOf",
+ "get options.relativeTo.hour",
+ "get options.relativeTo.microsecond",
+ "get options.relativeTo.millisecond",
+ "get options.relativeTo.minute",
+ "get options.relativeTo.month",
+ "get options.relativeTo.month.valueOf",
+ "call options.relativeTo.month.valueOf",
+ "get options.relativeTo.monthCode",
+ "get options.relativeTo.monthCode.toString",
+ "call options.relativeTo.monthCode.toString",
+ "get options.relativeTo.nanosecond",
+ "get options.relativeTo.offset",
+ "get options.relativeTo.second",
+ "get options.relativeTo.timeZone",
+ "get options.relativeTo.year",
+ "get options.relativeTo.year.valueOf",
+ "call options.relativeTo.year.valueOf",
+ "call options.relativeTo.calendar.dateFromFields",
+ // GetTemporalUnit
+ "get options.unit",
+ "get options.unit.toString",
+ "call options.unit.toString",
+ // lookup in Duration.p.total
+ "get options.relativeTo.calendar.dateAdd",
+ "get options.relativeTo.calendar.dateUntil",
+];
+
+const plainRelativeTo = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2001,
+ month: 5,
+ monthCode: "M05",
+ day: 2,
+ calendar: TemporalHelpers.calendarObserver(actual, "options.relativeTo.calendar"),
+}, "options.relativeTo");
+
+// basic order of observable operations, without rounding:
+instance.total(createOptionsObserver({ unit: "nanoseconds", relativeTo: plainRelativeTo }));
+assert.compareArray(actual, expectedOpsForPlainRelativeTo, "order of operations for PlainDate relativeTo");
+actual.splice(0); // clear
+
+// code path through RoundDuration that rounds to the nearest year with minimal calendar calls:
+const expectedOpsForMinimalYearRounding = expectedOpsForPlainRelativeTo.concat([
+ // 12.d and 12.f not called because years, months, weeks are 0
+ "call options.relativeTo.calendar.dateUntil", // 12.n
+ // 12.r not called because years, months, weeks are 0
+ "call options.relativeTo.calendar.dateAdd", // 12.x MoveRelativeDate
+]);
+instance.total(createOptionsObserver({ unit: "years", relativeTo: plainRelativeTo }));
+assert.compareArray(actual, expectedOpsForMinimalYearRounding, "order of operations with years = 0 and unit = years");
+actual.splice(0); // clear
+
+// code path through RoundDuration that rounds to the nearest year:
+const expectedOpsForYearRounding = expectedOpsForPlainRelativeTo.concat([
+ "call options.relativeTo.calendar.dateAdd", // 12.d
+ "call options.relativeTo.calendar.dateAdd", // 12.f
+ "call options.relativeTo.calendar.dateUntil", // 12.n
+ "call options.relativeTo.calendar.dateAdd", // 12.r MoveRelativeDate
+ "call options.relativeTo.calendar.dateAdd", // 12.x MoveRelativeDate
+]);
+const instanceYears = new Temporal.Duration(1, 12, 0, 0, /* hours = */ 2400);
+instanceYears.total(createOptionsObserver({ unit: "years", relativeTo: plainRelativeTo }));
+assert.compareArray(actual, expectedOpsForYearRounding, "order of operations with unit = years");
+actual.splice(0); // clear
+
+// code path through Duration.prototype.total that rounds to the nearest month:
+const expectedOpsForMonthRounding = expectedOpsForPlainRelativeTo.concat([
+ // UnbalanceDateDurationRelative
+ "call options.relativeTo.calendar.dateAdd", // 3.f
+ "call options.relativeTo.calendar.dateUntil", // 3.i
+ // RoundDuration
+ "call options.relativeTo.calendar.dateAdd", // 13.c
+ "call options.relativeTo.calendar.dateAdd", // 13.e
+ "call options.relativeTo.calendar.dateUntil", // 13.m
+ "call options.relativeTo.calendar.dateAdd", // 13.q MoveRelativeDate
+ "call options.relativeTo.calendar.dateAdd", // 13.w MoveRelativeDate
+]);
+const instance2 = new Temporal.Duration(1, 0, 0, 62);
+instance2.total(createOptionsObserver({ unit: "months", relativeTo: plainRelativeTo }));
+assert.compareArray(actual, expectedOpsForMonthRounding, "order of operations with unit = months");
+actual.splice(0); // clear
+
+// code path through Duration.prototype.total that rounds to the nearest week:
+const expectedOpsForWeekRounding = expectedOpsForPlainRelativeTo.concat([
+ // UnbalanceDateDurationRelative
+ "call options.relativeTo.calendar.dateAdd", // 4.e
+ // RoundDuration
+ "call options.relativeTo.calendar.dateUntil", // 14.f
+ "call options.relativeTo.calendar.dateAdd", // 14.j MoveRelativeDate
+ "call options.relativeTo.calendar.dateAdd", // 14.p MoveRelativeDate
+]);
+const instance3 = new Temporal.Duration(1, 1, 0, 15);
+instance3.total(createOptionsObserver({ unit: "weeks", relativeTo: plainRelativeTo }));
+assert.compareArray(actual, expectedOpsForWeekRounding, "order of operations with unit = weeks");
+actual.splice(0); // clear
+
+// code path through UnbalanceDateDurationRelative that rounds to the nearest day:
+const expectedOpsForDayRounding = expectedOpsForPlainRelativeTo.concat([
+ "call options.relativeTo.calendar.dateAdd", // 10
+]);
+const instance4 = new Temporal.Duration(1, 1, 1)
+instance4.total(createOptionsObserver({ unit: "days", relativeTo: plainRelativeTo }));
+assert.compareArray(actual, expectedOpsForDayRounding, "order of operations with unit = days");
+actual.splice(0); // clear
+
+const expectedOpsForZonedRelativeTo = [
+ // ToRelativeTemporalObject
+ "get options.relativeTo",
+ "get options.relativeTo.calendar",
+ "has options.relativeTo.calendar.dateAdd",
+ "has options.relativeTo.calendar.dateFromFields",
+ "has options.relativeTo.calendar.dateUntil",
+ "has options.relativeTo.calendar.day",
+ "has options.relativeTo.calendar.dayOfWeek",
+ "has options.relativeTo.calendar.dayOfYear",
+ "has options.relativeTo.calendar.daysInMonth",
+ "has options.relativeTo.calendar.daysInWeek",
+ "has options.relativeTo.calendar.daysInYear",
+ "has options.relativeTo.calendar.fields",
+ "has options.relativeTo.calendar.id",
+ "has options.relativeTo.calendar.inLeapYear",
+ "has options.relativeTo.calendar.mergeFields",
+ "has options.relativeTo.calendar.month",
+ "has options.relativeTo.calendar.monthCode",
+ "has options.relativeTo.calendar.monthDayFromFields",
+ "has options.relativeTo.calendar.monthsInYear",
+ "has options.relativeTo.calendar.weekOfYear",
+ "has options.relativeTo.calendar.year",
+ "has options.relativeTo.calendar.yearMonthFromFields",
+ "has options.relativeTo.calendar.yearOfWeek",
+ "get options.relativeTo.calendar.dateFromFields",
+ "get options.relativeTo.calendar.fields",
+ "call options.relativeTo.calendar.fields",
+ "get options.relativeTo.day",
+ "get options.relativeTo.day.valueOf",
+ "call options.relativeTo.day.valueOf",
+ "get options.relativeTo.hour",
+ "get options.relativeTo.hour.valueOf",
+ "call options.relativeTo.hour.valueOf",
+ "get options.relativeTo.microsecond",
+ "get options.relativeTo.microsecond.valueOf",
+ "call options.relativeTo.microsecond.valueOf",
+ "get options.relativeTo.millisecond",
+ "get options.relativeTo.millisecond.valueOf",
+ "call options.relativeTo.millisecond.valueOf",
+ "get options.relativeTo.minute",
+ "get options.relativeTo.minute.valueOf",
+ "call options.relativeTo.minute.valueOf",
+ "get options.relativeTo.month",
+ "get options.relativeTo.month.valueOf",
+ "call options.relativeTo.month.valueOf",
+ "get options.relativeTo.monthCode",
+ "get options.relativeTo.monthCode.toString",
+ "call options.relativeTo.monthCode.toString",
+ "get options.relativeTo.nanosecond",
+ "get options.relativeTo.nanosecond.valueOf",
+ "call options.relativeTo.nanosecond.valueOf",
+ "get options.relativeTo.offset",
+ "get options.relativeTo.offset.toString",
+ "call options.relativeTo.offset.toString",
+ "get options.relativeTo.second",
+ "get options.relativeTo.second.valueOf",
+ "call options.relativeTo.second.valueOf",
+ "get options.relativeTo.timeZone",
+ "get options.relativeTo.year",
+ "get options.relativeTo.year.valueOf",
+ "call options.relativeTo.year.valueOf",
+ "call options.relativeTo.calendar.dateFromFields",
+ "has options.relativeTo.timeZone.getOffsetNanosecondsFor",
+ "has options.relativeTo.timeZone.getPossibleInstantsFor",
+ "has options.relativeTo.timeZone.id",
+ "get options.relativeTo.timeZone.getOffsetNanosecondsFor",
+ "get options.relativeTo.timeZone.getPossibleInstantsFor",
+ // InterpretISODateTimeOffset
+ "call options.relativeTo.timeZone.getPossibleInstantsFor",
+ "call options.relativeTo.timeZone.getOffsetNanosecondsFor",
+ // GetTemporalUnit
+ "get options.unit",
+ "get options.unit.toString",
+ "call options.unit.toString",
+];
+
+const zonedRelativeTo = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2001,
+ month: 5,
+ monthCode: "M05",
+ day: 2,
+ hour: 6,
+ minute: 54,
+ second: 32,
+ millisecond: 987,
+ microsecond: 654,
+ nanosecond: 321,
+ offset: "+00:00",
+ calendar: TemporalHelpers.calendarObserver(actual, "options.relativeTo.calendar"),
+ timeZone: TemporalHelpers.timeZoneObserver(actual, "options.relativeTo.timeZone"),
+}, "options.relativeTo");
+
+// basic order of observable operations, without rounding:
+instance.total(createOptionsObserver({ unit: "nanoseconds", relativeTo: zonedRelativeTo }));
+assert.compareArray(actual, expectedOpsForZonedRelativeTo.concat([
+ "get options.relativeTo.calendar.dateAdd",
+ "get options.relativeTo.calendar.dateUntil",
+]), "order of operations for ZonedDateTime relativeTo");
+actual.splice(0); // clear
+
+// code path through RoundDuration that rounds to the nearest year with minimal calendar operations:
+const expectedOpsForMinimalYearRoundingZoned = expectedOpsForZonedRelativeTo.concat([
+ // ToTemporalDate
+ "call options.relativeTo.timeZone.getOffsetNanosecondsFor",
+ // lookup in Duration.p.total
+ "get options.relativeTo.calendar.dateAdd",
+ "get options.relativeTo.calendar.dateUntil",
+ // BalancePossiblyInfiniteDuration → NanosecondsToDays
+ "call options.relativeTo.timeZone.getOffsetNanosecondsFor", // 7. GetPlainDateTimeFor
+ "call options.relativeTo.timeZone.getOffsetNanosecondsFor", // 11. GetPlainDateTimeFor
+ // BalancePossiblyInfiniteDuration → NanosecondsToDays → AddDaysToZonedDateTime
+ "call options.relativeTo.timeZone.getPossibleInstantsFor",
+ // BalancePossiblyInfiniteDuration → NanosecondsToDays → AddDaysToZonedDateTime
+ "call options.relativeTo.timeZone.getPossibleInstantsFor",
+], [
+ // code path through RoundDuration that rounds to the nearest year:
+ // MoveRelativeZonedDateTime → AddDaysToZonedDateTime
+ "call options.relativeTo.timeZone.getPossibleInstantsFor",
+ // 12.d and 12.f not called because years, months, weeks are 0
+ "call options.relativeTo.calendar.dateUntil", // 12.n
+ // 12.r not called because years, months, weeks are 0
+ "call options.relativeTo.calendar.dateAdd", // 12.x MoveRelativeDate
+]);
+instance.total(createOptionsObserver({ unit: "years", relativeTo: zonedRelativeTo }));
+assert.compareArray(
+ actual,
+ expectedOpsForMinimalYearRoundingZoned,
+ "order of operations with years = 0, unit = years and ZonedDateTime relativeTo"
+);
+actual.splice(0); // clear
+
+// code path through RoundDuration that rounds to the nearest year:
+const expectedOpsForYearRoundingZoned = expectedOpsForZonedRelativeTo.concat([
+ // ToTemporalDate
+ "call options.relativeTo.timeZone.getOffsetNanosecondsFor",
+ // lookup in Duration.p.total
+ "get options.relativeTo.calendar.dateAdd",
+ "get options.relativeTo.calendar.dateUntil",
+ // MoveRelativeZonedDateTime → AddZonedDateTime
+ "call options.relativeTo.calendar.dateAdd",
+ "call options.relativeTo.timeZone.getPossibleInstantsFor",
+ // BalancePossiblyInfiniteTimeDurationRelative → NanosecondsToDays
+ "call options.relativeTo.timeZone.getOffsetNanosecondsFor", // 8. GetPlainDateTimeFor
+ "call options.relativeTo.timeZone.getOffsetNanosecondsFor", // 9. GetPlainDateTimeFor
+ // BalancePossiblyInfiniteTimeDurationRelative → NanosecondsToDays → AddDaysToZonedDateTime
+ "call options.relativeTo.timeZone.getPossibleInstantsFor",
+ // BalancePossiblyInfiniteTimeDurationRelative → NanosecondsToDays → AddDaysToZonedDateTime
+ "call options.relativeTo.timeZone.getPossibleInstantsFor",
+ // RoundDuration → MoveRelativeZonedDateTime → AddZonedDateTime
+ "call options.relativeTo.calendar.dateAdd",
+ "call options.relativeTo.timeZone.getPossibleInstantsFor",
+ // RoundDuration
+ "call options.relativeTo.calendar.dateAdd", // 12.d
+ "call options.relativeTo.calendar.dateAdd", // 12.f
+ "call options.relativeTo.calendar.dateUntil", // 12.n
+ "call options.relativeTo.calendar.dateAdd", // 12.r MoveRelativeDate
+ "call options.relativeTo.calendar.dateAdd", // 12.x MoveRelativeDate
+]);
+instanceYears.total(createOptionsObserver({ unit: "years", relativeTo: zonedRelativeTo }));
+assert.compareArray(
+ actual,
+ expectedOpsForYearRoundingZoned,
+ "order of operations with unit = years and ZonedDateTime relativeTo"
+);
+actual.splice(0); // clear
+
+// code path that hits UnbalanceDateDurationRelative and RoundDuration
+const expectedOpsForUnbalanceRound = expectedOpsForZonedRelativeTo.concat([
+ // ToTemporalDate
+ "call options.relativeTo.timeZone.getOffsetNanosecondsFor",
+ // lookup in Duration.p.total
+ "get options.relativeTo.calendar.dateAdd",
+ "get options.relativeTo.calendar.dateUntil",
+ // No user code calls in UnbalanceDateDurationRelative
+ // MoveRelativeZonedDateTime → AddZonedDateTime
+ "call options.relativeTo.calendar.dateAdd",
+ "call options.relativeTo.timeZone.getPossibleInstantsFor", // 13. GetInstantFor
+ // RoundDuration → MoveRelativeZonedDateTime → AddZonedDateTime
+ "call options.relativeTo.calendar.dateAdd",
+ "call options.relativeTo.timeZone.getPossibleInstantsFor", // 13. GetInstantFor
+ // RoundDuration
+ "call options.relativeTo.calendar.dateAdd", // 13.c
+ "call options.relativeTo.calendar.dateAdd", // 13.e
+ "call options.relativeTo.calendar.dateUntil", // 13.m
+ "call options.relativeTo.calendar.dateAdd", // 13.w MoveRelativeDate
+]);
+new Temporal.Duration(0, 1, 1).total(createOptionsObserver({ unit: "months", relativeTo: zonedRelativeTo }));
+assert.compareArray(
+ actual,
+ expectedOpsForUnbalanceRound,
+ "order of operations with unit = months and ZonedDateTime relativeTo"
+);
+actual.splice(0); // clear
+
+// code path that avoids converting Zoned twice in BalanceTimeDurationRelative
+const expectedOpsForBalanceRound = expectedOpsForZonedRelativeTo.concat([
+ // ToTemporalDate
+ "call options.relativeTo.timeZone.getOffsetNanosecondsFor",
+ // lookup in Duration.p.total
+ "get options.relativeTo.calendar.dateAdd",
+ "get options.relativeTo.calendar.dateUntil",
+ // No user code calls in UnbalanceDateDurationRelative
+ // No user code calls in AddZonedDateTime (years, months, weeks = 0)
+ // BalanceTimeDurationRelative
+ "call options.relativeTo.timeZone.getOffsetNanosecondsFor", // 4.a
+ "call options.relativeTo.timeZone.getPossibleInstantsFor", // 4.b
+ "call options.relativeTo.timeZone.getOffsetNanosecondsFor", // NanosecondsToDays 9
+ "call options.relativeTo.timeZone.getPossibleInstantsFor", // NanosecondsToDays 26
+ "call options.relativeTo.timeZone.getPossibleInstantsFor", // NanosecondsToDays 31.a
+ // RoundDuration → MoveRelativeZonedDateTime → AddZonedDateTime
+ "call options.relativeTo.timeZone.getPossibleInstantsFor", // 10. GetInstantFor
+ // RoundDuration
+ "call options.relativeTo.calendar.dateUntil", // 14.f
+ "call options.relativeTo.calendar.dateAdd", // 14.j MoveRelativeDate
+ "call options.relativeTo.calendar.dateAdd", // 14.p MoveRelativeDate
+]);
+new Temporal.Duration(0, 0, 0, 1, 240).total(createOptionsObserver({ unit: "weeks", relativeTo: zonedRelativeTo }));
+assert.compareArray(
+ actual,
+ expectedOpsForBalanceRound,
+ "order of operations with unit = weeks and no calendar units"
+);
+actual.splice(0); // clear
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/precision-exact-mathematical-values-1.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/precision-exact-mathematical-values-1.js
new file mode 100644
index 0000000000..9c74187855
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/precision-exact-mathematical-values-1.js
@@ -0,0 +1,99 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.total
+description: >
+ RoundDuration computes on exact mathematical values.
+features: [Temporal]
+---*/
+
+// Return the next Number value in direction to +Infinity.
+function nextUp(num) {
+ if (!Number.isFinite(num)) {
+ return num;
+ }
+ if (num === 0) {
+ return Number.MIN_VALUE;
+ }
+
+ var f64 = new Float64Array([num]);
+ var u64 = new BigUint64Array(f64.buffer);
+ u64[0] += (num < 0 ? -1n : 1n);
+ return f64[0];
+}
+
+// Return the next Number value in direction to -Infinity.
+function nextDown(num) {
+ if (!Number.isFinite(num)) {
+ return num;
+ }
+ if (num === 0) {
+ return -Number.MIN_VALUE;
+ }
+
+ var f64 = new Float64Array([num]);
+ var u64 = new BigUint64Array(f64.buffer);
+ u64[0] += (num < 0 ? 1n : -1n);
+ return f64[0];
+}
+
+let duration = Temporal.Duration.from({
+ hours: 4000,
+ nanoseconds: 1,
+});
+
+let total = duration.total({unit: "hours"});
+
+// From RoundDuration():
+//
+// 7. Let fractionalSeconds be nanoseconds × 10^-9 + microseconds × 10^-6 + milliseconds × 10^-3 + seconds.
+// = nanoseconds × 10^-9
+// = 1 × 10^-9
+// = 10^-9
+// = 0.000000001
+//
+// 13.a. Let fractionalHours be (fractionalSeconds / 60 + minutes) / 60 + hours.
+// = (fractionalSeconds / 60) / 60 + 4000
+// = 0.000000001 / 3600 + 4000
+//
+// 13.b. Set hours to RoundNumberToIncrement(fractionalHours, increment, roundingMode).
+// = trunc(fractionalHours)
+// = trunc(0.000000001 / 3600 + 4000)
+// = 4000
+//
+// 13.c. Set remainder to fractionalHours - hours.
+// = fractionalHours - hours
+// = 0.000000001 / 3600 + 4000 - 4000
+// = 0.000000001 / 3600
+//
+// From Temporal.Duration.prototype.total ( options ):
+//
+// 18. If unit is "hours", then let whole be roundResult.[[Hours]].
+// ...
+// 24. Return whole + roundResult.[[Remainder]].
+//
+// |whole| is 4000 and the remainder is (0.000000001 / 3600).
+//
+// 0.000000001 / 3600
+// = (1 / 10^9) / 3600
+// = (1 / 36) / 10^11
+// = 0.02777.... / 10^11
+// = 0.0000000000002777...
+//
+// 4000.0000000000002777... can't be represented exactly, the next best approximation
+// is 4000.0000000000005.
+
+const expected = 4000.0000000000005;
+assert.sameValue(expected, 4000.0000000000002777, "the float representation of the result is 4000.0000000000005");
+
+// The next Number in direction -Infinity is less precise.
+assert.sameValue(nextDown(expected), 4000, "the next Number in direction -Infinity is less precise");
+
+// The next Number in direction +Infinity is less precise.
+assert.sameValue(nextUp(expected), 4000.000000000001, "the next Number in direction +Infinity is less precise");
+
+assert.sameValue(total, expected, "return value of total()");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/precision-exact-mathematical-values-2.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/precision-exact-mathematical-values-2.js
new file mode 100644
index 0000000000..2a73bb5ef0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/precision-exact-mathematical-values-2.js
@@ -0,0 +1,101 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.total
+description: >
+ RoundDuration computes on exact mathematical values.
+features: [Temporal]
+---*/
+
+// Return the next Number value in direction to +Infinity.
+function nextUp(num) {
+ if (!Number.isFinite(num)) {
+ return num;
+ }
+ if (num === 0) {
+ return Number.MIN_VALUE;
+ }
+
+ var f64 = new Float64Array([num]);
+ var u64 = new BigUint64Array(f64.buffer);
+ u64[0] += (num < 0 ? -1n : 1n);
+ return f64[0];
+}
+
+// Return the next Number value in direction to -Infinity.
+function nextDown(num) {
+ if (!Number.isFinite(num)) {
+ return num;
+ }
+ if (num === 0) {
+ return -Number.MIN_VALUE;
+ }
+
+ var f64 = new Float64Array([num]);
+ var u64 = new BigUint64Array(f64.buffer);
+ u64[0] += (num < 0 ? 1n : -1n);
+ return f64[0];
+}
+
+let duration = Temporal.Duration.from({
+ hours: 4000,
+ minutes: 59,
+ seconds: 59,
+ milliseconds: 999,
+ microseconds: 999,
+ nanoseconds: 999,
+});
+
+let total = duration.total({unit: "hours"});
+
+// From RoundDuration():
+//
+// 7. Let fractionalSeconds be nanoseconds × 10^-9 + microseconds × 10^-6 + milliseconds × 10^-3 + seconds.
+// = 999 × 10^-9 + 999 × 10^-6 + 999 × 10^-3 + 59
+// = 59.999'999'999
+//
+// 13.a. Let fractionalHours be (fractionalSeconds / 60 + minutes) / 60 + hours.
+// = (59.999'999'999 / 60 + 59) / 60 + 4000
+// = 1 - 0.000000001 / 3600 + 4000
+//
+// 13.b. Set hours to RoundNumberToIncrement(fractionalHours, increment, roundingMode).
+// = trunc(fractionalHours)
+// = trunc(1 - 0.000000001 / 3600 + 4000)
+// = 4000
+//
+// 13.c. Set remainder to fractionalHours - hours.
+// = fractionalHours - hours
+// = 1 - 0.000000001 / 3600 + 4000 - 4000
+// = 1 - 0.000000001 / 3600
+//
+// From Temporal.Duration.prototype.total ( options ):
+//
+// 18. If unit is "hours", then let whole be roundResult.[[Hours]].
+// ...
+// 24. Return whole + roundResult.[[Remainder]].
+//
+// |whole| is 4000 and the remainder is (1 - 0.000000001 / 3600).
+//
+// 1 - 0.000000001 / 3600
+// = 1 - (1 / 10^9) / 3600
+// = 1 - (1 / 36) / 10^11
+// = 1 - 0.02777.... / 10^11
+// = 0.9999999999997222...
+//
+// 4000.9999999999997222... can't be represented exactly, the next best approximation
+// is 4000.9999999999995.
+
+const expected = 4000.9999999999995;
+assert.sameValue(expected, 4000.9999999999997222);
+
+// The next Number in direction -Infinity is less precise.
+assert.sameValue(nextDown(expected), 4000.999999999999);
+
+// The next Number in direction +Infinity is less precise.
+assert.sameValue(nextUp(expected), 4001);
+
+assert.sameValue(total, expected);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/precision-exact-mathematical-values-3.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/precision-exact-mathematical-values-3.js
new file mode 100644
index 0000000000..ab3fd772cc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/precision-exact-mathematical-values-3.js
@@ -0,0 +1,121 @@
+// |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.duration.prototype.total
+description: >
+ RoundDuration computes in such a way as to avoid precision loss when the
+ computed day, week, month, and year lengths are very large numbers.
+info: |
+ RoundDuration:
+ ...
+ 7. If _unit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*, then
+ a. If _zonedRelativeTo_ is not *undefined*, then
+ ...
+ iii. Let _fractionalDays_ be _days_ + _result_.[[Days]] + DivideNormalizedTimeDuration(_result_.[[Remainder]], _result_.[[DayLength]]).
+ ...
+ 10. If _unit_ is *"year"*, then
+ ...
+ z. Let _fractionalYears_ be _years_ + _fractionalDays_ / abs(_oneYearDays_).
+ ...
+ 11. If _unit_ is *"month"*, then
+ ...
+ z. Let _fractionalMonths_ be _months_ + _fractionalDays_ / abs(_oneMonthDays_).
+ ...
+ 12. If _unit_ is *"week"*, then
+ ...
+ s. Let _fractionalWeeks_ be _weeks_ + _fractionalDays_ / abs(_oneWeekDays_).
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+// Return the next Number value in direction to +Infinity.
+function nextUp(num) {
+ if (!Number.isFinite(num)) {
+ return num;
+ }
+ if (num === 0) {
+ return Number.MIN_VALUE;
+ }
+
+ var f64 = new Float64Array([num]);
+ var u64 = new BigUint64Array(f64.buffer);
+ u64[0] += (num < 0 ? -1n : 1n);
+ return f64[0];
+}
+
+// Return the next Number value in direction to -Infinity.
+function nextDown(num) {
+ if (!Number.isFinite(num)) {
+ return num;
+ }
+ if (num === 0) {
+ return -Number.MIN_VALUE;
+ }
+
+ var f64 = new Float64Array([num]);
+ var u64 = new BigUint64Array(f64.buffer);
+ u64[0] += (num < 0 ? 1n : -1n);
+ return f64[0];
+}
+
+// Return bit pattern representation of Number as a Uint8Array of bytes.
+function f64Repr(f) {
+ const buf = new ArrayBuffer(8);
+ new DataView(buf).setFloat64(0, f);
+ return new Uint8Array(buf);
+}
+
+// ============
+
+const tz = new (class extends Temporal.TimeZone {
+ getPossibleInstantsFor() {
+ // Called in NormalizedTimeDurationToDays 21.a from RoundDuration 7.b.
+ // Sets _result_.[[DayLength]] to 2⁵³ - 1 ns, its largest possible value
+ return [new Temporal.Instant(-86400_0000_0000_000_000_000n + 2n ** 53n - 1n)];
+ }
+})("UTC");
+
+const cal = new (class extends Temporal.Calendar {
+ dateAdd() {
+ // Called in MoveRelativeDate from RoundDuration 10.x, 11.x, or 12.q.
+ // Sets _oneYearDays_, _oneMonthDays_, or _oneWeekDays_ respectively to
+ // 200_000_000, its largest possible value.
+ return new Temporal.PlainDate(275760, 9, 13);
+ }
+})("iso8601");
+
+const relativeTo = new Temporal.ZonedDateTime(-86400_0000_0000_000_000_000n, tz, cal);
+const d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, /* nanoseconds = */ 1);
+
+/*
+ * RoundDuration step 7:
+ * ii. result = { [[Days]] = 0, [[Remainder]] = normalized time duration of 1 ns,
+ * [[DayLength]] = Number.MAX_SAFE_INTEGER }
+ * iii. fractionalDays = 0 + 0 + 1 / Number.MAX_SAFE_INTEGER
+ * step 10:
+ * y. oneYearDays = 200_000_000
+ * z. fractionalYears = 0 + (1 / Number.MAX_SAFE_INTEGER) / 200_000_000
+ */
+// Calculated with Python's Decimal module to 50 decimal places
+const expected = 5.55111512312578_3318415740544369642963189519987393e-25;
+
+// Check that we are not accidentally losing precision in our expected value:
+
+assert.sameValue(expected, 5.55111512312578_373662e-25, "the float representation of the result is 5.55111512312578373662e-25");
+assert.compareArray(
+ f64Repr(expected),
+ [0x3a, 0xe5, 0x79, 0x8e, 0xe2, 0x30, 0x8c, 0x3b],
+ "the bit representation of the result is 0x3ae5798ee2308c3b"
+);
+// The next Number in direction -Infinity is less precise.
+assert.sameValue(nextDown(expected), 5.55111512312578_281826e-25, "the next Number in direction -Infinity is less precise");
+// The next Number in direction +Infinity is less precise.
+assert.sameValue(nextUp(expected), 5.55111512312578_465497e-25, "the next Number in direction +Infinity is less precise");
+
+assert.sameValue(d.total({ unit: "years", relativeTo }), expected, "Correct division by large number in years total");
+assert.sameValue(d.total({ unit: "months", relativeTo }), expected, "Correct division by large number in months total");
+assert.sameValue(d.total({ unit: "weeks", relativeTo }), expected, "Correct division by large number in weeks total");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/precision-exact-mathematical-values-4.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/precision-exact-mathematical-values-4.js
new file mode 100644
index 0000000000..74a77e669f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/precision-exact-mathematical-values-4.js
@@ -0,0 +1,155 @@
+// |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.duration.prototype.total
+description: >
+ RoundDuration computes in such a way as to avoid precision loss when the
+ computed day, week, month, and year lengths are very large numbers.
+info: |
+ RoundDuration:
+ ...
+ 7. If _unit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*, then
+ a. If _zonedRelativeTo_ is not *undefined*, then
+ ...
+ iii. Let _fractionalDays_ be _days_ + _result_.[[Days]] + DivideNormalizedTimeDuration(_result_.[[Remainder]], _result_.[[DayLength]]).
+ ...
+ 10. If _unit_ is *"year"*, then
+ ...
+ z. Let _fractionalYears_ be _years_ + _fractionalDays_ / abs(_oneYearDays_).
+ ...
+ 11. If _unit_ is *"month"*, then
+ ...
+ z. Let _fractionalMonths_ be _months_ + _fractionalDays_ / abs(_oneMonthDays_).
+ ...
+ 12. If _unit_ is *"week"*, then
+ ...
+ s. Let _fractionalWeeks_ be _weeks_ + _fractionalDays_ / abs(_oneWeekDays_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+// Return the next Number value in direction to +Infinity.
+function nextUp(num) {
+ if (!Number.isFinite(num)) {
+ return num;
+ }
+ if (num === 0) {
+ return Number.MIN_VALUE;
+ }
+
+ var f64 = new Float64Array([num]);
+ var u64 = new BigUint64Array(f64.buffer);
+ u64[0] += (num < 0 ? -1n : 1n);
+ return f64[0];
+}
+
+// Return the next Number value in direction to -Infinity.
+function nextDown(num) {
+ if (!Number.isFinite(num)) {
+ return num;
+ }
+ if (num === 0) {
+ return -Number.MIN_VALUE;
+ }
+
+ var f64 = new Float64Array([num]);
+ var u64 = new BigUint64Array(f64.buffer);
+ u64[0] += (num < 0 ? 1n : -1n);
+ return f64[0];
+}
+
+// Return bit pattern representation of Number as a Uint8Array of bytes.
+function f64Repr(f) {
+ const buf = new ArrayBuffer(8);
+ new DataView(buf).setFloat64(0, f);
+ return new Uint8Array(buf);
+}
+
+// ============
+// Set up contrived custom time zone and calendar to give dayLengthNs,
+// oneYearDays, oneMonthDays, and oneWeekDays their largest possible values
+
+function createTimeZone() {
+ const tz = new Temporal.TimeZone("UTC");
+ TemporalHelpers.substituteMethod(tz, "getPossibleInstantsFor", [
+ TemporalHelpers.SUBSTITUTE_SKIP, // Duration.total step 19.a MoveRelativeZonedDateTime → AddZonedDateTime
+ TemporalHelpers.SUBSTITUTE_SKIP, // Duration.total step 19.e.ii AddDaysToZonedDateTime
+ TemporalHelpers.SUBSTITUTE_SKIP, // Duration.total step 19.i.ii NormalizedTimeDurationToDays step 16
+ TemporalHelpers.SUBSTITUTE_SKIP, // Duration.total step 19.i.ii NormalizedTimeDurationToDays step 19
+ [new Temporal.Instant(-86400_0000_0000_000_000_000n)], // RoundDuration step 7.a.i MoveRelativeZonedDateTime → AddZonedDateTime
+ [new Temporal.Instant(-86400_0000_0000_000_000_000n + 2n ** 53n - 1n)], // RoundDuration step 7.a.ii NormalizedTimeDurationToDays step 19
+ // sets dayLengthNs to Number.MAX_SAFE_INTEGER
+ ]);
+ return tz;
+}
+
+function createCalendar() {
+ const cal = new Temporal.Calendar("iso8601");
+ TemporalHelpers.substituteMethod(cal, "dateAdd", [
+ TemporalHelpers.SUBSTITUTE_SKIP, // Duration.total step 19.a MoveRelativeZonedDateTime → AddZonedDateTime
+ TemporalHelpers.SUBSTITUTE_SKIP, // RoundDuration step 7.a.i MoveRelativeZonedDateTime → AddZonedDateTime
+ new Temporal.PlainDate(-271821, 4, 20), // RoundDuration step 10.d/11.d AddDate
+ new Temporal.PlainDate(-271821, 4, 20), // RoundDuration step 10.f/11.f AddDate
+ new Temporal.PlainDate(275760, 9, 13), // RoundDuration step 10.r/11.r MoveRelativeDate
+ // sets one{Year,Month,Week}Days to 200_000_000
+ ]);
+ return cal;
+}
+
+// ============
+
+// We will calculate the total years/months/weeks of durations with:
+// 1 year/month/week, 1 day, 0.199000001 s
+
+// RoundDuration step 7:
+// ii. result = { [[Days]] = 1, [[Remainder]] = normalized time duration of 199_000_001 ns,
+// [[DayLength]] = Number.MAX_SAFE_INTEGER }
+// iii. fractionalDays = 1 + 0 + 199_000_001 / Number.MAX_SAFE_INTEGER
+// step 10: (similar for steps 11 and 12 in the case of months/weeks)
+// y. oneYearDays = 200_000_000
+// z. fractionalYears = 1 + (1 + 199_000_001 / Number.MAX_SAFE_INTEGER) / 200_000_000
+//
+// Note: if calculated as 1 + 1/200_000_000 + 199_000_001 / Number.MAX_SAFE_INTEGER / 200_000_000
+// this will lose precision and give the result 1.000000005.
+
+// Calculated with Python's Decimal module to 50 decimal places
+const expected = 1.000000005000000_1104671915053146003490515686745299;
+
+// Check that we are not accidentally losing precision in our expected value:
+
+assert.sameValue(expected, 1.000000005000000_2, "the float representation of the result is 1.0000000050000002");
+assert.compareArray(
+ f64Repr(expected),
+ [0x3f, 0xf0, 0x00, 0x00, 0x01, 0x57, 0x98, 0xef],
+ "the bit representation of the result is 0x3ff00000015798ef"
+);
+// The next Number in direction -Infinity is less precise.
+assert.sameValue(nextDown(expected), 1.000000004999999_96961, "the next Number in direction -Infinity is less precise");
+// The next Number in direction +Infinity is less precise.
+assert.sameValue(nextUp(expected), 1.000000005000000_4137, "the next Number in direction +Infinity is less precise");
+
+// ============
+
+let relativeTo = new Temporal.ZonedDateTime(-86400_0000_0000_000_000_000n, createTimeZone(), createCalendar());
+const dYears = new Temporal.Duration(/* years = */ 1, 0, 0, /* days = */ 1, 0, 0, 0, /* milliseconds = */ 199, 0, /* nanoseconds = */ 1);
+assert.sameValue(dYears.total({ unit: "years", relativeTo }), expected, "Correct division by large number in years total");
+
+relativeTo = new Temporal.ZonedDateTime(-86400_0000_0000_000_000_000n, createTimeZone(), createCalendar());
+const dMonths = new Temporal.Duration(0, /* months = */ 1, 0, /* days = */ 1, 0, 0, 0, /* milliseconds = */ 199, 0, /* nanoseconds = */ 1);
+assert.sameValue(dMonths.total({ unit: "months", relativeTo }), expected, "Correct division by large number in months total");
+
+// Weeks calculation doesn't have the AddDate calls to convert months/weeks to days
+const weeksCal = new Temporal.Calendar("iso8601");
+TemporalHelpers.substituteMethod(weeksCal, "dateAdd", [
+ TemporalHelpers.SUBSTITUTE_SKIP, // Duration.total step 19.a MoveRelativeZonedDateTime → AddZonedDateTime
+ TemporalHelpers.SUBSTITUTE_SKIP, // RoundDuration step 7.a.i MoveRelativeZonedDateTime → AddZonedDateTime
+ new Temporal.PlainDate(275760, 9, 13), // RoundDuration step 12.q MoveRelativeDate
+ // sets one{Year,Month,Week}Days to 200_000_000
+]);
+relativeTo = new Temporal.ZonedDateTime(-86400_0000_0000_000_000_000n, createTimeZone(), weeksCal);
+const dWeeks = new Temporal.Duration(0, 0, /* weejs = */ 1, /* days = */ 1, 0, 0, 0, /* milliseconds = */ 199, 0, /* nanoseconds = */ 1);
+assert.sameValue(dWeeks.total({ unit: "weeks", relativeTo }), expected, "Correct division by large number in weeks total");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/precision-exact-mathematical-values-5.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/precision-exact-mathematical-values-5.js
new file mode 100644
index 0000000000..9d315db9c2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/precision-exact-mathematical-values-5.js
@@ -0,0 +1,61 @@
+// |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.duration.prototype.total
+description: BalanceTimeDuration computes on exact mathematical values.
+features: [BigInt, Temporal]
+---*/
+
+const seconds = 8692288669465520;
+
+{
+ const milliseconds = 513;
+ const d = new Temporal.Duration(0, 0, 0, 0, 0, 0, seconds, milliseconds);
+
+ const result = d.total({ unit: "milliseconds" });
+
+ // The result should be the nearest Number value to 8692288669465520512
+ const expectedMilliseconds = Number(BigInt(seconds) * 1000n + BigInt(milliseconds));
+ assert.sameValue(expectedMilliseconds, 8692288669465520_513, "check expected value (ms)");
+
+ assert.sameValue(
+ result, expectedMilliseconds,
+ "BalanceTimeDuration should implement floating-point calculation correctly for largestUnit milliseconds"
+ );
+}
+
+{
+ const microseconds = 373761;
+ const d = new Temporal.Duration(0, 0, 0, 0, 0, 0, seconds, 0, microseconds);
+
+ const result = d.total({ unit: "microseconds" });
+
+ // The result should be the nearest Number value to 8692288669465520373761
+ const expectedMicroseconds = Number(BigInt(seconds) * 1_000_000n + BigInt(microseconds));
+ assert.sameValue(expectedMicroseconds, 8692288669465520_373_761, "check expected value (µs)");
+
+ assert.sameValue(
+ result, expectedMicroseconds,
+ "BalanceTimeDuration should implement floating-point calculation correctly for largestUnit milliseconds"
+ );
+}
+
+{
+ const nanoseconds = 321_414_345;
+ const d = new Temporal.Duration(0, 0, 0, 0, 0, 0, seconds, 0, 0, nanoseconds);
+
+ const result = d.total({ unit: "nanoseconds" });
+
+ // The result should be the nearest Number value to 8692288669465520321414345
+ const expectedNanoseconds = Number(BigInt(seconds) * 1_000_000_000n + BigInt(nanoseconds));
+ assert.sameValue(expectedNanoseconds, 8692288669465520_321_414_345, "check expected value (ns)");
+
+ assert.sameValue(
+ result, expectedNanoseconds,
+ "BalanceTimeDuration should implement floating-point calculation correctly for largestUnit nanoseconds"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/precision-exact-mathematical-values-6.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/precision-exact-mathematical-values-6.js
new file mode 100644
index 0000000000..8072451c5f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/precision-exact-mathematical-values-6.js
@@ -0,0 +1,143 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2024 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.total
+description: >
+ DivideNormalizedTimeDuration computes on exact mathematical values.
+info: |
+ Temporal.Duration.prototype.total ( totalOf )
+
+ ...
+ 20. Let roundRecord be ? RoundDuration(unbalanceResult.[[Years]],
+ unbalanceResult.[[Months]], unbalanceResult.[[Weeks]], days, norm, 1,
+ unit, "trunc", plainRelativeTo, calendarRec, zonedRelativeTo, timeZoneRec,
+ precalculatedPlainDateTime).
+ 21. Return 𝔽(roundRecord.[[Total]]).
+
+ RoundDuration ( ... )
+
+ ...
+ 14. Else if unit is "hour", then
+ a. Let divisor be 3.6 × 10^12.
+ b. Set total to DivideNormalizedTimeDuration(norm, divisor).
+ ...
+
+ DivideNormalizedTimeDuration ( d, divisor )
+
+ 1. Assert: divisor ≠ 0.
+ 2. Return d.[[TotalNanoseconds]] / divisor.
+features: [Temporal]
+---*/
+
+// Randomly generated test data.
+const data = [
+ {
+ hours: 816,
+ nanoseconds: 2049_187_497_660,
+ },
+ {
+ hours: 7825,
+ nanoseconds: 1865_665_040_770,
+ },
+ {
+ hours: 0,
+ nanoseconds: 1049_560_584_034,
+ },
+ {
+ hours: 2055144,
+ nanoseconds: 2502_078_444_371,
+ },
+ {
+ hours: 31,
+ nanoseconds: 1010_734_758_745,
+ },
+ {
+ hours: 24,
+ nanoseconds: 2958_999_560_387,
+ },
+ {
+ hours: 0,
+ nanoseconds: 342_058_521_588,
+ },
+ {
+ hours: 17746,
+ nanoseconds: 3009_093_506_309,
+ },
+ {
+ hours: 4,
+ nanoseconds: 892_480_914_569,
+ },
+ {
+ hours: 3954,
+ nanoseconds: 571_647_777_618,
+ },
+ {
+ hours: 27,
+ nanoseconds: 2322_199_502_640,
+ },
+ {
+ hours: 258054064,
+ nanoseconds: 2782_411_891_222,
+ },
+ {
+ hours: 1485,
+ nanoseconds: 2422_559_903_100,
+ },
+ {
+ hours: 0,
+ nanoseconds: 1461_068_214_153,
+ },
+ {
+ hours: 393,
+ nanoseconds: 1250_229_561_658,
+ },
+ {
+ hours: 0,
+ nanoseconds: 91_035_820,
+ },
+ {
+ hours: 0,
+ nanoseconds: 790_982_655,
+ },
+ {
+ hours: 150,
+ nanoseconds: 608_531_524,
+ },
+ {
+ hours: 5469,
+ nanoseconds: 889_204_952,
+ },
+ {
+ hours: 7870,
+ nanoseconds: 680_042_770,
+ },
+];
+
+const nsPerHour = 3600_000_000_000;
+
+const fractionDigits = Math.log10(nsPerHour) + Math.log10(100_000_000_000) - Math.log10(36);
+assert.sameValue(fractionDigits, 22);
+
+for (let {hours, nanoseconds} of data) {
+ assert(nanoseconds < nsPerHour);
+
+ // Compute enough fractional digits to approximate the exact result. Use BigInts
+ // to avoid floating point precision loss. Fill to the left with implicit zeros.
+ let fraction = ((BigInt(nanoseconds) * 100_000_000_000n) / 36n).toString().padStart(fractionDigits, "0");
+
+ // Get the Number approximation from the string representation.
+ let expected = Number(`${hours}.${fraction}`);
+
+ let d = Temporal.Duration.from({hours, nanoseconds});
+ let actual = d.total("hours");
+
+ assert.sameValue(
+ actual,
+ expected,
+ `hours=${hours}, nanoseconds=${nanoseconds}`,
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/precision-exact-mathematical-values-7.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/precision-exact-mathematical-values-7.js
new file mode 100644
index 0000000000..6096f1afac
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/precision-exact-mathematical-values-7.js
@@ -0,0 +1,122 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2024 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.total
+description: >
+ DivideNormalizedTimeDuration computes on exact mathematical values.
+info: |
+ Temporal.Duration.prototype.total ( totalOf )
+
+ ...
+ 20. Let roundRecord be ? RoundDuration(unbalanceResult.[[Years]],
+ unbalanceResult.[[Months]], unbalanceResult.[[Weeks]], days, norm, 1,
+ unit, "trunc", plainRelativeTo, calendarRec, zonedRelativeTo, timeZoneRec,
+ precalculatedPlainDateTime).
+ 21. Return 𝔽(roundRecord.[[Total]]).
+
+ RoundDuration ( ... )
+
+ ...
+ 16. Else if unit is "second", then
+ a. Let divisor be 10^9.
+ b. Set total to DivideNormalizedTimeDuration(norm, divisor).
+ ...
+ 17. Else if unit is "millisecond", then
+ a. Let divisor be 10^6.
+ b. Set total to DivideNormalizedTimeDuration(norm, divisor).
+ ...
+ 18. Else if unit is "microsecond", then
+ a. Let divisor be 10^3.
+ b. Set total to DivideNormalizedTimeDuration(norm, divisor).
+ ...
+
+ DivideNormalizedTimeDuration ( d, divisor )
+
+ 1. Assert: divisor ≠ 0.
+ 2. Return d.[[TotalNanoseconds]] / divisor.
+features: [Temporal]
+---*/
+
+// Test duration units where the fractional part is a power of ten.
+const units = [
+ "seconds", "milliseconds", "microseconds", "nanoseconds",
+];
+
+// Conversion factors to nanoseconds precision.
+const toNanos = {
+ "seconds": 1_000_000_000n,
+ "milliseconds": 1_000_000n,
+ "microseconds": 1_000n,
+ "nanoseconds": 1n,
+};
+
+const integers = [
+ // Small integers.
+ 0,
+ 1,
+ 2,
+
+ // Large integers around Number.MAX_SAFE_INTEGER.
+ 2**51,
+ 2**52,
+ 2**53,
+ 2**54,
+];
+
+const fractions = [
+ // True fractions.
+ 0, 1, 10, 100, 125, 200, 250, 500, 750, 800, 900, 950, 999,
+
+ // Fractions with overflow.
+ 1_000,
+ 1_999,
+ 2_000,
+ 2_999,
+ 3_000,
+ 3_999,
+ 4_000,
+ 4_999,
+
+ 999_999,
+ 1_000_000,
+ 1_000_001,
+
+ 999_999_999,
+ 1_000_000_000,
+ 1_000_000_001,
+];
+
+const maxTimeDuration = (2n ** 53n) * (10n ** 9n) - 1n;
+
+// Iterate over all units except the last one.
+for (let unit of units.slice(0, -1)) {
+ let smallerUnit = units[units.indexOf(unit) + 1];
+
+ for (let integer of integers) {
+ for (let fraction of fractions) {
+ // Total nanoseconds must not exceed |maxTimeDuration|.
+ let totalNanoseconds = BigInt(integer) * toNanos[unit] + BigInt(fraction) * toNanos[smallerUnit];
+ if (totalNanoseconds > maxTimeDuration) {
+ continue;
+ }
+
+ // Get the Number approximation from the string representation.
+ let i = BigInt(integer) + BigInt(fraction) / 1000n;
+ let f = String(fraction % 1000).padStart(3, "0");
+ let expected = Number(`${i}.${f}`);
+
+ let d = Temporal.Duration.from({[unit]: integer, [smallerUnit]: fraction});
+ let actual = d.total(unit);
+
+ assert.sameValue(
+ actual,
+ expected,
+ `${unit}=${integer}, ${smallerUnit}=${fraction}`,
+ );
+ }
+ }
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/prop-desc.js
new file mode 100644
index 0000000000..8374803e9f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/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.duration.prototype.total
+description: The "total" property of Temporal.Duration.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Duration.prototype.total,
+ "function",
+ "`typeof Duration.prototype.total` is `function`"
+);
+
+verifyProperty(Temporal.Duration.prototype, "total", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/proto-in-calendar-fields.js
new file mode 100644
index 0000000000..b40b2d4b30
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/proto-in-calendar-fields.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.duration.prototype.total
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const relativeTo = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar, timeZone: 'Europe/Paris' };
+const instance = new Temporal.Duration(1, 0, 0, 0, 24);
+
+assert.throws(RangeError, () => instance.total({ unit: "days", relativeTo }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/read-time-fields-before-datefromfields.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/read-time-fields-before-datefromfields.js
new file mode 100644
index 0000000000..0e80cd0ef5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/read-time-fields-before-datefromfields.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.duration.prototype.total
+description: The time fields are read from the object before being passed to dateFromFields().
+info: |
+ sec-temporal.duration.prototype.total step 4:
+ 4. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_).
+ sec-temporal-torelativetemporalobject step 4.g:
+ g. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+ sec-temporal-interprettemporaldatetimefields steps 1–2:
+ 1. Let _timeResult_ be ? ToTemporalTimeRecord(_fields_).
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarMakeInvalidGettersTime();
+const duration = new Temporal.Duration(1, 1, 1, 1, 1, 1, 1);
+duration.total({ unit: 'seconds', relativeTo: { year: 2000, month: 1, day: 1, calendar } });
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..9567656f64
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-infinity-throws-rangeerror.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.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.duration.prototype.total
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+const base = { year: 2000, month: 5, day: 2, hour: 15, minute: 30, second: 45, millisecond: 987, microsecond: 654, nanosecond: 321 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.total({ unit: "seconds", relativeTo: { ...base, [prop]: inf } }), `${prop} property cannot be ${inf} in relativeTo`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.total({ unit: "seconds", relativeTo: { ...base, [prop]: obj } }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-leap-second.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-leap-second.js
new file mode 100644
index 0000000000..c69a1dd35e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-leap-second.js
@@ -0,0 +1,45 @@
+// |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.duration.prototype.total
+description: Leap second is constrained in both an ISO string and a property bag
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(1, 0, 0, 0, 24);
+
+let relativeTo = "2016-12-31T23:59:60";
+const result1 = instance.total({ unit: "days", relativeTo });
+assert.sameValue(
+ result1,
+ 366,
+ "leap second is a valid ISO string for PlainDate relativeTo"
+);
+
+relativeTo = "2016-12-31T23:59:60+00:00[UTC]";
+const result2 = instance.total({ unit: "days", relativeTo });
+assert.sameValue(
+ result2,
+ 366,
+ "leap second is a valid ISO string for ZonedDateTime relativeTo"
+);
+
+relativeTo = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+const result3 = instance.total({ unit: "days", relativeTo });
+assert.sameValue(
+ result3,
+ 366,
+ "second: 60 is valid in a property bag for PlainDate relativeTo"
+);
+
+relativeTo = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60, timeZone: "UTC" };
+const result4 = instance.total({ unit: "days", relativeTo });
+assert.sameValue(
+ result4,
+ 366,
+ "second: 60 is valid in a property bag for ZonedDateTime relativeTo"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-number.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-number.js
new file mode 100644
index 0000000000..765099da48
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-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.duration.prototype.total
+description: A number cannot be used in place of a relativeTo
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(1, 0, 0, 0, 24);
+
+const numbers = [
+ 1,
+ 20191101,
+ -20191101,
+ 1234567890,
+];
+
+for (const relativeTo of numbers) {
+ assert.throws(
+ TypeError,
+ () => instance.total({ unit: "days", relativeTo }),
+ `A number (${relativeTo}) is not a valid ISO string for relativeTo`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-plaindate-add24hourdaystonormalizedtimeduration-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-plaindate-add24hourdaystonormalizedtimeduration-out-of-range.js
new file mode 100644
index 0000000000..d1bdde5edf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-plaindate-add24hourdaystonormalizedtimeduration-out-of-range.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2024 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.compare
+description: RangeError thrown if adding the duration to the relativeTo date would result in anout-of-range date-time
+features: [Temporal]
+---*/
+
+let duration = Temporal.Duration.from({
+ years: 1,
+ seconds: 2**53 - 1,
+});
+let relativeTo = new Temporal.PlainDate(2000, 1, 1);
+
+assert.throws(RangeError, () => duration.total({ relativeTo, unit: "days" }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-ambiguous-wall-clock-time.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-ambiguous-wall-clock-time.js
new file mode 100644
index 0000000000..965d3e8813
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-ambiguous-wall-clock-time.js
@@ -0,0 +1,92 @@
+// |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.duration.prototype.total
+description: >
+ Correct time zone calls are made when converting a ZonedDateTime-like
+ relativeTo property bag denoting an ambiguous wall-clock time
+includes: [temporalHelpers.js, compareArray.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+
+const dstTimeZone = TemporalHelpers.springForwardFallBackTimeZone();
+const dstTimeZoneObserver = TemporalHelpers.timeZoneObserver(actual, "timeZone", {
+ getOffsetNanosecondsFor: dstTimeZone.getOffsetNanosecondsFor.bind(dstTimeZone),
+ getPossibleInstantsFor: dstTimeZone.getPossibleInstantsFor.bind(dstTimeZone),
+});
+const calendar = TemporalHelpers.calendarObserver(actual, "calendar");
+
+const instance = new Temporal.Duration(1, 0, 0, 0, 24);
+
+let relativeTo = { year: 2000, month: 4, day: 2, hour: 2, minute: 30, timeZone: dstTimeZoneObserver, calendar };
+instance.total({ unit: "days", relativeTo });
+
+const expected = [
+ // GetTemporalCalendarSlotValueWithISODefault
+ "has calendar.dateAdd",
+ "has calendar.dateFromFields",
+ "has calendar.dateUntil",
+ "has calendar.day",
+ "has calendar.dayOfWeek",
+ "has calendar.dayOfYear",
+ "has calendar.daysInMonth",
+ "has calendar.daysInWeek",
+ "has calendar.daysInYear",
+ "has calendar.fields",
+ "has calendar.id",
+ "has calendar.inLeapYear",
+ "has calendar.mergeFields",
+ "has calendar.month",
+ "has calendar.monthCode",
+ "has calendar.monthDayFromFields",
+ "has calendar.monthsInYear",
+ "has calendar.weekOfYear",
+ "has calendar.year",
+ "has calendar.yearMonthFromFields",
+ "has calendar.yearOfWeek",
+ // lookup
+ "get calendar.dateFromFields",
+ "get calendar.fields",
+ // CalendarFields
+ "call calendar.fields",
+ // InterpretTemporalDateTimeFields
+ "call calendar.dateFromFields",
+ // ToTemporalTimeZoneSlotValue
+ "has timeZone.getOffsetNanosecondsFor",
+ "has timeZone.getPossibleInstantsFor",
+ "has timeZone.id",
+ // lookup
+ "get timeZone.getOffsetNanosecondsFor",
+ "get timeZone.getPossibleInstantsFor",
+ // InterpretISODateTimeOffset
+ "call timeZone.getPossibleInstantsFor",
+];
+
+const expectedSpringForward = expected.concat([
+ // DisambiguatePossibleInstants
+ "call timeZone.getOffsetNanosecondsFor",
+ "call timeZone.getOffsetNanosecondsFor",
+ "call timeZone.getPossibleInstantsFor",
+]);
+assert.compareArray(
+ actual.slice(0, expectedSpringForward.length), // ignore operations after ToRelativeTemporalObject
+ expectedSpringForward,
+ "order of operations converting property bag at skipped wall-clock time"
+);
+actual.splice(0); // clear
+
+relativeTo = { year: 2000, month: 10, day: 29, hour: 1, minute: 30, timeZone: dstTimeZoneObserver, calendar };
+instance.total({ unit: "days", relativeTo });
+
+assert.compareArray(
+ actual.slice(0, expected.length), // ignore operations after ToRelativeTemporalObject
+ expected,
+ "order of operations converting property bag at repeated wall-clock time"
+);
+actual.splice(0); // clear
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..44c97b82e9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-builtin-calendar-no-array-iteration.js
@@ -0,0 +1,25 @@
+// |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.duration.prototype.total
+description: >
+ Calling the method with a relativeTo property bag with a builtin calendar
+ causes no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const timeZone = "UTC";
+const instance = new Temporal.Duration(1, 0, 0, 0, 24);
+const relativeTo = { year: 2000, month: 5, day: 2, hour: 21, minute: 43, second: 5, timeZone, calendar: "iso8601" };
+instance.total({ unit: "days", relativeTo });
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-calendar-datefromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..d8a4d09f9d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-calendar-datefromfields-called-with-null-prototype-fields.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.duration.prototype.total
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.Duration(1, 0, 0, 0, 24);
+const relativeTo = { year: 2000, month: 5, day: 2, calendar };
+instance.total({ unit: "days", relativeTo });
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-calendar-number.js
new file mode 100644
index 0000000000..52a4647bcf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-calendar-number.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.duration.prototype.total
+description: A number as calendar in relativeTo property bag is invalid
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(1, 0, 0, 0, 24);
+
+const numbers = [
+ 1,
+ 19970327,
+ -19970327,
+ 1234567890,
+];
+
+for (const calendar of numbers) {
+ const relativeTo = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ TypeError,
+ () => instance.total({ unit: "days", relativeTo }),
+ `A number (${calendar}) is not a valid ISO string for relativeTo.calendar`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-calendar-string.js
new file mode 100644
index 0000000000..8cba83ba56
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-calendar-string.js
@@ -0,0 +1,29 @@
+// |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.duration.prototype.total
+description: >
+ Builtin dateFromFields method is not observably called when the property bag
+ has a string-valued calendar property
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dateFromFieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dateFromFields");
+Object.defineProperty(Temporal.Calendar.prototype, "dateFromFields", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("dateFromFields should not be looked up");
+ },
+});
+
+const instance = new Temporal.Duration(1, 0, 0, 0, 24);
+const relativeTo = { year: 2000, month: 5, day: 2, calendar: "iso8601" };
+instance.total({ unit: "days", relativeTo });
+
+Object.defineProperty(Temporal.Calendar.prototype, "dateFromFields", dateFromFieldsOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-calendar-wrong-type.js
new file mode 100644
index 0000000000..cef7d31b45
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-calendar-wrong-type.js
@@ -0,0 +1,48 @@
+// |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.duration.prototype.total
+description: >
+ Appropriate error thrown when relativeTo.calendar cannot be converted to a
+ calendar object or string
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.Duration(1, 0, 0, 0, 24);
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [calendar, description] of primitiveTests) {
+ const relativeTo = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ typeof calendar === 'string' ? RangeError : TypeError,
+ () => instance.total({ unit: "days", relativeTo }),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.PlainDate, "Temporal.PlainDate, object"],
+ [Temporal.PlainDate.prototype, "Temporal.PlainDate.prototype, object"],
+ [Temporal.ZonedDateTime, "Temporal.ZonedDateTime, object"],
+ [Temporal.ZonedDateTime.prototype, "Temporal.ZonedDateTime.prototype, object"],
+];
+
+for (const [calendar, description] of typeErrorTests) {
+ const relativeTo = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(TypeError, () => instance.total({ unit: "days", relativeTo }), `${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/Duration/prototype/total/relativeto-propertybag-getpossibleinstantsfor-called-with-iso8601-calendar.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-getpossibleinstantsfor-called-with-iso8601-calendar.js
new file mode 100644
index 0000000000..16ab8873ec
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-getpossibleinstantsfor-called-with-iso8601-calendar.js
@@ -0,0 +1,59 @@
+// |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.duration.prototype.total
+description: >
+ Time zone's getPossibleInstantsFor is called with a PlainDateTime with the
+ built-in ISO 8601 calendar
+features: [Temporal]
+info: |
+ DisambiguatePossibleInstants:
+ 2. Let _n_ be _possibleInstants_'s length.
+ ...
+ 5. Assert: _n_ = 0.
+ ...
+ 19. If _disambiguation_ is *"earlier"*, then
+ ...
+ c. Let _earlierDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*).
+ d. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _earlierDateTime_).
+ ...
+ 20. Assert: _disambiguation_ is *"compatible"* or *"later"*.
+ ...
+ 23. Let _laterDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*).
+ 24. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _laterDateTime_).
+---*/
+
+class SkippedDateTime extends Temporal.TimeZone {
+ constructor() {
+ super("UTC");
+ this.calls = 0;
+ }
+
+ getPossibleInstantsFor(dateTime) {
+ // Calls occur in pairs. For the first one return no possible instants so
+ // that DisambiguatePossibleInstants will call it again
+ if (this.calls++ % 2 == 0) {
+ return [];
+ }
+
+ assert.sameValue(
+ dateTime.getISOFields().calendar,
+ "iso8601",
+ "getPossibleInstantsFor called with dateTime with built-in ISO 8601 calendar"
+ );
+ return super.getPossibleInstantsFor(dateTime);
+ }
+}
+
+const nonBuiltinISOCalendar = new Temporal.Calendar("iso8601");
+const timeZone = new SkippedDateTime();
+const relativeTo = { year: 2000, month: 5, day: 2, timeZone, calendar: nonBuiltinISOCalendar };
+
+const instance = new Temporal.Duration(1, 0, 0, 0, 24);
+instance.total({ unit: "days", relativeTo });
+
+assert.sameValue(timeZone.calls, 10, "getPossibleInstantsFor should have been called 10 times");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-invalid-offset-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-invalid-offset-string.js
new file mode 100644
index 0000000000..dcb4944855
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-invalid-offset-string.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.duration.prototype.total
+description: relativeTo property bag with offset property is rejected if offset is in the wrong format
+features: [Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.Duration(1, 0, 0, 0, 24);
+
+const badOffsets = [
+ "00:00", // missing sign
+ "+0", // too short
+ "-000:00", // too long
+ 0, // must be a string
+ null, // must be a string
+ true, // must be a string
+ 1000n, // must be a string
+];
+badOffsets.forEach((offset) => {
+ const relativeTo = { year: 2021, month: 10, day: 28, offset, timeZone };
+ assert.throws(
+ typeof(offset) === 'string' ? RangeError : TypeError,
+ () => instance.total({ unit: "days", relativeTo }),
+ `"${offset} is not a valid offset string`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-no-time-units.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-no-time-units.js
new file mode 100644
index 0000000000..dc9d54ed5d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-no-time-units.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.duration.prototype.total
+description: Missing time units in relativeTo property bag default to 0
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(1, 0, 0, 0, 24);
+
+let relativeTo = { year: 2000, month: 1, day: 1 };
+const result = instance.total({ unit: "days", relativeTo });
+assert.sameValue(result, 367, "missing time units default to 0");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..d5200d49b8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,18 @@
+// |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.duration.prototype.total
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+ assert.throws(RangeError, () => duration.total({ unit: "seconds", relativeTo: { year: 2000, month: 5, day: 2, hour: 12, timeZone } }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..4929986c8c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-getoffsetnanosecondsfor-not-callable.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.duration.prototype.total
+description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable
+features: [BigInt, Symbol, Temporal, arrow-function]
+---*/
+
+[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => {
+ const timeZone = new Temporal.TimeZone("UTC");
+ const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => duration.total({ unit: "seconds", relativeTo: { year: 2000, month: 5, day: 2, hour: 12, timeZone } }),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..332fa1bec5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,18 @@
+// |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.duration.prototype.total
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+ assert.throws(RangeError, () => duration.total({ unit: "seconds", relativeTo: { year: 2000, month: 5, day: 2, hour: 12, timeZone } }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..323e64cc8f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.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.duration.prototype.total
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+ assert.throws(TypeError, () => duration.total({ unit: "seconds", relativeTo: { year: 2000, month: 5, day: 2, hour: 12, timeZone } }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-string-datetime.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-string-datetime.js
new file mode 100644
index 0000000000..d5fa6c32ef
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-string-datetime.js
@@ -0,0 +1,66 @@
+// |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.duration.prototype.total
+description: Conversion of ISO date-time strings to Temporal.TimeZone instances
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(1);
+
+let timeZone = "2021-08-19T17:30";
+assert.throws(RangeError, () => instance.total({ unit: "months", relativeTo: { year: 2000, month: 5, day: 2, timeZone } }), "bare date-time string is not a time zone");
+
+[
+ "2021-08-19T17:30-07:00:01",
+ "2021-08-19T17:30-07:00:00",
+ "2021-08-19T17:30-07:00:00.1",
+ "2021-08-19T17:30-07:00:00.0",
+ "2021-08-19T17:30-07:00:00.01",
+ "2021-08-19T17:30-07:00:00.00",
+ "2021-08-19T17:30-07:00:00.001",
+ "2021-08-19T17:30-07:00:00.000",
+ "2021-08-19T17:30-07:00:00.0001",
+ "2021-08-19T17:30-07:00:00.0000",
+ "2021-08-19T17:30-07:00:00.00001",
+ "2021-08-19T17:30-07:00:00.00000",
+ "2021-08-19T17:30-07:00:00.000001",
+ "2021-08-19T17:30-07:00:00.000000",
+ "2021-08-19T17:30-07:00:00.0000001",
+ "2021-08-19T17:30-07:00:00.0000000",
+ "2021-08-19T17:30-07:00:00.00000001",
+ "2021-08-19T17:30-07:00:00.00000000",
+ "2021-08-19T17:30-07:00:00.000000001",
+ "2021-08-19T17:30-07:00:00.000000000",
+].forEach((timeZone) => {
+ assert.throws(
+ RangeError,
+ () => instance.total({ unit: "months", relativeTo: { year: 2000, month: 5, day: 2, timeZone } }),
+ `ISO string ${timeZone} with a sub-minute offset is not a valid time zone`
+ );
+});
+
+// The following are all valid strings so should not throw:
+
+[
+ "2021-08-19T17:30Z",
+ "2021-08-19T1730Z",
+ "2021-08-19T17:30-07:00",
+ "2021-08-19T1730-07:00",
+ "2021-08-19T17:30-0700",
+ "2021-08-19T1730-0700",
+ "2021-08-19T17:30[UTC]",
+ "2021-08-19T1730[UTC]",
+ "2021-08-19T17:30Z[UTC]",
+ "2021-08-19T1730Z[UTC]",
+ "2021-08-19T17:30-07:00[UTC]",
+ "2021-08-19T1730-07:00[UTC]",
+ "2021-08-19T17:30-0700[UTC]",
+ "2021-08-19T1730-0700[UTC]",
+].forEach((timeZone) => {
+ instance.total({ unit: "months", relativeTo: { year: 2000, month: 5, day: 2, timeZone } });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-string-leap-second.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-string-leap-second.js
new file mode 100644
index 0000000000..a77fbb0f78
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-string-leap-second.js
@@ -0,0 +1,22 @@
+// |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.duration.prototype.total
+description: Leap second is a valid ISO string for TimeZone
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(1);
+let timeZone = "2016-12-31T23:59:60+00:00[UTC]";
+
+// A string with a leap second is a valid ISO string, so the following
+// operation should not throw
+
+instance.total({ unit: "months", relativeTo: { year: 2000, month: 5, day: 2, timeZone } });
+
+timeZone = "2021-08-19T17:30:45.123456789+23:59[+23:59:60]";
+assert.throws(RangeError, () => instance.total({ unit: "months", relativeTo: { year: 2000, month: 5, day: 2, timeZone } }), "leap second in time zone name not valid");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-string-year-zero.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-string-year-zero.js
new file mode 100644
index 0000000000..2592d5267a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-string-year-zero.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.duration.prototype.total
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+const instance = new Temporal.Duration(1);
+invalidStrings.forEach((timeZone) => {
+ assert.throws(
+ RangeError,
+ () => instance.total({ unit: "months", relativeTo: { year: 2000, month: 5, day: 2, timeZone } }),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-string.js
new file mode 100644
index 0000000000..bc64d7156c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-string.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.duration.prototype.total
+description: Time zone IDs are valid input for a time zone
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getPossibleInstantsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getPossibleInstantsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be looked up");
+ },
+});
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+
+const instance = new Temporal.Duration(1);
+
+// The following are all valid strings so should not throw:
+
+["UTC", "+01:00"].forEach((timeZone) => {
+ instance.total({ unit: "months", relativeTo: { year: 2000, month: 5, day: 2, timeZone } });
+});
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", getPossibleInstantsForOriginal);
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-wrong-type.js
new file mode 100644
index 0000000000..1f2d79fa47
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-wrong-type.js
@@ -0,0 +1,42 @@
+// |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.duration.prototype.total
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or object for TimeZone
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.Duration(1);
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [19761118, "number that would convert to a valid ISO string in other contexts"],
+ [1n, "bigint"],
+];
+
+for (const [timeZone, description] of primitiveTests) {
+ assert.throws(
+ typeof timeZone === 'string' ? RangeError : TypeError,
+ () => instance.total({ unit: "months", relativeTo: { year: 2000, month: 5, day: 2, timeZone } }),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "object not implementing time zone protocol"],
+ [new Temporal.Calendar("iso8601"), "calendar instance"],
+];
+
+for (const [timeZone, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.total({ unit: "months", relativeTo: { year: 2000, month: 5, day: 2, timeZone } }), `${description} is not a valid object and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-string-datetime.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-string-datetime.js
new file mode 100644
index 0000000000..b2e0cd8a56
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-string-datetime.js
@@ -0,0 +1,40 @@
+// |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.duration.prototype.total
+description: >
+ Conversion of ISO date-time strings as relativeTo option to
+ Temporal.ZonedDateTime or Temporal.PlainDateTime instances
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(1, 0, 0, 0, 24);
+
+let relativeTo = "2019-11-01T00:00";
+const result1 = instance.total({ unit: "days", relativeTo });
+assert.sameValue(result1, 367, "bare date-time string is a plain relativeTo");
+
+relativeTo = "2019-11-01T00:00-07:00";
+const result2 = instance.total({ unit: "days", relativeTo });
+assert.sameValue(result2, 367, "date-time + offset is a plain relativeTo");
+
+relativeTo = "2019-11-01T00:00[-07:00]";
+const result3 = instance.total({ unit: "days", relativeTo });
+assert.sameValue(result3, 367, "date-time + IANA annotation is a zoned relativeTo");
+
+relativeTo = "2019-11-01T00:00Z[-07:00]";
+const result4 = instance.total({ unit: "days", relativeTo });
+assert.sameValue(result4, 367, "date-time + Z + IANA annotation is a zoned relativeTo");
+
+relativeTo = "2019-11-01T00:00+00:00[UTC]";
+const result5 = instance.total({ unit: "days", relativeTo });
+assert.sameValue(result5, 367, "date-time + offset + IANA annotation is a zoned relativeTo");
+
+relativeTo = "2019-11-01T00:00Z";
+assert.throws(RangeError, () => instance.total({ unit: "days", relativeTo }), "date-time + Z throws without an IANA annotation");
+relativeTo = "2019-11-01T00:00+04:15[UTC]";
+assert.throws(RangeError, () => instance.total({ unit: "days", relativeTo }), "date-time + offset + IANA annotation throws if wall time and exact time mismatch");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-string-invalid.js
new file mode 100644
index 0000000000..f60e10f831
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-string-invalid.js
@@ -0,0 +1,16 @@
+// |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.duration.prototype.total
+description: RangeError thrown if relativeTo is a string with the wrong format
+features: [Temporal]
+---*/
+
+['bad string', '15:30:45.123456', 'iso8601', 'UTC', 'P1YT1H'].forEach((relativeTo) => {
+ const duration = new Temporal.Duration(0, 0, 0, 31);
+ assert.throws(RangeError, () => duration.total({ unit: "months", relativeTo }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-string-plaindatetime-invalid.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-string-plaindatetime-invalid.js
new file mode 100644
index 0000000000..d3d544c220
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-string-plaindatetime-invalid.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.total
+description: >
+ Throws a RangeError if "relativeTo" is a date/time value outside the valid limits.
+info: |
+ Temporal.Duration.prototype.total ( totalOf )
+ ...
+ 6. Let relativeTo be ? ToRelativeTemporalObject(totalOf).
+ ...
+
+ ToRelativeTemporalObject ( options )
+ ...
+ 9. Return ? CreateTemporalDate(result.[[Year]], result.[[Month]], result.[[Day]], calendar).
+features: [Temporal]
+---*/
+
+var duration = Temporal.Duration.from({nanoseconds: 0});
+var options = {unit: "nanoseconds", relativeTo: "+999999-01-01"};
+
+assert.throws(RangeError, () => duration.total(options));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-string-plaindatetime.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-string-plaindatetime.js
new file mode 100644
index 0000000000..8f5a2c3f94
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-string-plaindatetime.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.duration.prototype.total
+description: The relativeTo option accepts a PlainDateTime-like ISO 8601 string
+features: [Temporal]
+---*/
+
+['2000-01-01', '2000-01-01T00:00', '2000-01-01T00:00[u-ca=iso8601]'].forEach((relativeTo) => {
+ const duration = new Temporal.Duration(0, 0, 0, 31);
+ const result = duration.total({ unit: "months", relativeTo });
+ assert.sameValue(result, 1);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-string-zoneddatetime-wrong-offset.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-string-zoneddatetime-wrong-offset.js
new file mode 100644
index 0000000000..10b00920d9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-string-zoneddatetime-wrong-offset.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.duration.prototype.total
+description: Throws if a ZonedDateTime-like relativeTo string has the wrong UTC offset
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(1, 0, 0, 0, 24);
+const relativeTo = "2000-01-01T00:00+05:30[UTC]";
+assert.throws(
+ RangeError,
+ () => instance.total({ unit: "days", relativeTo }),
+ "total should throw RangeError on a string with UTC offset mismatch"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-string-zoneddatetime.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-string-zoneddatetime.js
new file mode 100644
index 0000000000..c23b3e97a4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-string-zoneddatetime.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.duration.prototype.total
+description: The relativeTo option accepts a ZonedDateTime-like ISO 8601 string
+features: [Temporal]
+---*/
+
+[
+ '2000-01-01[UTC]',
+ '2000-01-01T00:00[UTC]',
+ '2000-01-01T00:00+00:00[UTC]',
+ '2000-01-01T00:00+00:00[UTC][u-ca=iso8601]',
+].forEach((relativeTo) => {
+ const duration = new Temporal.Duration(0, 0, 0, 31);
+ const result = duration.total({ unit: "months", relativeTo });
+ assert.sameValue(result, 1);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-sub-minute-offset.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-sub-minute-offset.js
new file mode 100644
index 0000000000..46f3849688
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-sub-minute-offset.js
@@ -0,0 +1,29 @@
+// |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.duration.prototype.total
+description: relativeTo string accepts trailing zeroes in sub-minute UTC offset
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(1, 0, 0, 0, 24);
+
+let result;
+let relativeTo;
+
+const action = (relativeTo) => instance.total({ unit: "days", relativeTo });
+
+relativeTo = "1970-01-01T00:00-00:45:00[-00:45]";
+result = action(relativeTo);
+assert.sameValue(result, 366, "ISO string offset accepted with zero seconds (string)");
+
+relativeTo = { year: 1970, month: 1, day: 1, offset: "+00:45:00.000000000", timeZone: "+00:45" };
+result = action(relativeTo);
+assert.sameValue(result, 366, "ISO string offset accepted with zero seconds (property bag)");
+
+relativeTo = "1970-01-01T00:00+00:44:30.123456789[+00:45]";
+assert.throws(RangeError, () => action(relativeTo), "rounding is not accepted between ISO offset and time zone");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-undefined-throw-on-calendar-units.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-undefined-throw-on-calendar-units.js
new file mode 100644
index 0000000000..47a8fdb33f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-undefined-throw-on-calendar-units.js
@@ -0,0 +1,35 @@
+// |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.duration.prototype.total
+description: >
+ The relativeTo option is required when the Duration contains years, months,
+ or weeks, and unit is days; or unit is weeks or months
+features: [Temporal, arrow-function]
+---*/
+
+const oneYear = new Temporal.Duration(1);
+const oneMonth = new Temporal.Duration(0, 1);
+const oneWeek = new Temporal.Duration(0, 0, 1);
+const oneDay = new Temporal.Duration(0, 0, 0, 1);
+
+const options = { unit: "days" };
+assert.sameValue(oneDay.total(options), 1, "days do not require relativeTo");
+assert.sameValue(oneDay.total("days"), 1, "days do not require relativeTo (string shorthand)");
+assert.throws(RangeError, () => oneWeek.total(options), "total days of weeks requires relativeTo");
+assert.throws(RangeError, () => oneWeek.total("days"), "total days of weeks requires relativeTo (string shorthand)");
+assert.throws(RangeError, () => oneMonth.total(options), "total days of months requires relativeTo");
+assert.throws(RangeError, () => oneMonth.total("days"), "total days of months requires relativeTo (string shorthand)");
+assert.throws(RangeError, () => oneYear.total(options), "total days of years requires relativeTo");
+assert.throws(RangeError, () => oneYear.total("days"), "total days of years requires relativeTo (string shorthand)");
+
+["months", "weeks"].forEach((unit) => {
+ [oneDay, oneWeek, oneMonth, oneYear].forEach((duration) => {
+ assert.throws(RangeError, () => duration.total({ unit }), `${duration} total ${unit} requires relativeTo`);
+ assert.throws(RangeError, () => duration.total(unit), `${duration} total ${unit} requires relativeTo (string shorthand)`);
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-wrong-type.js
new file mode 100644
index 0000000000..1cb6e1bf8d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-wrong-type.js
@@ -0,0 +1,50 @@
+// |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.duration.prototype.total
+description: >
+ Appropriate error thrown when relativeTo cannot be converted to a valid
+ relativeTo string or property bag
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone('UTC');
+const instance = new Temporal.Duration(1, 0, 0, 0, 24);
+
+const primitiveTests = [
+ [undefined, 'undefined'],
+ [null, 'null'],
+ [true, 'boolean'],
+ ['', 'empty string'],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, 'bigint']
+];
+
+for (const [relativeTo, description] of primitiveTests) {
+ assert.throws(
+ typeof relativeTo === 'string' || typeof relativeTo === 'undefined' ? RangeError : TypeError,
+ () => instance.total({ unit: 'days', relativeTo }),
+ `${description} does not convert to a valid ISO string (first argument)`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), 'symbol'],
+ [{}, 'plain object'],
+ [Temporal.PlainDate, 'Temporal.PlainDate, object'],
+ [Temporal.PlainDate.prototype, 'Temporal.PlainDate.prototype, object'],
+ [Temporal.ZonedDateTime, 'Temporal.ZonedDateTime, object'],
+ [Temporal.ZonedDateTime.prototype, 'Temporal.ZonedDateTime.prototype, object']
+];
+
+for (const [relativeTo, description] of typeErrorTests) {
+ assert.throws(
+ TypeError,
+ () => instance.total({ unit: 'days', relativeTo }),
+ `${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/Duration/prototype/total/relativeto-zoneddatetime-negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-negative-epochnanoseconds.js
new file mode 100644
index 0000000000..a52a8c4943
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-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.duration.prototype.total
+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]
+---*/
+
+const relativeTo = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC");
+const duration = new Temporal.Duration(0, 0, 0, 1);
+
+// This code path shows up anywhere we convert an exact time, before the Unix
+// epoch, with nonzero microseconds or nanoseconds, into a wall time; in this
+// case via relativeTo.
+
+const result = duration.total({ relativeTo, unit: "days" });
+assert.sameValue(result, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-normalized-time-duration-to-days-range-errors.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-normalized-time-duration-to-days-range-errors.js
new file mode 100644
index 0000000000..d415117f89
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-normalized-time-duration-to-days-range-errors.js
@@ -0,0 +1,128 @@
+// |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.duration.prototype.total
+description: >
+ Abstract operation NormalizedTimeDurationToDays can throw four different
+ RangeErrors.
+info: |
+ NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ , precalculatedPlainDateTime ] )
+ 22. If days < 0 and sign = 1, throw a RangeError exception.
+ 23. If days > 0 and sign = -1, throw a RangeError exception.
+ ...
+ 25. If NormalizedTimeDurationSign(_norm_) = 1 and sign = -1, throw a RangeError exception.
+ ...
+ 28. If dayLength ≥ 2⁵³, throw a RangeError exception.
+features: [Temporal, BigInt]
+includes: [temporalHelpers.js]
+---*/
+
+const oneNsDuration = Temporal.Duration.from({ nanoseconds: 1 });
+const negOneNsDuration = Temporal.Duration.from({ nanoseconds: -1 });
+const dayNs = 86_400_000_000_000;
+const epochInstant = new Temporal.Instant(0n);
+
+function timeZoneSubstituteValues(
+ getPossibleInstantsFor,
+ getOffsetNanosecondsFor
+) {
+ const tz = new Temporal.TimeZone("UTC");
+ TemporalHelpers.substituteMethod(
+ tz,
+ "getPossibleInstantsFor",
+ getPossibleInstantsFor
+ );
+ TemporalHelpers.substituteMethod(
+ tz,
+ "getOffsetNanosecondsFor",
+ getOffsetNanosecondsFor
+ );
+ return tz;
+}
+
+// Step 22: days < 0 and sign = 1
+let zdt = new Temporal.ZonedDateTime(
+ 0n, // Sets _startNs_ to 0
+ timeZoneSubstituteValues(
+ [[epochInstant]], // Returned in step 16, setting _relativeResult_
+ [
+ TemporalHelpers.SUBSTITUTE_SKIP, // Pre-conversion in Duration.p.total
+ dayNs - 1, // Returned in step 8, setting _startDateTime_
+ -dayNs + 1, // Returned in step 9, setting _endDateTime_
+ ]
+ )
+);
+assert.throws(RangeError, () =>
+ // Using 1ns duration _nanoseconds_ to 1 and _sign_ to 1
+ oneNsDuration.total({
+ relativeTo: zdt,
+ unit: "day",
+ }),
+ "RangeError when days < 0 and sign = 1"
+);
+
+// Step 23: days > 0 and sign = -1
+zdt = new Temporal.ZonedDateTime(
+ 0n, // Sets _startNs_ to 0
+ timeZoneSubstituteValues(
+ [[epochInstant]], // Returned in step 16, setting _relativeResult_
+ [
+ TemporalHelpers.SUBSTITUTE_SKIP, // Pre-conversion in Duration.p.total
+ -dayNs + 1, // Returned in step 8, setting _startDateTime_
+ dayNs - 1, // Returned in step 9, setting _endDateTime_
+ ]
+ )
+);
+assert.throws(RangeError, () =>
+ // Using -1ns duration sets _nanoseconds_ to -1 and _sign_ to -1
+ negOneNsDuration.total({
+ relativeTo: zdt,
+ unit: "day",
+ }),
+ "RangeError when days > 0 and sign = -1"
+);
+
+// Step 25: nanoseconds > 0 and sign = -1
+zdt = new Temporal.ZonedDateTime(
+ 0n, // Sets _startNs_ to 0
+ timeZoneSubstituteValues(
+ [
+ [new Temporal.Instant(-2n)], // Returned in step 16, setting _relativeResult_
+ [new Temporal.Instant(-4n)], // Returned in step 21.a, setting _oneDayFarther_
+ ],
+ [
+ TemporalHelpers.SUBSTITUTE_SKIP, // pre-conversion in Duration.p.total
+ dayNs - 1, // Returned in step 8, setting _startDateTime_
+ -dayNs + 1, // Returned in step 9, setting _endDateTime_
+ ]
+ )
+);
+assert.throws(RangeError, () =>
+ // Using -1ns duration sets _nanoseconds_ to -1 and _sign_ to -1
+ negOneNsDuration.total({
+ relativeTo: zdt,
+ unit: "day",
+ }),
+ "RangeError when nanoseconds > 0 and sign = -1"
+);
+
+// Step 28: day length is an unsafe integer
+zdt = new Temporal.ZonedDateTime(
+ 0n,
+ timeZoneSubstituteValues(
+ // Not called in step 16 because _days_ = 0
+ // Returned in step 21.a, making _oneDayFarther_ 2^53 ns later than _relativeResult_
+ [[new Temporal.Instant(2n ** 53n)]],
+ []
+ )
+);
+assert.throws(RangeError, () =>
+ oneNsDuration.total({
+ relativeTo: zdt,
+ unit: "days",
+ }),
+ "Should throw RangeError when time zone calculates an outrageous day length"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..4be071d057
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-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.duration.prototype.total
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => duration.total({ unit: "seconds", relativeTo: datetime }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..0534adf10f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-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.duration.prototype.total
+description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable
+features: [BigInt, Symbol, Temporal, arrow-function]
+---*/
+
+[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => {
+ const timeZone = new Temporal.TimeZone("UTC");
+ const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => duration.total({ unit: "seconds", relativeTo: datetime }),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..10d39099e8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-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.duration.prototype.total
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => duration.total({ unit: "seconds", relativeTo: datetime }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..26cd533b6c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-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.duration.prototype.total
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => duration.total({ unit: "seconds", relativeTo: datetime }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-with-fractional-days-different-sign.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-with-fractional-days-different-sign.js
new file mode 100644
index 0000000000..8173ef0b23
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-with-fractional-days-different-sign.js
@@ -0,0 +1,38 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.total
+description: >
+ Relative to a ZonedDateTime with a fractional number of days and different sign.
+features: [Temporal]
+---*/
+
+let duration = Temporal.Duration.from({
+ weeks: 1,
+ days: 0,
+ hours: 1,
+});
+
+let cal = new class extends Temporal.Calendar {
+ #dateAdd = 0;
+
+ dateAdd(date, duration, options) {
+ if (++this.#dateAdd === 1) {
+ duration = "-P1W";
+ }
+ return super.dateAdd(date, duration, options);
+ }
+}("iso8601");
+
+let zdt = new Temporal.ZonedDateTime(0n, "UTC", cal);
+
+let result = duration.total({
+ relativeTo: zdt,
+ unit: "days",
+});
+
+assert.sameValue(result, -7 + 1 / 24);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-with-fractional-days.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-with-fractional-days.js
new file mode 100644
index 0000000000..6f234590f0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-with-fractional-days.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.total
+description: >
+ Relative to a ZonedDateTime with a fractional number of days.
+features: [Temporal]
+---*/
+
+let duration = Temporal.Duration.from({
+ weeks: 1,
+ days: 0,
+ hours: 1,
+});
+
+let zdt = new Temporal.ZonedDateTime(0n, "UTC", "iso8601");
+
+let result = duration.total({
+ relativeTo: zdt,
+ unit: "days",
+});
+
+assert.sameValue(result, 7 + 1 / 24);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/shell.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/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/Duration/prototype/total/timezone-getpossibleinstantsfor-iterable.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/timezone-getpossibleinstantsfor-iterable.js
new file mode 100644
index 0000000000..bd7ca83961
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/timezone-getpossibleinstantsfor-iterable.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.duration.prototype.total
+description: An iterable returned from timeZone.getPossibleInstantsFor is consumed after each call
+info: |
+ sec-temporal.duration.prototype.total steps 4 and 10:
+ 4. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_).
+ 10. Let _balanceResult_ be ? BalanceDuration(_unbalanceResult_.[[Days]], [...], _unbalanceResult_.[[Nanoseconds]], _unit_, _intermediate_).
+ sec-temporal-torelativetemporalobject step 6.d:
+ d. Let _epochNanoseconds_ be ? InterpretISODateTimeOffset(_result_.[[Year]], [...], _result_.[[Nanosecond]], _offsetNs_, _timeZone_, *"compatible"*, *"reject"*).
+ sec-temporal-interpretisodatetimeoffset step 7:
+ 7. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_).
+ sec-temporal-addzoneddatetime step 8:
+ 8. Let _intermediateInstant_ be ? BuiltinTimeZoneGetInstantFor(_timeZone_, _intermediateDateTime_, *"compatible"*).
+ sec-temporal-builtintimezonegetinstantfor step 1:
+ 1. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_).
+ sec-temporal-getpossibleinstantsfor step 2:
+ 2. Let _list_ be ? IterableToList(_possibleInstants_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "2000-01-01T00:00:00", // called once on the input relativeTo if ZonedDateTime
+ "2001-02-09T00:00:00", // called once on the intermediate ZonedDateTime with the calendar parts of the Duration added
+];
+
+TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => {
+ const duration = new Temporal.Duration(1, 1, 1, 1, 1, 1, 1);
+ duration.total({ unit: 'seconds', relativeTo: { year: 2000, month: 1, day: 1, timeZone } });
+}, expected);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/unit-disallowed-units-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/unit-disallowed-units-string.js
new file mode 100644
index 0000000000..ac37e1be6e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/unit-disallowed-units-string.js
@@ -0,0 +1,29 @@
+// |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.duration.prototype.total
+description: Specifically disallowed units for the unit option
+features: [Temporal, arrow-function]
+---*/
+
+const instance = new Temporal.Duration(0, 0, 0, 4, 5, 6, 7, 987, 654, 321);
+const invalidUnits = [
+ "era",
+ "eras",
+];
+invalidUnits.forEach((unit) => {
+ assert.throws(
+ RangeError,
+ () => instance.total({ unit }),
+ `{ unit: "${unit}" } should not be allowed as an argument to total`
+ );
+ assert.throws(
+ RangeError,
+ () => instance.total(unit),
+ `"${unit}" should not be allowed as an argument to total`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/unit-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/unit-invalid-string.js
new file mode 100644
index 0000000000..119d7a5f05
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/unit-invalid-string.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.duration.protoype.total
+description: RangeError thrown when unit option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaldurationtotal step 1:
+ 1. Let _unit_ be ? GetOption(_normalizedOptions_, *"unit"*, « String », « *"year"*, *"years"*, *"month"*, *"months"*, *"week"*, *"weeks"*, *"day"*, *"days"*, *"hour"*, *"hours"*, *"minute"*, *"minutes"*, *"second"*, *"seconds"*, *"millisecond"*, *"milliseconds"*, *"microsecond"*, *"microseconds"*, *"nanosecond"*, *"nanoseconds"* », *undefined*).
+ sec-temporal.duration.protoype.total step 5:
+ 5. Let _unit_ be ? ToTemporalDurationTotalUnit(_options_).
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(0, 0, 0, 1);
+assert.throws(RangeError, () => duration.total({ unit: "other string" }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/unit-plurals-accepted-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/unit-plurals-accepted-string.js
new file mode 100644
index 0000000000..82b7932d1d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/unit-plurals-accepted-string.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.duration.prototype.total
+description: Plural units are accepted as well for the shorthand for the unit option
+includes: [temporalHelpers.js]
+features: [Temporal, arrow-function]
+---*/
+
+const duration = new Temporal.Duration(0, 0, 0, 4, 5, 6, 7, 987, 654, 321);
+const validUnits = [
+ "day",
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+];
+TemporalHelpers.checkPluralUnitsAccepted((unit) => duration.total(unit), validUnits);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/unit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/unit-plurals-accepted.js
new file mode 100644
index 0000000000..17737506cc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/unit-plurals-accepted.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.duration.prototype.total
+description: Plural units are accepted as well for the unit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+const relativeTo = new Temporal.PlainDate(2000, 1, 1);
+const validUnits = [
+ "year",
+ "month",
+ "week",
+ "day",
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+];
+TemporalHelpers.checkPluralUnitsAccepted((unit) => duration.total({ unit, relativeTo }), validUnits);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/unit-string-shorthand-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/unit-string-shorthand-string.js
new file mode 100644
index 0000000000..9aab057420
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/unit-string-shorthand-string.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.duration.prototype.total
+description: String as first argument is equivalent to options bag with unit option
+features: [Temporal, arrow-function]
+---*/
+
+const instance = new Temporal.Duration(0, 0, 0, 4, 5, 6, 7, 987, 654, 321);
+const validUnits = [
+ "day",
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+];
+validUnits.forEach((unit) => {
+ const full = instance.total({ unit });
+ const shorthand = instance.total(unit);
+ assert.sameValue(shorthand, full, `"${unit}" as first argument to total is equivalent to options bag`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/unit-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/unit-wrong-type.js
new file mode 100644
index 0000000000..7fa1a8ba6b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/unit-wrong-type.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.duration.prototype.total
+description: Type conversions for unit option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaldurationtotal step 1:
+ 1. Let _unit_ be ? GetOption(_normalizedOptions_, *"unit"*, « String », « *"year"*, *"years"*, *"month"*, *"months"*, *"week"*, *"weeks"*, *"day"*, *"days"*, *"hour"*, *"hours"*, *"minute"*, *"minutes"*, *"second"*, *"seconds"*, *"millisecond"*, *"milliseconds"*, *"microsecond"*, *"microseconds"*, *"nanosecond"*, *"nanoseconds"* », *undefined*).
+ sec-temporal.duration.protoype.total step 5:
+ 5. Let _unit_ be ? ToTemporalDurationTotalUnit(_options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(0, 0, 0, 1);
+TemporalHelpers.checkStringOptionWrongType("unit", "hour",
+ (unit) => duration.total({ unit }),
+ (result, descr) => assert.sameValue(result, 24, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/year-zero.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/year-zero.js
new file mode 100644
index 0000000000..eb790cce0a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/year-zero.js
@@ -0,0 +1,20 @@
+// |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.duration.prototype.total
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const instance = new Temporal.Duration(1, 0, 0, 0, 24);
+
+let relativeTo = "-000000-11-04T00:00";
+assert.throws(
+ RangeError,
+ () => { instance.total({ unit: "days", relativeTo }); },
+ "reject minus zero as extended year"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/zero-day-length.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/zero-day-length.js
new file mode 100644
index 0000000000..8b93da72eb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/zero-day-length.js
@@ -0,0 +1,43 @@
+// |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.duration.prototype.total
+description: A malicious time zone resulting a day length of zero is handled correctly
+info: |
+ Based on a test by André Bargull.
+
+ RoundDuration step 6:
+ d. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _intermediate_).
+ e. Set _days_ to _days_ + _result_.[[Days]] + _result_.[[Nanoseconds]] / _result_.[[DayLength]].
+
+ NanosecondsToDays steps 19-23:
+ 19. If _days_ < 0 and _sign_ = 1, throw a *RangeError* exception.
+ 20. If _days_ > 0 and _sign_ = -1, throw a *RangeError* exception.
+ 21. If _nanoseconds_ < 0, then
+ a. Assert: sign is -1.
+ 22. If _nanoseconds_ > 0 and _sign_ = -1, throw a *RangeError* exception.
+ 23. Assert: The inequality abs(_nanoseconds_) < abs(_dayLengthNs_) holds.
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(0, 0, 0, 0, -24, 0, 0, 0, 0, -1);
+
+const tz = new class extends Temporal.TimeZone {
+ #getPossibleInstantsForCalls = 0;
+
+ getPossibleInstantsFor(dt) {
+ this.#getPossibleInstantsForCalls++;
+
+ if (this.#getPossibleInstantsForCalls <= 2) {
+ return [new Temporal.Instant(-86400_000_000_000n - 2n)]
+ }
+ return super.getPossibleInstantsFor(dt);
+ }
+}("UTC");
+
+const relativeTo = new Temporal.ZonedDateTime(0n, tz, "iso8601");
+assert.throws(RangeError, () => instance.total({ relativeTo, unit: "days" }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/zero-year-month-week-length.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/zero-year-month-week-length.js
new file mode 100644
index 0000000000..8036c28320
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/zero-year-month-week-length.js
@@ -0,0 +1,34 @@
+// |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.duration.prototype.total
+description: >
+ A malicious calendar resulting in a year, month, or week length of zero is
+ handled correctly
+info: |
+ RoundDuration
+ 10.z. If _oneYearDays_ = 0, throw a *RangeError* exception.
+ ...
+ 11.z. If _oneMonthDays_ = 0, throw a *RangeError* exception.
+ ...
+ 12.s. If _oneWeekDays_ = 0, throw a *RangeError* exception.
+features: [Temporal]
+---*/
+
+const cal = new class extends Temporal.Calendar {
+ dateAdd(date, duration, options) {
+ // Called several times, last call sets oneYear/Month/WeekDays to 0
+ return new Temporal.PlainDate(1970, 1, 1);
+ }
+}("iso8601");
+
+const instance = new Temporal.Duration(1, 0, 0, 0, 0, 0, 0, 0, 0, 1);
+const relativeTo = new Temporal.ZonedDateTime(0n, "UTC", cal);
+
+assert.throws(RangeError, () => instance.total({ relativeTo, unit: "years" }), "zero year length handled correctly");
+assert.throws(RangeError, () => instance.total({ relativeTo, unit: "months" }), "zero month length handled correctly");
+assert.throws(RangeError, () => instance.total({ relativeTo, unit: "weeks" }), "zero week length handled correctly");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/valueOf/basic.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/valueOf/basic.js
new file mode 100644
index 0000000000..dae5b5ea8c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/valueOf/basic.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.duration.prototype.valueof
+description: Basic tests for valueOf().
+features: [Temporal]
+---*/
+
+const d1 = Temporal.Duration.from("P3DT1H");
+const d2 = Temporal.Duration.from("P3DT1H");
+
+assert.throws(TypeError, () => d1.valueOf(), "valueOf");
+assert.throws(TypeError, () => d1 < d1, "<");
+assert.throws(TypeError, () => d1 <= d1, "<=");
+assert.throws(TypeError, () => d1 > d1, ">");
+assert.throws(TypeError, () => d1 >= d1, ">=");
+assert.sameValue(d1 === d1, true, "===");
+assert.sameValue(d1 === d2, false, "===");
+assert.sameValue(d1 !== d1, false, "!==");
+assert.sameValue(d1 !== d2, true, "!==");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/valueOf/branding.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/valueOf/branding.js
new file mode 100644
index 0000000000..f08c11b73a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/valueOf/branding.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.duration.prototype.valueof
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const valueOf = Temporal.Duration.prototype.valueOf;
+
+assert.sameValue(typeof valueOf, "function");
+
+assert.throws(TypeError, () => valueOf.call(undefined), "undefined");
+assert.throws(TypeError, () => valueOf.call(null), "null");
+assert.throws(TypeError, () => valueOf.call(true), "true");
+assert.throws(TypeError, () => valueOf.call(""), "empty string");
+assert.throws(TypeError, () => valueOf.call(Symbol()), "symbol");
+assert.throws(TypeError, () => valueOf.call(1), "1");
+assert.throws(TypeError, () => valueOf.call({}), "plain object");
+assert.throws(TypeError, () => valueOf.call(Temporal.Duration), "Temporal.Duration");
+assert.throws(TypeError, () => valueOf.call(Temporal.Duration.prototype), "Temporal.Duration.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/valueOf/browser.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/valueOf/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/valueOf/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/valueOf/builtin.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/valueOf/builtin.js
new file mode 100644
index 0000000000..6317761ddb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/valueOf/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.duration.prototype.valueof
+description: >
+ Tests that Temporal.Duration.prototype.valueOf
+ 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.Duration.prototype.valueOf),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Duration.prototype.valueOf),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Duration.prototype.valueOf),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Duration.prototype.valueOf.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/valueOf/length.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/valueOf/length.js
new file mode 100644
index 0000000000..82cc80e47d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/valueOf/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.duration.prototype.valueof
+description: Temporal.Duration.prototype.valueOf.length is 0
+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.Duration.prototype.valueOf, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/valueOf/name.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/valueOf/name.js
new file mode 100644
index 0000000000..cd660c25fa
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/valueOf/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.duration.prototype.valueof
+description: Temporal.Duration.prototype.valueOf.name is "valueOf".
+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.Duration.prototype.valueOf, "name", {
+ value: "valueOf",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/valueOf/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/valueOf/not-a-constructor.js
new file mode 100644
index 0000000000..97a56fb68b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/valueOf/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.duration.prototype.valueof
+description: >
+ Temporal.Duration.prototype.valueOf 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.Duration.prototype.valueOf();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Duration.prototype.valueOf), false,
+ "isConstructor(Temporal.Duration.prototype.valueOf)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/valueOf/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/valueOf/prop-desc.js
new file mode 100644
index 0000000000..943914b1b0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/valueOf/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.duration.prototype.valueof
+description: The "valueOf" property of Temporal.Duration.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Duration.prototype.valueOf,
+ "function",
+ "`typeof Duration.prototype.valueOf` is `function`"
+);
+
+verifyProperty(Temporal.Duration.prototype, "valueOf", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/valueOf/shell.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/valueOf/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/valueOf/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/Duration/prototype/weeks/branding.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/weeks/branding.js
new file mode 100644
index 0000000000..8acf1a8545
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/weeks/branding.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-get-temporal.duration.prototype.weeks
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const weeks = Object.getOwnPropertyDescriptor(Temporal.Duration.prototype, "weeks").get;
+
+assert.sameValue(typeof weeks, "function");
+
+assert.throws(TypeError, () => weeks.call(undefined), "undefined");
+assert.throws(TypeError, () => weeks.call(null), "null");
+assert.throws(TypeError, () => weeks.call(true), "true");
+assert.throws(TypeError, () => weeks.call(""), "empty string");
+assert.throws(TypeError, () => weeks.call(Symbol()), "symbol");
+assert.throws(TypeError, () => weeks.call(1), "1");
+assert.throws(TypeError, () => weeks.call({}), "plain object");
+assert.throws(TypeError, () => weeks.call(Temporal.Duration), "Temporal.Duration");
+assert.throws(TypeError, () => weeks.call(Temporal.Duration.prototype), "Temporal.Duration.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/weeks/browser.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/weeks/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/weeks/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/weeks/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/weeks/prop-desc.js
new file mode 100644
index 0000000000..6a4a729afa
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/weeks/prop-desc.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-get-temporal.duration.prototype.weeks
+description: The "weeks" property of Temporal.Duration.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.Duration.prototype, "weeks");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/weeks/shell.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/weeks/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/weeks/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/all-negative.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/all-negative.js
new file mode 100644
index 0000000000..2bd30c5316
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/all-negative.js
@@ -0,0 +1,96 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.with
+description: >
+ Returns a correctly merged object when the argument replaces the fields with
+ all negative values.
+info: |
+ 1. Let duration be the this value.
+ 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]).
+ 3. Let temporalDurationLike be ? ToPartialDuration(temporalDurationLike).
+ 4. If temporalDurationLike.[[Years]] is not undefined, then
+ a. Let years be temporalDurationLike.[[Years]].
+ 5. Else,
+ a. Let years be duration.[[Years]].
+ 6. If temporalDurationLike.[[Months]] is not undefined, then
+ a. Let months be temporalDurationLike.[[Months]].
+ 7. Else,
+ a. Let months be duration.[[Months]].
+ 8. If temporalDurationLike.[[Weeks]] is not undefined, then
+ a. Let weeks be temporalDurationLike.[[Weeks]].
+ 9. Else,
+ a. Let weeks be duration.[[Weeks]].
+ 10. If temporalDurationLike.[[Days]] is not undefined, then
+ a. Let days be temporalDurationLike.[[Days]].
+ 11. Else,
+ a. Let days be duration.[[Days]].
+ 12. If temporalDurationLike.[[Hours]] is not undefined, then
+ a. Let hours be temporalDurationLike.[[Hours]].
+ 13. Else,
+ a. Let hours be duration.[[Hours]].
+ 14. If temporalDurationLike.[[Minutes]] is not undefined, then
+ a. Let minutes be temporalDurationLike.[[Minutes]].
+ 15. Else,
+ a. Let minutes be duration.[[Minutes]].
+ 16. If temporalDurationLike.[[Seconds]] is not undefined, then
+ a. Let seconds be temporalDurationLike.[[Seconds]].
+ 17. Else,
+ a. Let seconds be duration.[[Seconds]].
+ 18. If temporalDurationLike.[[Milliseconds]] is not undefined, then
+ a. Let milliseconds be temporalDurationLike.[[Milliseconds]].
+ 19. Else,
+ a. Let milliseconds be duration.[[Milliseconds]].
+ 20. If temporalDurationLike.[[Microseconds]] is not undefined, then
+ a. Let microseconds be temporalDurationLike.[[Microseconds]].
+ 21. Else,
+ a. Let microseconds be duration.[[Microseconds]].
+ 22. If temporalDurationLike.[[Nanoseconds]] is not undefined, then
+ a. Let nanoseconds be temporalDurationLike.[[Nanoseconds]].
+ 23. Else,
+ a. Let nanoseconds be duration.[[Nanoseconds]].
+ 24. Return ? CreateTemporalDuration(years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds).
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const argAllNegative = {
+ years: -9,
+ months: -8,
+ weeks: -7,
+ days: -6,
+ hours: -5,
+ minutes: -4,
+ seconds: -3,
+ milliseconds: -2,
+ microseconds: -1,
+ nanoseconds: -10,
+};
+
+const d1 = new Temporal.Duration();
+TemporalHelpers.assertDuration(
+ d1.with(argAllNegative), -9, -8, -7, -6, -5, -4, -3, -2, -1, -10,
+ "replace all zeroes with all negative"
+);
+
+const d2 = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
+TemporalHelpers.assertDuration(
+ d2.with(argAllNegative), -9, -8, -7, -6, -5, -4, -3, -2, -1, -10,
+ "replace all positive with all negative"
+);
+
+const d3 = new Temporal.Duration(1e5, 2e5, 3e5, 4e5, 5e5, 6e5, 7e5, 8e5, 9e5, 10e5);
+TemporalHelpers.assertDuration(
+ d3.with(argAllNegative), -9, -8, -7, -6, -5, -4, -3, -2, -1, -10,
+ "replace all positive large numbers with all negative"
+);
+
+const d4 = new Temporal.Duration(-1, -2, -3, -4, -5, -6, -7, -8, -9, -10);
+TemporalHelpers.assertDuration(
+ d4.with(argAllNegative), -9, -8, -7, -6, -5, -4, -3, -2, -1, -10,
+ "replace all negative with all negative"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/all-positive.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/all-positive.js
new file mode 100644
index 0000000000..c73192272b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/all-positive.js
@@ -0,0 +1,95 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.with
+description: >
+ Returns a correctly merged object when the argument replaces the fields with
+ all positive values.
+info: |
+ 1. Let duration be the this value.
+ 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]).
+ 3. Let temporalDurationLike be ? ToPartialDuration(temporalDurationLike).
+ 4. If temporalDurationLike.[[Years]] is not undefined, then
+ a. Let years be temporalDurationLike.[[Years]].
+ 5. Else,
+ a. Let years be duration.[[Years]].
+ 6. If temporalDurationLike.[[Months]] is not undefined, then
+ a. Let months be temporalDurationLike.[[Months]].
+ 7. Else,
+ a. Let months be duration.[[Months]].
+ 8. If temporalDurationLike.[[Weeks]] is not undefined, then
+ a. Let weeks be temporalDurationLike.[[Weeks]].
+ 9. Else,
+ a. Let weeks be duration.[[Weeks]].
+ 10. If temporalDurationLike.[[Days]] is not undefined, then
+ a. Let days be temporalDurationLike.[[Days]].
+ 11. Else,
+ a. Let days be duration.[[Days]].
+ 12. If temporalDurationLike.[[Hours]] is not undefined, then
+ a. Let hours be temporalDurationLike.[[Hours]].
+ 13. Else,
+ a. Let hours be duration.[[Hours]].
+ 14. If temporalDurationLike.[[Minutes]] is not undefined, then
+ a. Let minutes be temporalDurationLike.[[Minutes]].
+ 15. Else,
+ a. Let minutes be duration.[[Minutes]].
+ 16. If temporalDurationLike.[[Seconds]] is not undefined, then
+ a. Let seconds be temporalDurationLike.[[Seconds]].
+ 17. Else,
+ a. Let seconds be duration.[[Seconds]].
+ 18. If temporalDurationLike.[[Milliseconds]] is not undefined, then
+ a. Let milliseconds be temporalDurationLike.[[Milliseconds]].
+ 19. Else,
+ a. Let milliseconds be duration.[[Milliseconds]].
+ 20. If temporalDurationLike.[[Microseconds]] is not undefined, then
+ a. Let microseconds be temporalDurationLike.[[Microseconds]].
+ 21. Else,
+ a. Let microseconds be duration.[[Microseconds]].
+ 22. If temporalDurationLike.[[Nanoseconds]] is not undefined, then
+ a. Let nanoseconds be temporalDurationLike.[[Nanoseconds]].
+ 23. Else,
+ a. Let nanoseconds be duration.[[Nanoseconds]].
+ 24. Return ? CreateTemporalDuration(years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds).
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const argAllPositive = {
+ years: 9,
+ months: 8,
+ weeks: 7,
+ days: 6,
+ hours: 5,
+ minutes: 4,
+ seconds: 3,
+ milliseconds: 2,
+ microseconds: 1,
+ nanoseconds: 10,
+};
+
+const d1 = new Temporal.Duration();
+TemporalHelpers.assertDuration(
+ d1.with(argAllPositive), 9, 8, 7, 6, 5, 4, 3, 2, 1, 10,
+ "replace all zeroes with all positive"
+);
+
+const d2 = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
+TemporalHelpers.assertDuration(
+ d2.with(argAllPositive), 9, 8, 7, 6, 5, 4, 3, 2, 1, 10,
+ "replace all positive with all positive");
+
+const d3 = new Temporal.Duration(1e5, 2e5, 3e5, 4e5, 5e5, 6e5, 7e5, 8e5, 9e5, 10e5);
+TemporalHelpers.assertDuration(
+ d3.with(argAllPositive), 9, 8, 7, 6, 5, 4, 3, 2, 1, 10,
+ "replace all positive large numbers with all positive"
+);
+
+const d4 = new Temporal.Duration(-1, -2, -3, -4, -5, -6, -7, -8, -9, -10);
+TemporalHelpers.assertDuration(
+ d4.with(argAllPositive), 9, 8, 7, 6, 5, 4, 3, 2, 1, 10,
+ "replace all negative with all positive"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/argument-invalid-property.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/argument-invalid-property.js
new file mode 100644
index 0000000000..9924840eb1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/argument-invalid-property.js
@@ -0,0 +1,31 @@
+// |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.duration.prototype.with
+description: temporalDurationLike object must contain at least one correctly spelled property
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(0, 0, 0, 1, 2, 3, 4, 987, 654, 321);
+
+assert.throws(
+ TypeError,
+ () => instance.with({}),
+ "Throws TypeError if no property is present"
+);
+
+assert.throws(
+ TypeError,
+ () => instance.with({ nonsense: true }),
+ "Throws TypeError if no recognized property is present"
+);
+
+assert.throws(
+ TypeError,
+ () => instance.with({ sign: 1 }),
+ "Sign property is not recognized"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/argument-mixed-sign.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/argument-mixed-sign.js
new file mode 100644
index 0000000000..827412d397
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/argument-mixed-sign.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.duration.prototype.with
+description: Positive and negative values in the temporalDurationLike argument are not acceptable
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(0, 0, 0, 1, 2, 3, 4, 987, 654, 321);
+
+assert.throws(
+ RangeError,
+ () => instance.with({ hours: 1, minutes: -30 }),
+ `mixed positive and negative values always throw`
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/argument-not-object.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/argument-not-object.js
new file mode 100644
index 0000000000..8259ef2f0f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/argument-not-object.js
@@ -0,0 +1,23 @@
+// |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.duration.prototype.with
+description: Passing a primitive other than string to with() throws
+features: [Symbol, Temporal]
+---*/
+
+const instance = new Temporal.Duration(0, 0, 0, 1, 2, 3, 4, 987, 654, 321);
+assert.throws(TypeError, () => instance.with(undefined), "undefined");
+assert.throws(TypeError, () => instance.with(null), "null");
+assert.throws(TypeError, () => instance.with(true), "boolean");
+assert.throws(TypeError, () => instance.with(""), "empty string");
+assert.throws(TypeError, () => instance.with(Symbol()), "Symbol");
+assert.throws(TypeError, () => instance.with(7), "number");
+assert.throws(TypeError, () => instance.with(7n), "bigint");
+assert.throws(TypeError, () => instance.with([]), "array");
+assert.throws(TypeError, () => instance.with(() => {}), "function");
+assert.throws(TypeError, () => instance.with("string"), "string");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/argument-singular-properties.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/argument-singular-properties.js
new file mode 100644
index 0000000000..14da8fe2ba
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/argument-singular-properties.js
@@ -0,0 +1,31 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.with
+description: Singular properties in the property bag are always ignored
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(0, 0, 0, 1, 2, 3, 4, 987, 654, 321);
+
+[
+ { year: 1 },
+ { month: 2 },
+ { week: 3 },
+ { day: 4 },
+ { hour: 5 },
+ { minute: 6 },
+ { second: 7 },
+ { millisecond: 8 },
+ { microsecond: 9 },
+ { nanosecond: 10 },
+].forEach((badObject) => {
+ assert.throws(TypeError, () => instance.with(badObject),
+ "Throw TypeError if temporalDurationLike is not valid");
+});
+
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/branding.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/branding.js
new file mode 100644
index 0000000000..564c5b4bbd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/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.duration.prototype.with
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const with_ = Temporal.Duration.prototype.with;
+
+assert.sameValue(typeof with_, "function");
+
+const args = [{ years: 3 }];
+
+assert.throws(TypeError, () => with_.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => with_.apply(null, args), "null");
+assert.throws(TypeError, () => with_.apply(true, args), "true");
+assert.throws(TypeError, () => with_.apply("", args), "empty string");
+assert.throws(TypeError, () => with_.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => with_.apply(1, args), "1");
+assert.throws(TypeError, () => with_.apply({}, args), "plain object");
+assert.throws(TypeError, () => with_.apply(Temporal.Duration, args), "Temporal.Duration");
+assert.throws(TypeError, () => with_.apply(Temporal.Duration.prototype, args), "Temporal.Duration.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/browser.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/builtin.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/builtin.js
new file mode 100644
index 0000000000..c5d5792b69
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/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.duration.prototype.with
+description: >
+ Tests that Temporal.Duration.prototype.with
+ 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.Duration.prototype.with),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Duration.prototype.with),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Duration.prototype.with),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Duration.prototype.with.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/copy-properties-not-undefined.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/copy-properties-not-undefined.js
new file mode 100644
index 0000000000..7ef5c09408
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/copy-properties-not-undefined.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.duration.prototype.with
+description: PreparePartialTemporalFields copies only defined properties of source object
+info: |
+ 4. For each value _property_ of _fieldNames_, do
+ a. Let _value_ be ? Get(_fields_, _property_).
+ b. If _value_ is not *undefined*, then
+ ...
+ iii. Perform ! CreateDataPropertyOrThrow(_result_, _property_, _value_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const d = new Temporal.Duration(9, 8, 7, 6, 5, 4, 3, 2, 1, 0);
+
+TemporalHelpers.assertDuration(
+ d.with({ minutes: 11, hours: 6, months: undefined }),
+ 9, 8, 7, 6, 6, 11, 3, 2, 1, 0,
+ "only the properties that are present and defined in the plain object are copied"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..691cd83302
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/infinity-throws-rangeerror.js
@@ -0,0 +1,33 @@
+// |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.
+
+/*---
+description: Temporal.Duration.prototype.with handles a property bag if any value is Infinity
+esid: sec-temporal.duration.prototype.with
+features: [Temporal]
+---*/
+
+const fields = ['years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds', 'milliseconds', 'microseconds', 'nanoseconds'];
+
+const instance = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+
+fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.with({ [field]: Infinity }));
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return Infinity;
+ }
+};
+
+fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => instance.with({ [field]: obj }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/length.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/length.js
new file mode 100644
index 0000000000..f274ee181f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/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.duration.prototype.with
+description: Temporal.Duration.prototype.with.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.Duration.prototype.with, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/name.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/name.js
new file mode 100644
index 0000000000..a62a0ac66a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/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.duration.prototype.with
+description: Temporal.Duration.prototype.with.name is "with".
+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.Duration.prototype.with, "name", {
+ value: "with",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/negative-infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/negative-infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..a84e98f6e0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/negative-infinity-throws-rangeerror.js
@@ -0,0 +1,33 @@
+// |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.
+
+/*---
+description: Temporal.Duration.prototype.with throws a RangeError if any value in a property bag is -Infinity
+esid: sec-temporal.duration.prototype.with
+features: [Temporal]
+---*/
+
+const fields = ['years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds', 'milliseconds', 'microseconds', 'nanoseconds'];
+
+const instance = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+
+fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.with({ [field]: -Infinity }));
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return -Infinity;
+ }
+};
+
+fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => instance.with({ [field]: obj }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/non-integer-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/non-integer-throws-rangeerror.js
new file mode 100644
index 0000000000..879fe38d3e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/non-integer-throws-rangeerror.js
@@ -0,0 +1,29 @@
+// |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.duration.prototype.with
+description: A non-integer value for any recognized property in the property bag, throws a RangeError
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(0, 0, 0, 1, 2, 3, 4, 987, 654, 321);
+const fields = [
+ "years",
+ "months",
+ "weeks",
+ "days",
+ "hours",
+ "minutes",
+ "seconds",
+ "milliseconds",
+ "microseconds",
+ "nanoseconds",
+];
+fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.with({ [field]: 1.5 }));
+ assert.throws(RangeError, () => instance.with({ [field]: -1.5 }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/not-a-constructor.js
new file mode 100644
index 0000000000..22922b99d3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/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.duration.prototype.with
+description: >
+ Temporal.Duration.prototype.with 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.Duration.prototype.with();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Duration.prototype.with), false,
+ "isConstructor(Temporal.Duration.prototype.with)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/order-of-operations.js
new file mode 100644
index 0000000000..17024f8a5a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/order-of-operations.js
@@ -0,0 +1,62 @@
+// |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.duration.prototype.with
+description: Properties on an object passed to with() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+const expected = [
+ "get fields.days",
+ "get fields.days.valueOf",
+ "call fields.days.valueOf",
+ "get fields.hours",
+ "get fields.hours.valueOf",
+ "call fields.hours.valueOf",
+ "get fields.microseconds",
+ "get fields.microseconds.valueOf",
+ "call fields.microseconds.valueOf",
+ "get fields.milliseconds",
+ "get fields.milliseconds.valueOf",
+ "call fields.milliseconds.valueOf",
+ "get fields.minutes",
+ "get fields.minutes.valueOf",
+ "call fields.minutes.valueOf",
+ "get fields.months",
+ "get fields.months.valueOf",
+ "call fields.months.valueOf",
+ "get fields.nanoseconds",
+ "get fields.nanoseconds.valueOf",
+ "call fields.nanoseconds.valueOf",
+ "get fields.seconds",
+ "get fields.seconds.valueOf",
+ "call fields.seconds.valueOf",
+ "get fields.weeks",
+ "get fields.weeks.valueOf",
+ "call fields.weeks.valueOf",
+ "get fields.years",
+ "get fields.years.valueOf",
+ "call fields.years.valueOf",
+];
+const actual = [];
+const fields = TemporalHelpers.propertyBagObserver(actual, {
+ years: 1,
+ months: 1,
+ weeks: 1,
+ days: 1,
+ hours: 1,
+ minutes: 1,
+ seconds: 1,
+ milliseconds: 1,
+ microseconds: 1,
+ nanoseconds: 1,
+}, "fields");
+const result = instance.with(fields);
+TemporalHelpers.assertDuration(result, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1);
+assert.compareArray(actual, expected, "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/partial-positive.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/partial-positive.js
new file mode 100644
index 0000000000..62a17262d8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/partial-positive.js
@@ -0,0 +1,89 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.with
+description: >
+ Returns a correctly merged object when the argument replaces only some of the
+ fields with positive values.
+info: |
+ 1. Let duration be the this value.
+ 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]).
+ 3. Let temporalDurationLike be ? ToPartialDuration(temporalDurationLike).
+ 4. If temporalDurationLike.[[Years]] is not undefined, then
+ a. Let years be temporalDurationLike.[[Years]].
+ 5. Else,
+ a. Let years be duration.[[Years]].
+ 6. If temporalDurationLike.[[Months]] is not undefined, then
+ a. Let months be temporalDurationLike.[[Months]].
+ 7. Else,
+ a. Let months be duration.[[Months]].
+ 8. If temporalDurationLike.[[Weeks]] is not undefined, then
+ a. Let weeks be temporalDurationLike.[[Weeks]].
+ 9. Else,
+ a. Let weeks be duration.[[Weeks]].
+ 10. If temporalDurationLike.[[Days]] is not undefined, then
+ a. Let days be temporalDurationLike.[[Days]].
+ 11. Else,
+ a. Let days be duration.[[Days]].
+ 12. If temporalDurationLike.[[Hours]] is not undefined, then
+ a. Let hours be temporalDurationLike.[[Hours]].
+ 13. Else,
+ a. Let hours be duration.[[Hours]].
+ 14. If temporalDurationLike.[[Minutes]] is not undefined, then
+ a. Let minutes be temporalDurationLike.[[Minutes]].
+ 15. Else,
+ a. Let minutes be duration.[[Minutes]].
+ 16. If temporalDurationLike.[[Seconds]] is not undefined, then
+ a. Let seconds be temporalDurationLike.[[Seconds]].
+ 17. Else,
+ a. Let seconds be duration.[[Seconds]].
+ 18. If temporalDurationLike.[[Milliseconds]] is not undefined, then
+ a. Let milliseconds be temporalDurationLike.[[Milliseconds]].
+ 19. Else,
+ a. Let milliseconds be duration.[[Milliseconds]].
+ 20. If temporalDurationLike.[[Microseconds]] is not undefined, then
+ a. Let microseconds be temporalDurationLike.[[Microseconds]].
+ 21. Else,
+ a. Let microseconds be duration.[[Microseconds]].
+ 22. If temporalDurationLike.[[Nanoseconds]] is not undefined, then
+ a. Let nanoseconds be temporalDurationLike.[[Nanoseconds]].
+ 23. Else,
+ a. Let nanoseconds be duration.[[Nanoseconds]].
+ 24. Return ? CreateTemporalDuration(years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds).
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const durationlike1 = { years: 9, hours: 5 };
+const durationlike2 = { months: 8, minutes: 4 };
+const durationlike3 = { weeks: 7, seconds: 3 };
+const durationlike4 = { days: 6, milliseconds: 2 };
+const durationlike5 = { microseconds: 987, nanoseconds: 123 };
+
+const d1 = new Temporal.Duration();
+TemporalHelpers.assertDuration(
+ d1.with(durationlike1), 9, 0, 0, 0, 5, 0, 0, 0, 0, 0, "replace all zeroes with years and hours");
+TemporalHelpers.assertDuration(
+ d1.with(durationlike2), 0, 8, 0, 0, 0, 4, 0, 0, 0, 0, "replace all zeroes wtih months and minutes");
+TemporalHelpers.assertDuration(
+ d1.with(durationlike3), 0, 0, 7, 0, 0, 0, 3, 0, 0, 0, "replace all zeroes with weeks and seconds");
+TemporalHelpers.assertDuration(
+ d1.with(durationlike4), 0, 0, 0, 6, 0, 0, 0, 2, 0, 0, "replace all zeroes with days and milliseconds");
+TemporalHelpers.assertDuration(
+ d1.with(durationlike5), 0, 0, 0, 0, 0, 0, 0, 0, 987, 123, "replace all zeroes with microseconds and nanoseconds");
+
+const d2 = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
+TemporalHelpers.assertDuration(
+ d2.with(durationlike1), 9, 2, 3, 4, 5, 6, 7, 8, 9, 10, "replace all positive with years and hours");
+TemporalHelpers.assertDuration(
+ d2.with(durationlike2), 1, 8, 3, 4, 5, 4, 7, 8, 9, 10, "replace all positive with months and minutes");
+TemporalHelpers.assertDuration(
+ d2.with(durationlike3), 1, 2, 7, 4, 5, 6, 3, 8, 9, 10, "replace all positive with weeks and seconds");
+TemporalHelpers.assertDuration(
+ d2.with(durationlike4), 1, 2, 3, 6, 5, 6, 7, 2, 9, 10, "replace all positive with days and milliseconds");
+TemporalHelpers.assertDuration(
+ d2.with(durationlike5), 1, 2, 3, 4, 5, 6, 7, 8, 987, 123, "replace all positive with microseconds and nanoseconds");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/prop-desc.js
new file mode 100644
index 0000000000..cde2a62cdd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/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.duration.prototype.with
+description: The "with" property of Temporal.Duration.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Duration.prototype.with,
+ "function",
+ "`typeof Duration.prototype.with` is `function`"
+);
+
+verifyProperty(Temporal.Duration.prototype, "with", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/shell.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/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/Duration/prototype/with/sign-conflict-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/sign-conflict-throws-rangeerror.js
new file mode 100644
index 0000000000..f388721eaf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/sign-conflict-throws-rangeerror.js
@@ -0,0 +1,31 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.with
+description: Throw RangeError if the resulting duration has mixed signs
+info: |
+ 24. Return ? CreateTemporalDuration(years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds).
+features: [Temporal]
+---*/
+
+const d1 = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
+const d2 = new Temporal.Duration(-1, -2, -3, -4, -5, -6, -7, -8, -9, -10);
+const fields = ["years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"];
+
+fields.forEach((field) => {
+ assert.throws(
+ RangeError,
+ () => d1.with({ [field]: -1 }),
+ `sign in argument { ${field}: -1 } conflicting with sign of duration should throw RangeError`
+ );
+
+ assert.throws(
+ RangeError,
+ () => d2.with({ [field]: 1 }),
+ `sign in argument { ${field}: 1 } conflicting with sign of duration should throw RangeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/sign-replace.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/sign-replace.js
new file mode 100644
index 0000000000..fe182a4737
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/sign-replace.js
@@ -0,0 +1,18 @@
+// |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.duration.prototype.with
+description: Replacing the sign is supported.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const d = Temporal.Duration.from({ years: 5, days: 1 });
+assert.sameValue(d.sign, 1, "original sign");
+const d2 = d.with({ years: -1, days: 0, minutes: -1 });
+assert.sameValue(d2.sign, -1, "new sign");
+TemporalHelpers.assertDuration(d2, -1, 0, 0, 0, 0, -1, 0, 0, 0, 0);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/subclassing-ignored.js
new file mode 100644
index 0000000000..11576633b9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/subclassing-ignored.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.duration.prototype.with
+description: Objects of a subclass are never created as return values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.Duration,
+ [0, 0, 0, 4, 5, 6, 7, 987, 654, 321],
+ "with",
+ [{ nanoseconds: 1 }],
+ (result) => TemporalHelpers.assertDuration(result, 0, 0, 0, 4, 5, 6, 7, 987, 654, 1),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/years/branding.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/years/branding.js
new file mode 100644
index 0000000000..b41d476088
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/years/branding.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-get-temporal.duration.prototype.years
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const years = Object.getOwnPropertyDescriptor(Temporal.Duration.prototype, "years").get;
+
+assert.sameValue(typeof years, "function");
+
+assert.throws(TypeError, () => years.call(undefined), "undefined");
+assert.throws(TypeError, () => years.call(null), "null");
+assert.throws(TypeError, () => years.call(true), "true");
+assert.throws(TypeError, () => years.call(""), "empty string");
+assert.throws(TypeError, () => years.call(Symbol()), "symbol");
+assert.throws(TypeError, () => years.call(1), "1");
+assert.throws(TypeError, () => years.call({}), "plain object");
+assert.throws(TypeError, () => years.call(Temporal.Duration), "Temporal.Duration");
+assert.throws(TypeError, () => years.call(Temporal.Duration.prototype), "Temporal.Duration.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/years/browser.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/years/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/years/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/years/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/years/prop-desc.js
new file mode 100644
index 0000000000..b1d473bb2c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/years/prop-desc.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-get-temporal.duration.prototype.years
+description: The "years" property of Temporal.Duration.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.Duration.prototype, "years");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/years/shell.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/years/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/years/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/seconds-undefined.js b/js/src/tests/test262/built-ins/Temporal/Duration/seconds-undefined.js
new file mode 100644
index 0000000000..3a36e3779c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/seconds-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.duration
+description: Undefined arguments should be treated as zero.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const args = [1, 1, 1, 1, 1, 1];
+
+const explicit = new Temporal.Duration(...args, undefined);
+TemporalHelpers.assertDuration(explicit, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, "explicit");
+
+const implicit = new Temporal.Duration(...args);
+TemporalHelpers.assertDuration(implicit, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, "implicit");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/shell.js b/js/src/tests/test262/built-ins/Temporal/Duration/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/subclass.js b/js/src/tests/test262/built-ins/Temporal/Duration/subclass.js
new file mode 100644
index 0000000000..bc413760a3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/subclass.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.duration
+description: Test for Temporal.Duration subclassing.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+class CustomDuration extends Temporal.Duration {
+}
+
+const instance = new CustomDuration(1, 1, 0, 1);
+TemporalHelpers.assertDuration(instance, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0);
+assert.sameValue(Object.getPrototypeOf(instance), CustomDuration.prototype, "Instance of CustomDuration");
+assert(instance instanceof CustomDuration, "Instance of CustomDuration");
+assert(instance instanceof Temporal.Duration, "Instance of Temporal.Duration");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/weeks-undefined.js b/js/src/tests/test262/built-ins/Temporal/Duration/weeks-undefined.js
new file mode 100644
index 0000000000..a8b81b7a84
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/weeks-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.duration
+description: Undefined arguments should be treated as zero.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const args = [1, 1];
+
+const explicit = new Temporal.Duration(...args, undefined);
+TemporalHelpers.assertDuration(explicit, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, "explicit");
+
+const implicit = new Temporal.Duration(...args);
+TemporalHelpers.assertDuration(implicit, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, "implicit");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/years-undefined.js b/js/src/tests/test262/built-ins/Temporal/Duration/years-undefined.js
new file mode 100644
index 0000000000..c415841027
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Duration/years-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.duration
+description: Undefined arguments should be treated as zero.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const args = [];
+
+const explicit = new Temporal.Duration(...args, undefined);
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "explicit");
+
+const implicit = new Temporal.Duration(...args);
+TemporalHelpers.assertDuration(implicit, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "implicit");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/argument.js b/js/src/tests/test262/built-ins/Temporal/Instant/argument.js
new file mode 100644
index 0000000000..8111ee0ddd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/argument.js
@@ -0,0 +1,17 @@
+// |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.instant
+description: TypeError thrown if input is of wrong primitive type
+features: [Symbol, Temporal]
+---*/
+
+assert.throws(TypeError, () => new Temporal.Instant(), "undefined");
+assert.throws(TypeError, () => new Temporal.Instant(undefined), "undefined");
+assert.throws(TypeError, () => new Temporal.Instant(null), "null");
+assert.throws(TypeError, () => new Temporal.Instant(42), "number");
+assert.throws(TypeError, () => new Temporal.Instant(Symbol()), "symbol");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/basic.js b/js/src/tests/test262/built-ins/Temporal/Instant/basic.js
new file mode 100644
index 0000000000..e8d94e069d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/basic.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.instant
+description: Basic functionality of the Temporal.Instant constructor
+features: [Temporal]
+---*/
+
+const bigIntInstant = new Temporal.Instant(217175010123456789n);
+assert(bigIntInstant instanceof Temporal.Instant, "BigInt instanceof");
+assert.sameValue(bigIntInstant.epochSeconds, 217175010, "BigInt epochSeconds");
+assert.sameValue(bigIntInstant.epochMilliseconds, 217175010123, "BigInt epochMilliseconds");
+
+const stringInstant = new Temporal.Instant("217175010123456789");
+assert(stringInstant instanceof Temporal.Instant, "String instanceof");
+assert.sameValue(stringInstant.epochSeconds, 217175010, "String epochSeconds");
+assert.sameValue(stringInstant.epochMilliseconds, 217175010123, "String epochMilliseconds");
+
+assert.throws(SyntaxError, () => new Temporal.Instant("abc123"), "invalid BigInt syntax");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/browser.js b/js/src/tests/test262/built-ins/Temporal/Instant/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/builtin.js b/js/src/tests/test262/built-ins/Temporal/Instant/builtin.js
new file mode 100644
index 0000000000..702331d372
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/builtin.js
@@ -0,0 +1,30 @@
+// |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.instant
+description: Tests that Temporal.Instant meets the requirements for built-in objects
+info: |
+ 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.Instant),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Instant),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Instant),
+ Function.prototype, "prototype");
+
+assert.sameValue(typeof Temporal.Instant.prototype,
+ "object", "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/compare/argument-object-tostring.js b/js/src/tests/test262/built-ins/Temporal/Instant/compare/argument-object-tostring.js
new file mode 100644
index 0000000000..22bec7e4d6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/compare/argument-object-tostring.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.instant.compare
+description: Object is converted to a string, then to Temporal.Instant
+features: [Temporal]
+---*/
+
+const epoch = new Temporal.Instant(0n);
+
+const arg = {};
+assert.throws(RangeError, () => Temporal.Instant.compare(arg, epoch), "[object Object] is not a valid ISO string (first argument)");
+assert.throws(RangeError, () => Temporal.Instant.compare(epoch, arg), "[object Object] is not a valid ISO string (second argument)");
+
+arg.toString = function() {
+ return "1970-01-01T00:00Z";
+};
+const result1 = Temporal.Instant.compare(arg, epoch);
+assert.sameValue(result1, 0, "result of toString is interpreted as ISO string (first argument)");
+const result2 = Temporal.Instant.compare(epoch, arg);
+assert.sameValue(result2, 0, "result of toString is interpreted as ISO string (second argument)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/compare/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/Instant/compare/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..4373e78b3a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/compare/argument-string-calendar-annotation.js
@@ -0,0 +1,31 @@
+// |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.instant.compare
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["1970-01-01T00:00Z[u-ca=iso8601]", "without time zone"],
+ ["1970-01-01T00:00Z[UTC][u-ca=gregory]", "with time zone"],
+ ["1970-01-01T00:00Z[!u-ca=hebrew]", "with ! and no time zone"],
+ ["1970-01-01T00:00Z[UTC][!u-ca=chinese]", "with ! and time zone"],
+ ["1970-01-01T00:00Z[u-ca=discord]", "annotation is ignored"],
+ ["1970-01-01T00:00Z[!u-ca=discord]", "annotation with ! is ignored"],
+ ["1970-01-01T00:00Z[u-ca=iso8601][u-ca=discord]", "two annotations are ignored"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.Instant.compare(arg, arg);
+
+ assert.sameValue(
+ result,
+ 0,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/compare/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/Instant/compare/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..304958ba97
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/compare/argument-string-critical-unknown-annotation.js
@@ -0,0 +1,34 @@
+// |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.instant.compare
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01T00:00Z[!foo=bar]",
+ "1970-01-01T00:00Z[UTC][!foo=bar]",
+ "1970-01-01T00:00Z[u-ca=iso8601][!foo=bar]",
+ "1970-01-01T00:00Z[UTC][!foo=bar][u-ca=iso8601]",
+ "1970-01-01T00:00Z[foo=bar][!_foo-bar0=Dont-Ignore-This-99999999999]",
+];
+
+const epoch = new Temporal.Instant(0n);
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.Instant.compare(arg, epoch),
+ `reject unknown annotation with critical flag: ${arg} (first argument)`
+ );
+ assert.throws(
+ RangeError,
+ () => Temporal.Instant.compare(epoch, arg),
+ `reject unknown annotation with critical flag: ${arg} (second argument)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/compare/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/Instant/compare/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..5d95d92ccc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/compare/argument-string-date-with-utc-offset.js
@@ -0,0 +1,56 @@
+// |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.instant.compare
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+---*/
+
+const validStrings = [
+ "1970-01-01T00Z",
+ "1970-01-01T00Z[UTC]",
+ "1970-01-01T00Z[!UTC]",
+ "1970-01-01T00Z[Europe/Vienna]",
+ "1970-01-01T00+00:00",
+ "1970-01-01T00+00:00[UTC]",
+ "1970-01-01T00+00:00[!UTC]",
+ "1969-12-31T16-08:00[America/Vancouver]",
+];
+
+for (const arg of validStrings) {
+ const result = Temporal.Instant.compare(arg, arg);
+
+ assert.sameValue(
+ result,
+ 0,
+ `"${arg}" is a valid UTC offset with time for Instant`
+ );
+}
+
+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]",
+];
+const epoch = new Temporal.Instant(0n);
+
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => Temporal.Instant.compare(arg, epoch),
+ `"${arg}" UTC offset without time is not valid for Instant (first argument)`
+ );
+ assert.throws(
+ RangeError,
+ () => Temporal.Instant.compare(epoch, arg),
+ `"${arg}" UTC offset without time is not valid for Instant (second argument)`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/compare/argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/Instant/compare/argument-string-invalid.js
new file mode 100644
index 0000000000..d12f63882e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/compare/argument-string-invalid.js
@@ -0,0 +1,74 @@
+// |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.instant.compare
+description: >
+ RangeError thrown if an invalid ISO string (or syntactically valid ISO string
+ that is not supported) is used as an Instant
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ // invalid ISO strings:
+ "",
+ "invalid iso8601",
+ "2020-01-00T00:00Z",
+ "2020-01-32T00:00Z",
+ "2020-02-30T00:00Z",
+ "2021-02-29T00:00Z",
+ "2020-00-01T00:00Z",
+ "2020-13-01T00:00Z",
+ "2020-01-01TZ",
+ "2020-01-01T25:00:00Z",
+ "2020-01-01T01:60:00Z",
+ "2020-01-01T01:60:61Z",
+ "2020-01-01T00:00Zjunk",
+ "2020-01-01T00:00:00Zjunk",
+ "2020-01-01T00:00:00.000000000Zjunk",
+ "2020-01-01T00:00:00+00:00junk",
+ "2020-01-01T00:00:00+00:00[UTC]junk",
+ "2020-01-01T00:00:00+00:00[UTC][u-ca=iso8601]junk",
+ "02020-01-01T00:00Z",
+ "2020-001-01T00:00Z",
+ "2020-01-001T00:00Z",
+ "2020-01-01T001Z",
+ "2020-01-01T01:001Z",
+ "2020-01-01T01:01:001Z",
+ // valid, but forms not supported in Temporal:
+ "2020-W01-1T00:00Z",
+ "2020-001T00:00Z",
+ "+0002020-01-01T00:00Z",
+ // may be valid in other contexts, but insufficient information for Instant:
+ "2020-01",
+ "+002020-01",
+ "01-01",
+ "2020-W01",
+ "P1Y",
+ "-P12Y",
+ "2020-01-01",
+ "2020-01-01T00",
+ "2020-01-01T00:00",
+ "2020-01-01T00:00:00",
+ "2020-01-01T00:00:00.000000000",
+ // valid, but outside the supported range:
+ "-999999-01-01T00:00Z",
+ "+999999-01-01T00:00Z",
+];
+
+const epoch = new Temporal.Instant(0n);
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => Temporal.Instant.compare(arg, epoch),
+ `"${arg}" should not be a valid ISO string for an Instant (first argument)`
+ );
+ assert.throws(
+ RangeError,
+ () => Temporal.Instant.compare(epoch, arg),
+ `"${arg}" should not be a valid ISO string for an Instant (second argument)`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/compare/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/Instant/compare/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..a6b50b7bc9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/compare/argument-string-multiple-calendar.js
@@ -0,0 +1,35 @@
+// |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.instant.compare
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01T00:00Z[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00Z[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01T00:00Z[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00Z[u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+];
+
+const epoch = new Temporal.Instant(0n);
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.Instant.compare(arg, epoch),
+ `reject more than one calendar annotation if any critical: ${arg} (first argument)`
+ );
+ assert.throws(
+ RangeError,
+ () => Temporal.Instant.compare(epoch, arg),
+ `reject more than one calendar annotation if any critical: ${arg} (second argument)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/compare/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/Instant/compare/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..f25eb647d5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/compare/argument-string-multiple-time-zone.js
@@ -0,0 +1,34 @@
+// |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.instant.compare
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01T00:00Z[UTC][UTC]",
+ "1970-01-01T00:00Z[!UTC][UTC]",
+ "1970-01-01T00:00Z[UTC][!UTC]",
+ "1970-01-01T00:00Z[UTC][u-ca=iso8601][UTC]",
+ "1970-01-01T00:00Z[UTC][foo=bar][UTC]",
+];
+
+const epoch = new Temporal.Instant(0n);
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.Instant.compare(arg, epoch),
+ `reject more than one time zone annotation: ${arg} (first argument)`
+ );
+ assert.throws(
+ RangeError,
+ () => Temporal.Instant.compare(epoch, arg),
+ `reject more than one time zone annotation: ${arg} (second argument)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/compare/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/Instant/compare/argument-string-time-separators.js
new file mode 100644
index 0000000000..96384822c3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/compare/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.instant.compare
+description: Time separator in string argument can vary
+features: [Temporal]
+---*/
+
+const epoch = new Temporal.Instant(0n);
+const tests = [
+ ["1970-01-01T00:00Z", "uppercase T"],
+ ["1970-01-01t00:00Z", "lowercase T"],
+ ["1970-01-01 00:00Z", "space between date and time"],
+];
+
+tests.forEach(([arg, description]) => {
+ assert.sameValue(
+ Temporal.Instant.compare(arg, epoch),
+ 0,
+ `variant time separators (${description}), first argument`
+ );
+
+ assert.sameValue(
+ Temporal.Instant.compare(epoch, arg),
+ 0,
+ `variant time separators (${description}), second argument`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/compare/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/Instant/compare/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..b34f31e10a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/compare/argument-string-time-zone-annotation.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.instant.compare
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["1970-01-01T00:00Z[Asia/Kolkata]", "named, with Z"],
+ ["1970-01-01T00:00Z[!Europe/Vienna]", "named, with Z and !"],
+ ["1970-01-01T00:00Z[+00:00]", "numeric, with Z"],
+ ["1970-01-01T00:00Z[!-02:30]", "numeric, with Z and !"],
+ ["1970-01-01T00:00+00:00[UTC]", "named, with offset"],
+ ["1970-01-01T00:00+00:00[!Africa/Abidjan]", "named, with offset and !"],
+ ["1970-01-01T00:00+00:00[-08:00]", "numeric, with offset"],
+ ["1970-01-01T00:00+00:00[!+01:00]", "numeric, with offset and !"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.Instant.compare(arg, arg);
+
+ assert.sameValue(
+ result,
+ 0,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/compare/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/Instant/compare/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..3370096e1a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/compare/argument-string-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.instant.compare
+description: Various forms of unknown annotation
+features: [Temporal]
+---*/
+
+const tests = [
+ ["1970-01-01T00:00Z[foo=bar]", "alone"],
+ ["1970-01-01T00:00Z[UTC][foo=bar]", "with time zone"],
+ ["1970-01-01T00:00Z[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["1970-01-01T00:00Z[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["1970-01-01T00:00Z[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.Instant.compare(arg, arg);
+
+ assert.sameValue(
+ result,
+ 0,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/compare/argument-string-with-offset-not-valid-epoch-nanoseconds.js b/js/src/tests/test262/built-ins/Temporal/Instant/compare/argument-string-with-offset-not-valid-epoch-nanoseconds.js
new file mode 100644
index 0000000000..e364683efe
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/compare/argument-string-with-offset-not-valid-epoch-nanoseconds.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.compare
+description: >
+ Throws when argument at maximum representable date/time value with a negative offset.
+features: [Temporal]
+---*/
+
+// Not a valid epoch nanoseconds value due to the offset.
+var one = "+275760-09-13T00:00:00.000-12";
+
+var two = {
+ toString() {
+ throw new Test262Error();
+ }
+};
+
+assert.throws(RangeError, () => Temporal.Instant.compare(one, two));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/compare/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Instant/compare/argument-wrong-type.js
new file mode 100644
index 0000000000..a8d800ab65
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/compare/argument-wrong-type.js
@@ -0,0 +1,62 @@
+// |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.instant.compare
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ for Instant
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const other = new Temporal.Instant(0n);
+
+const primitiveTests = [
+ [undefined, "undefined"],
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [19761118, "number that would convert to a valid ISO string in other contexts"],
+ [1n, "bigint"],
+ [{}, "plain object"],
+ [Temporal.Instant, "Temporal.Instant, object"]
+];
+
+for (const [arg, description] of primitiveTests) {
+ assert.throws(
+ typeof arg === "string" || (typeof arg === "object" && arg !== null) || typeof arg === "function"
+ ? RangeError
+ : TypeError,
+ () => Temporal.Instant.compare(arg, other),
+ `${description} does not convert to a valid ISO string (first argument)`
+ );
+ assert.throws(
+ typeof arg === "string" || (typeof arg === "object" && arg !== null) || typeof arg === "function"
+ ? RangeError
+ : TypeError,
+ () => Temporal.Instant.compare(other, arg),
+ `${description} does not convert to a valid ISO string (second argument)`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [Temporal.Instant.prototype, "Temporal.Instant.prototype, object"] // fails brand check in toString()
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(
+ TypeError,
+ () => Temporal.Instant.compare(arg, other),
+ `${description} does not convert to a string (first argument)`
+ );
+ assert.throws(
+ TypeError,
+ () => Temporal.Instant.compare(other, arg),
+ `${description} does not convert to a string (second argument)`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/compare/argument-zoneddatetime.js b/js/src/tests/test262/built-ins/Temporal/Instant/compare/argument-zoneddatetime.js
new file mode 100644
index 0000000000..922d3ae520
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/compare/argument-zoneddatetime.js
@@ -0,0 +1,31 @@
+// |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.instant.compare
+description: Fast path for converting Temporal.ZonedDateTime to Temporal.Instant
+info: |
+ sec-temporal.instant.compare steps 1–2:
+ 1. Set _one_ to ? ToTemporalInstant(_one_).
+ 2. Set _two_ to ? ToTemporalInstant(_two_).
+ sec-temporal-totemporalinstant step 1.b:
+ b. If _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return ! CreateTemporalInstant(_item_.[[Nanoseconds]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instant = new Temporal.Instant(1_000_000_000_000_000_000n);
+
+TemporalHelpers.checkToTemporalInstantFastPath((datetime) => {
+ const result = Temporal.Instant.compare(datetime, instant);
+ assert.sameValue(result, 1, "comparison result");
+});
+
+TemporalHelpers.checkToTemporalInstantFastPath((datetime) => {
+ const result = Temporal.Instant.compare(instant, datetime);
+ assert.sameValue(result, -1, "comparison result");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/compare/browser.js b/js/src/tests/test262/built-ins/Temporal/Instant/compare/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/compare/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/compare/builtin.js b/js/src/tests/test262/built-ins/Temporal/Instant/compare/builtin.js
new file mode 100644
index 0000000000..6f5dcddb85
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/compare/builtin.js
@@ -0,0 +1,33 @@
+// |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.instant.compare
+description: Tests that Temporal.Instant.compare meets the requirements for built-in objects
+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.Instant.compare),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Instant.compare),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Instant.compare),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Instant.compare.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/compare/exhaustive.js b/js/src/tests/test262/built-ins/Temporal/Instant/compare/exhaustive.js
new file mode 100644
index 0000000000..8cdfb630d9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/compare/exhaustive.js
@@ -0,0 +1,27 @@
+// |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.instant.compare
+description: Tests for compare() with each possible outcome
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ Temporal.Instant.compare(new Temporal.Instant(1_000_000_000_000_000_000n), new Temporal.Instant(500_000_000_000_000_000n)),
+ 1,
+ ">"
+);
+assert.sameValue(
+ Temporal.Instant.compare(new Temporal.Instant(-1000n), new Temporal.Instant(1000n)),
+ -1,
+ "<"
+);
+assert.sameValue(
+ Temporal.Instant.compare(new Temporal.Instant(123_456_789n), new Temporal.Instant(123_456_789n)),
+ 0,
+ "="
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/compare/instant-string-limits.js b/js/src/tests/test262/built-ins/Temporal/Instant/compare/instant-string-limits.js
new file mode 100644
index 0000000000..3a257b5153
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/compare/instant-string-limits.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.instant.compare
+description: String arguments at the limit of the representable range
+features: [Temporal]
+---*/
+
+const minInstant = new Temporal.Instant(-86400_00000000_000_000_000n);
+const maxInstant = new Temporal.Instant(86400_00000000_000_000_000n);
+
+const minInstantStrings = [
+ "-271821-04-20T00:00Z",
+ "-271821-04-19T23:00-01:00",
+ "-271821-04-19T00:00:00.000000001-23:59:59.999999999",
+];
+for (const str of minInstantStrings) {
+ assert.sameValue(Temporal.Instant.compare(str, minInstant), 0, `instant string ${str} should be valid (first argument)`);
+ assert.sameValue(Temporal.Instant.compare(minInstant, str), 0, `instant string ${str} should be valid (second argument)`);
+}
+
+const maxInstantStrings = [
+ "+275760-09-13T00:00Z",
+ "+275760-09-13T01:00+01:00",
+ "+275760-09-13T23:59:59.999999999+23:59:59.999999999",
+];
+
+for (const str of maxInstantStrings) {
+ assert.sameValue(Temporal.Instant.compare(str, maxInstant), 0, `instant string ${str} should be valid (first argument)`);
+ assert.sameValue(Temporal.Instant.compare(maxInstant, str), 0, `instant string ${str} should be valid (second argument)`);
+}
+
+const outOfRangeInstantStrings = [
+ "-271821-04-19T23:59:59.999999999Z",
+ "-271821-04-19T23:00-00:59:59.999999999",
+ "-271821-04-19T00:00:00-23:59:59.999999999",
+ "-271821-04-19T00:00:00-24:00",
+ "+275760-09-13T00:00:00.000000001Z",
+ "+275760-09-13T01:00+00:59:59.999999999",
+ "+275760-09-14T00:00+23:59:59.999999999",
+ "+275760-09-14T00:00+24:00",
+];
+
+for (const str of outOfRangeInstantStrings) {
+ assert.throws(RangeError, () => Temporal.Instant.compare(str, minInstant), `instant string ${str} should not be valid (first argument)`);
+ assert.throws(RangeError, () => Temporal.Instant.compare(minInstant, str), `instant string ${str} should not be valid (second argument)`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/compare/instant-string-multiple-offsets.js b/js/src/tests/test262/built-ins/Temporal/Instant/compare/instant-string-multiple-offsets.js
new file mode 100644
index 0000000000..090c3a78bf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/compare/instant-string-multiple-offsets.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.instant.compare
+description: Instant strings with UTC offset fractional part are not confused with time fractional part
+features: [Temporal]
+---*/
+
+const epoch = new Temporal.Instant(0n);
+const str = "1970-01-01T00:02:00.000000000+00:02[+01:30]";
+
+assert.sameValue(Temporal.Instant.compare(str, epoch), 0, "UTC offset determined from offset part of string (first argument)");
+assert.sameValue(Temporal.Instant.compare(epoch, str), 0, "UTC offset determined from offset part of string (second argument)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/compare/instant-string-sub-minute-offset.js b/js/src/tests/test262/built-ins/Temporal/Instant/compare/instant-string-sub-minute-offset.js
new file mode 100644
index 0000000000..4675b7122e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/compare/instant-string-sub-minute-offset.js
@@ -0,0 +1,20 @@
+// |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.instant.compare
+description: Temporal.Instant string with sub-minute offset
+features: [Temporal]
+---*/
+
+const epoch = new Temporal.Instant(0n);
+const str = "1970-01-01T00:19:32.37+00:19:32.37";
+
+const result1 = Temporal.Instant.compare(str, epoch);
+assert.sameValue(result1, 0, "if present, sub-minute offset is accepted exactly (first argument)");
+
+const result2 = Temporal.Instant.compare(epoch, str);
+assert.sameValue(result2, 0, "if present, sub-minute offset is accepted exactly (second argument)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/compare/instant-string.js b/js/src/tests/test262/built-ins/Temporal/Instant/compare/instant-string.js
new file mode 100644
index 0000000000..20b562fd53
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/compare/instant-string.js
@@ -0,0 +1,49 @@
+// |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.instant.compare
+description: Conversion of ISO date-time strings to Temporal.Instant instances
+features: [Temporal]
+---*/
+
+const epoch = new Temporal.Instant(0n);
+const hourBefore = new Temporal.Instant(-3600_000_000_000n);
+
+let str = "1970-01-01T00:00";
+assert.throws(RangeError, () => Temporal.Instant.compare(str, epoch), "bare date-time string is not an instant (first argument)");
+assert.throws(RangeError, () => Temporal.Instant.compare(epoch, str), "bare date-time string is not an instant (second argument)");
+str = "1970-01-01T00:00[UTC]";
+assert.throws(RangeError, () => Temporal.Instant.compare(str, epoch), "date-time + IANA annotation is not an instant (first argument)");
+assert.throws(RangeError, () => Temporal.Instant.compare(epoch, str), "date-time + IANA annotation is not an instant (second argument)");
+
+str = "1970-01-01T00:00Z";
+assert.sameValue(Temporal.Instant.compare(str, epoch), 0, "date-time + Z preserves exact time (first argument)");
+assert.sameValue(Temporal.Instant.compare(epoch, str), 0, "date-time + Z preserves exact time (second argument)");
+
+str = "1970-01-01T00:00+01:00";
+assert.sameValue(Temporal.Instant.compare(str, hourBefore), 0, "date-time + offset preserves exact time with offset (first argument)");
+assert.sameValue(Temporal.Instant.compare(hourBefore, str), 0, "date-time + offset preserves exact time with offset (second argument)");
+
+str = "1970-01-01T00:00Z[Etc/Ignored]";
+assert.sameValue(Temporal.Instant.compare(str, epoch), 0, "date-time + Z + IANA annotation ignores the IANA annotation (first argument)");
+assert.sameValue(Temporal.Instant.compare(epoch, str), 0, "date-time + Z + IANA annotation ignores the IANA annotation (second argument)");
+
+str = "1970-01-01T00:00+01:00[Etc/Ignored]";
+assert.sameValue(Temporal.Instant.compare(str, hourBefore), 0, "date-time + offset + IANA annotation ignores the IANA annotation (first argument)");
+assert.sameValue(Temporal.Instant.compare(hourBefore, str), 0, "date-time + offset + IANA annotation ignores the IANA annotation (second argument)");
+
+str = "1970-01-01T00:00Z[u-ca=hebrew]";
+assert.sameValue(Temporal.Instant.compare(str, epoch), 0, "date-time + Z + Calendar ignores the Calendar (first argument)");
+assert.sameValue(Temporal.Instant.compare(epoch, str), 0, "date-time + Z + Calendar ignores the Calendar (second argument)");
+
+str = "1970-01-01T00:00+01:00[u-ca=hebrew]";
+assert.sameValue(Temporal.Instant.compare(str, hourBefore), 0, "date-time + offset + Calendar ignores the Calendar (first argument)");
+assert.sameValue(Temporal.Instant.compare(hourBefore, str), 0, "date-time + offset + Calendar ignores the Calendar (second argument)");
+
+str = "1970-01-01T00:00+01:00[Etc/Ignored][u-ca=hebrew]";
+assert.sameValue(Temporal.Instant.compare(str, hourBefore), 0, "date-time + offset + IANA annotation + Calendar ignores the IANA annotation and the Calendar (first argument)");
+assert.sameValue(Temporal.Instant.compare(hourBefore, str), 0, "date-time + offset + IANA annotation + Calendar ignores the IANA annotation and the Calendar (second argument)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/compare/leap-second.js b/js/src/tests/test262/built-ins/Temporal/Instant/compare/leap-second.js
new file mode 100644
index 0000000000..026b7d3574
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/compare/leap-second.js
@@ -0,0 +1,18 @@
+// |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.instant.compare
+description: Leap second is a valid ISO string for Instant
+features: [Temporal]
+---*/
+
+const other = new Temporal.Instant(1_483_228_799_000_000_000n);
+const arg = "2016-12-31T23:59:60Z";
+const result1 = Temporal.Instant.compare(arg, other);
+assert.sameValue(result1, 0, "leap second is a valid ISO string for Instant (first argument)");
+const result2 = Temporal.Instant.compare(other, arg);
+assert.sameValue(result2, 0, "leap second is a valid ISO string for Instant (second argument)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/compare/length.js b/js/src/tests/test262/built-ins/Temporal/Instant/compare/length.js
new file mode 100644
index 0000000000..d4ec828691
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/compare/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.instant.compare
+description: Temporal.Instant.compare.length is 2
+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.Instant.compare, "length", {
+ value: 2,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/compare/name.js b/js/src/tests/test262/built-ins/Temporal/Instant/compare/name.js
new file mode 100644
index 0000000000..6925aa6222
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/compare/name.js
@@ -0,0 +1,26 @@
+// |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.instant.compare
+description: Temporal.Instant.compare.name is "compare"
+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.Instant.compare, "name", {
+ value: "compare",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/compare/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Instant/compare/not-a-constructor.js
new file mode 100644
index 0000000000..16c292ce61
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/compare/not-a-constructor.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.instant.compare
+description: Temporal.Instant.compare 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.Instant.compare();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Instant.compare), false,
+ "isConstructor(Temporal.Instant.compare)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/compare/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Instant/compare/prop-desc.js
new file mode 100644
index 0000000000..80d8165903
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/compare/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.instant.compare
+description: The "compare" property of Temporal.Instant
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Instant.compare,
+ "function",
+ "`typeof Instant.compare` is `function`"
+);
+
+verifyProperty(Temporal.Instant, "compare", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/compare/shell.js b/js/src/tests/test262/built-ins/Temporal/Instant/compare/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/compare/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/Instant/compare/year-zero.js b/js/src/tests/test262/built-ins/Temporal/Instant/compare/year-zero.js
new file mode 100644
index 0000000000..eab3c98cf2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/compare/year-zero.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.instant.compare
+description: Negative zero, as an extended year, fails
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(0n);
+const invalidStrings = [
+ "-000000-03-30T00:45Z",
+ "-000000-03-30T01:45+01:00",
+ "-000000-03-30T01:45:00+00:00[UTC]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(RangeError,
+ () => Temporal.Instant.compare(arg, instance),
+ "minus zero is invalid extended year (first argument)");
+ assert.throws(RangeError,
+ () => Temporal.Instant.compare(instance, arg),
+ "minus zero is invalid extended year (second argument)"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/constructor.js b/js/src/tests/test262/built-ins/Temporal/Instant/constructor.js
new file mode 100644
index 0000000000..e7ddc05c37
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/constructor.js
@@ -0,0 +1,15 @@
+// |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.instant
+description: Temporal.Instant constructor cannot be called as a function
+info: |
+ 1. If NewTarget is undefined, throw a TypeError exception.
+features: [Temporal]
+---*/
+
+assert.throws(TypeError, () => Temporal.Instant(0n));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/from/argument-instant.js b/js/src/tests/test262/built-ins/Temporal/Instant/from/argument-instant.js
new file mode 100644
index 0000000000..cc7799833d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/from/argument-instant.js
@@ -0,0 +1,22 @@
+// |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.instant.from
+description: A Instant object is copied, not returned directly
+features: [Temporal]
+---*/
+
+const orig = new Temporal.Instant(217_175_010_123_456_789n);
+const result = Temporal.Instant.from(orig);
+
+assert.sameValue(result.epochNanoseconds, 217_175_010_123_456_789n, "Instant is copied");
+
+assert.notSameValue(
+ result,
+ orig,
+ "When an Instant is given, the returned value is not the original Instant"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/from/argument-object-tostring.js b/js/src/tests/test262/built-ins/Temporal/Instant/from/argument-object-tostring.js
new file mode 100644
index 0000000000..92af4d6f63
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/from/argument-object-tostring.js
@@ -0,0 +1,20 @@
+// |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.instant.from
+description: Object is converted to a string, then to Temporal.Instant
+features: [Temporal]
+---*/
+
+const arg = {};
+assert.throws(RangeError, () => Temporal.Instant.from(arg), "[object Object] is not a valid ISO string");
+
+arg.toString = function() {
+ return "1970-01-01T00:00Z";
+};
+const result = Temporal.Instant.from(arg);
+assert.sameValue(result.epochNanoseconds, 0n, "result of toString is interpreted as ISO string");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/from/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/Instant/from/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..4b126bfd56
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/from/argument-string-calendar-annotation.js
@@ -0,0 +1,31 @@
+// |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.instant.from
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["1970-01-01T00:00Z[u-ca=iso8601]", "without time zone"],
+ ["1970-01-01T00:00Z[UTC][u-ca=gregory]", "with time zone"],
+ ["1970-01-01T00:00Z[!u-ca=hebrew]", "with ! and no time zone"],
+ ["1970-01-01T00:00Z[UTC][!u-ca=chinese]", "with ! and time zone"],
+ ["1970-01-01T00:00Z[u-ca=discord]", "annotation is ignored"],
+ ["1970-01-01T00:00Z[!u-ca=discord]", "annotation with ! is ignored"],
+ ["1970-01-01T00:00Z[u-ca=iso8601][u-ca=discord]", "two annotations are ignored"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.Instant.from(arg);
+
+ assert.sameValue(
+ result.epochNanoseconds,
+ 0n,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/from/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/Instant/from/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..1c39534e7f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/from/argument-string-critical-unknown-annotation.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.instant.from
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01T00:00Z[!foo=bar]",
+ "1970-01-01T00:00Z[UTC][!foo=bar]",
+ "1970-01-01T00:00Z[u-ca=iso8601][!foo=bar]",
+ "1970-01-01T00:00Z[UTC][!foo=bar][u-ca=iso8601]",
+ "1970-01-01T00:00Z[foo=bar][!_foo-bar0=Dont-Ignore-This-99999999999]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.Instant.from(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/from/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/Instant/from/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..5e56f4c48c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/from/argument-string-date-with-utc-offset.js
@@ -0,0 +1,50 @@
+// |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.instant.from
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+---*/
+
+const validStrings = [
+ "1970-01-01T00Z",
+ "1970-01-01T00Z[UTC]",
+ "1970-01-01T00Z[!UTC]",
+ "1970-01-01T00Z[Europe/Vienna]",
+ "1970-01-01T00+00:00",
+ "1970-01-01T00+00:00[UTC]",
+ "1970-01-01T00+00:00[!UTC]",
+ "1969-12-31T16-08:00[America/Vancouver]",
+];
+
+for (const arg of validStrings) {
+ const result = Temporal.Instant.from(arg);
+
+ assert.sameValue(
+ result.epochNanoseconds,
+ 0n,
+ `"${arg}" is a valid UTC offset with time for Instant`
+ );
+}
+
+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,
+ () => Temporal.Instant.from(arg),
+ `"${arg}" UTC offset without time is not valid for Instant`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/from/argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/Instant/from/argument-string-invalid.js
new file mode 100644
index 0000000000..f73b441661
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/from/argument-string-invalid.js
@@ -0,0 +1,67 @@
+// |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.instant.from
+description: >
+ RangeError thrown if an invalid ISO string (or syntactically valid ISO string
+ that is not supported) is used as an Instant
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ // invalid ISO strings:
+ "",
+ "invalid iso8601",
+ "2020-01-00T00:00Z",
+ "2020-01-32T00:00Z",
+ "2020-02-30T00:00Z",
+ "2021-02-29T00:00Z",
+ "2020-00-01T00:00Z",
+ "2020-13-01T00:00Z",
+ "2020-01-01TZ",
+ "2020-01-01T25:00:00Z",
+ "2020-01-01T01:60:00Z",
+ "2020-01-01T01:60:61Z",
+ "2020-01-01T00:00Zjunk",
+ "2020-01-01T00:00:00Zjunk",
+ "2020-01-01T00:00:00.000000000Zjunk",
+ "2020-01-01T00:00:00+00:00junk",
+ "2020-01-01T00:00:00+00:00[UTC]junk",
+ "2020-01-01T00:00:00+00:00[UTC][u-ca=iso8601]junk",
+ "02020-01-01T00:00Z",
+ "2020-001-01T00:00Z",
+ "2020-01-001T00:00Z",
+ "2020-01-01T001Z",
+ "2020-01-01T01:001Z",
+ "2020-01-01T01:01:001Z",
+ // valid, but forms not supported in Temporal:
+ "2020-W01-1T00:00Z",
+ "2020-001T00:00Z",
+ "+0002020-01-01T00:00Z",
+ // may be valid in other contexts, but insufficient information for Instant:
+ "2020-01",
+ "+002020-01",
+ "01-01",
+ "2020-W01",
+ "P1Y",
+ "-P12Y",
+ "2020-01-01",
+ "2020-01-01T00",
+ "2020-01-01T00:00",
+ "2020-01-01T00:00:00",
+ "2020-01-01T00:00:00.000000000",
+ // valid, but outside the supported range:
+ "-999999-01-01T00:00Z",
+ "+999999-01-01T00:00Z",
+];
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => Temporal.Instant.from(arg),
+ `"${arg}" should not be a valid ISO string for an Instant`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/from/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/Instant/from/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..9b4771b951
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/from/argument-string-multiple-calendar.js
@@ -0,0 +1,28 @@
+// |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.instant.from
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01T00:00Z[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00Z[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01T00:00Z[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00Z[u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.Instant.from(arg),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/from/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/Instant/from/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..4a7d004e92
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/from/argument-string-multiple-time-zone.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.instant.from
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01T00:00Z[UTC][UTC]",
+ "1970-01-01T00:00Z[!UTC][UTC]",
+ "1970-01-01T00:00Z[UTC][!UTC]",
+ "1970-01-01T00:00Z[UTC][u-ca=iso8601][UTC]",
+ "1970-01-01T00:00Z[UTC][foo=bar][UTC]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.Instant.from(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/from/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/Instant/from/argument-string-time-separators.js
new file mode 100644
index 0000000000..9d363af5dd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/from/argument-string-time-separators.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.instant.from
+description: Time separator in string argument can vary
+features: [Temporal]
+---*/
+
+const tests = [
+ ["1970-01-01T00:00Z", "uppercase T"],
+ ["1970-01-01t00:00Z", "lowercase T"],
+ ["1970-01-01 00:00Z", "space between date and time"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.Instant.from(arg);
+
+ assert.sameValue(
+ result.epochNanoseconds,
+ 0n,
+ `variant time separators (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/from/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/Instant/from/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..b1092bbf1d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/from/argument-string-time-zone-annotation.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.instant.from
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["1970-01-01T00:00Z[Asia/Kolkata]", "named, with Z"],
+ ["1970-01-01T00:00Z[!Europe/Vienna]", "named, with Z and !"],
+ ["1970-01-01T00:00Z[+00:00]", "numeric, with Z"],
+ ["1970-01-01T00:00Z[!-02:30]", "numeric, with Z and !"],
+ ["1970-01-01T00:00+00:00[UTC]", "named, with offset"],
+ ["1970-01-01T00:00+00:00[!Africa/Abidjan]", "named, with offset and !"],
+ ["1970-01-01T00:00+00:00[-08:00]", "numeric, with offset"],
+ ["1970-01-01T00:00+00:00[!+01:00]", "numeric, with offset and !"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.Instant.from(arg);
+
+ assert.sameValue(
+ result.epochNanoseconds,
+ 0n,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/from/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/Instant/from/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..b58d606dce
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/from/argument-string-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.instant.from
+description: Various forms of unknown annotation
+features: [Temporal]
+---*/
+
+const tests = [
+ ["1970-01-01T00:00Z[foo=bar]", "alone"],
+ ["1970-01-01T00:00Z[UTC][foo=bar]", "with time zone"],
+ ["1970-01-01T00:00Z[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["1970-01-01T00:00Z[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["1970-01-01T00:00Z[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.Instant.from(arg);
+
+ assert.sameValue(
+ result.epochNanoseconds,
+ 0n,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/from/argument-string.js b/js/src/tests/test262/built-ins/Temporal/Instant/from/argument-string.js
new file mode 100644
index 0000000000..2d70833ce8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/from/argument-string.js
@@ -0,0 +1,80 @@
+// |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.instant.from
+description: Various string arguments.
+features: [Temporal]
+---*/
+
+const tests = [
+ ['1976-11-18T15:23z', 217178580000000000n],
+ ['1976-11-18T15:23:30.1Z', 217178610100000000n],
+ ['1976-11-18T15:23:30.12Z', 217178610120000000n],
+ ['1976-11-18T15:23:30.123Z', 217178610123000000n],
+ ['1976-11-18T15:23:30.1234Z', 217178610123400000n],
+ ['1976-11-18T15:23:30.12345Z', 217178610123450000n],
+ ['1976-11-18T15:23:30.123456Z', 217178610123456000n],
+ ['1976-11-18T15:23:30.1234567Z', 217178610123456700n],
+ ['1976-11-18T15:23:30.12345678Z', 217178610123456780n],
+ ['1976-11-18T15:23:30.123456789Z', 217178610123456789n],
+ ['1976-11-18T15:23:30,12Z', 217178610120000000n],
+ ['1976-11-18T15:23:30.12\u221202:00', 217185810120000000n],
+ ['\u2212009999-11-18T15:23:30.12Z', -377677326989880000000n],
+ ['19761118T15:23:30.1+00:00', 217178610100000000n],
+ ['1976-11-18T152330.1+00:00', 217178610100000000n],
+ ['1976-11-18T15:23:30.1+0000', 217178610100000000n],
+ ['1976-11-18T152330.1+0000', 217178610100000000n],
+ ['19761118T15:23:30.1+0000', 217178610100000000n],
+ ['19761118T152330.1+00:00', 217178610100000000n],
+ ['+0019761118T15:23:30.1+00:00', 217178610100000000n],
+ ['+001976-11-18T152330.1+00:00', 217178610100000000n],
+ ['+001976-11-18T15:23:30.1+0000', 217178610100000000n],
+ ['+001976-11-18T152330.1+0000', 217178610100000000n],
+ ['+0019761118T15:23:30.1+0000', 217178610100000000n],
+ ['+0019761118T152330.1+00:00', 217178610100000000n],
+ ['+0019761118T152330.1+0000', 217178610100000000n],
+ ['1976-11-18T15:23:30+00', 217178610000000000n],
+ ['1976-11-18T15:23:30.123456789-00:00:00', 217178610123456789n],
+ ['1976-11-18T15:23:30.123456789-00:00:00.0', 217178610123456789n],
+ ['1976-11-18T15:23:30.123456789-00:00:00.00', 217178610123456789n],
+ ['1976-11-18T15:23:30.123456789-00:00:00.000', 217178610123456789n],
+ ['1976-11-18T15:23:30.123456789-00:00:00.0000', 217178610123456789n],
+ ['1976-11-18T15:23:30.123456789-00:00:00.00000', 217178610123456789n],
+ ['1976-11-18T15:23:30.123456789-00:00:00.000000', 217178610123456789n],
+ ['1976-11-18T15:23:30.123456789-00:00:00.0000000', 217178610123456789n],
+ ['1976-11-18T15:23:30.123456789-00:00:00.00000000', 217178610123456789n],
+ ['1976-11-18T15:23:30.123456789-00:00:00.000000000', 217178610123456789n],
+ ['1976-11-18T15:23:30.123456789-00:00:00.1', 217178610223456789n],
+ ['1976-11-18T15:23:30.123456789-00:00:00.01', 217178610133456789n],
+ ['1976-11-18T15:23:30.123456789-00:00:00.001', 217178610124456789n],
+ ['1976-11-18T15:23:30.123456789-00:00:00.0001', 217178610123556789n],
+ ['1976-11-18T15:23:30.123456789-00:00:00.00001', 217178610123466789n],
+ ['1976-11-18T15:23:30.123456789-00:00:00.000001', 217178610123457789n],
+ ['1976-11-18T15:23:30.123456789-00:00:00.0000001', 217178610123456889n],
+ ['1976-11-18T15:23:30.123456789-00:00:00.00000001', 217178610123456799n],
+ ['1976-11-18T15:23:30.123456789-00:00:00.000000001', 217178610123456790n],
+ ['1976-11-18T15:23:30.123456789-00:00:00.100000000', 217178610223456789n],
+ ['1976-11-18T15:23:30.123456789-00:00:00.010000000', 217178610133456789n],
+ ['1976-11-18T15:23:30.123456789-00:00:00.001000000', 217178610124456789n],
+ ['1976-11-18T15:23:30.123456789-00:00:00.000100000', 217178610123556789n],
+ ['1976-11-18T15:23:30.123456789-00:00:00.000010000', 217178610123466789n],
+ ['1976-11-18T15:23:30.123456789-00:00:00.000001000', 217178610123457789n],
+ ['1976-11-18T15:23:30.123456789-00:00:00.000000100', 217178610123456889n],
+ ['1976-11-18T15:23:30.123456789-00:00:00.000000010', 217178610123456799n],
+ ['1976-11-18T15Z', 217177200000000000n],
+ ['1976-11-18T15:23:30.123456789Z[u-ca=discord]', 217178610123456789n],
+ ['1976-11-18T15:23:30.123456789Z[+00]', 217178610123456789n],
+ ['1976-11-18T15:23:30.123456789Z[-00]', 217178610123456789n],
+ ['1976-11-18T15:23:30.123456789Z[-00:00]', 217178610123456789n],
+ ['1976-11-18T15:23:30.123456789Z[+12]', 217178610123456789n],
+ ['1976-11-18T15:23:30.123456789Z[NotATimeZone]', 217178610123456789n],
+];
+
+for (const [arg, expected] of tests) {
+ const result = Temporal.Instant.from(arg);
+ assert.sameValue(result.epochNanoseconds, expected, `Instant.from(${arg})`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/from/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Instant/from/argument-wrong-type.js
new file mode 100644
index 0000000000..eadb4b3963
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/from/argument-wrong-type.js
@@ -0,0 +1,44 @@
+// |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.instant.from
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ for Instant
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const primitiveTests = [
+ [undefined, 'undefined'],
+ [null, 'null'],
+ [true, 'boolean'],
+ ['', 'empty string'],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [19761118, 'number that would convert to a valid ISO string in other contexts'],
+ [1n, 'bigint'],
+ [{}, 'plain object'],
+ [Temporal.Instant, 'Temporal.Instant, object']
+];
+
+for (const [arg, description] of primitiveTests) {
+ assert.throws(
+ typeof arg === 'string' || (typeof arg === 'object' && arg !== null) || typeof arg === 'function'
+ ? RangeError
+ : TypeError,
+ () => Temporal.Instant.from(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), 'symbol'],
+ [Temporal.Instant.prototype, 'Temporal.Instant.prototype, object'] // fails brand check in toString()
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => Temporal.Instant.from(arg), `${description} does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/from/argument-zoneddatetime.js b/js/src/tests/test262/built-ins/Temporal/Instant/from/argument-zoneddatetime.js
new file mode 100644
index 0000000000..7b5dc9e6d0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/from/argument-zoneddatetime.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.instant.from
+description: Fast path for converting Temporal.ZonedDateTime to Temporal.Instant
+info: |
+ sec-temporal.instant.from step 2:
+ 2. Return ? ToTemporalInstant(_item_).
+ sec-temporal-totemporalinstant step 1.b:
+ b. If _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return ! CreateTemporalInstant(_item_.[[Nanoseconds]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalInstantFastPath((datetime) => {
+ const result = Temporal.Instant.from(datetime);
+ assert.sameValue(result.epochNanoseconds, result.epochNanoseconds, "epochNanoseconds result");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/from/basic.js b/js/src/tests/test262/built-ins/Temporal/Instant/from/basic.js
new file mode 100644
index 0000000000..8402108a6a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/from/basic.js
@@ -0,0 +1,62 @@
+// |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.instant.from
+description: Basic functionality of Temporal.Instant.from
+features: [Temporal]
+---*/
+
+const baseValue = 217_178_580_000_000_000n;
+
+let instant = Temporal.Instant.from("1976-11-18T15:23Z");
+assert.sameValue(
+ instant.epochNanoseconds,
+ baseValue,
+ "ISO string with UTC designator and minutes precision"
+);
+
+instant = Temporal.Instant.from("1976-11-18T15:23:30Z");
+assert.sameValue(
+ instant.epochNanoseconds,
+ baseValue + 30_000_000_000n,
+ "ISO string with UTC designator and seconds precision"
+);
+
+instant = Temporal.Instant.from("1976-11-18T15:23:30.123Z");
+assert.sameValue(
+ instant.epochNanoseconds,
+ baseValue + 30_123_000_000n,
+ "ISO string with UTC designator and milliseconds precision"
+);
+
+instant = Temporal.Instant.from("1976-11-18T15:23:30.123456Z");
+assert.sameValue(
+ instant.epochNanoseconds,
+ baseValue + 30_123_456_000n,
+ "ISO string with UTC designator and microseconds precision"
+);
+
+instant = Temporal.Instant.from("1976-11-18T15:23:30.123456789Z");
+assert.sameValue(
+ instant.epochNanoseconds,
+ baseValue + 30_123_456_789n,
+ "ISO string with UTC designator and nanoseconds precision"
+);
+
+instant = Temporal.Instant.from("1976-11-18T15:23-01:00");
+assert.sameValue(
+ instant.epochNanoseconds,
+ baseValue + 3600_000_000_000n,
+ "ISO string with negative UTC offset"
+);
+
+instant = Temporal.Instant.from("1976-11-18T15:23+01:00");
+assert.sameValue(
+ instant.epochNanoseconds,
+ baseValue - 3600_000_000_000n,
+ "ISO string with positive UTC offset"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/from/browser.js b/js/src/tests/test262/built-ins/Temporal/Instant/from/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/from/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/from/builtin.js b/js/src/tests/test262/built-ins/Temporal/Instant/from/builtin.js
new file mode 100644
index 0000000000..fe8384314f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/from/builtin.js
@@ -0,0 +1,33 @@
+// |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.instant.from
+description: Tests that Temporal.Instant.from meets the requirements for built-in objects
+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.Instant.from),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Instant.from),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Instant.from),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Instant.from.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/from/instant-string-limits.js b/js/src/tests/test262/built-ins/Temporal/Instant/from/instant-string-limits.js
new file mode 100644
index 0000000000..6f9b14e46d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/from/instant-string-limits.js
@@ -0,0 +1,48 @@
+// |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.instant.from
+description: String arguments at the limit of the representable range
+features: [Temporal]
+---*/
+
+const minInstant = new Temporal.Instant(-86400_00000000_000_000_000n);
+const maxInstant = new Temporal.Instant(86400_00000000_000_000_000n);
+
+const minInstantStrings = [
+ "-271821-04-20T00:00Z",
+ "-271821-04-19T23:00-01:00",
+ "-271821-04-19T00:00:00.000000001-23:59:59.999999999",
+];
+for (const str of minInstantStrings) {
+ assert.sameValue(Temporal.Instant.from(str).epochNanoseconds, minInstant.epochNanoseconds, `instant string ${str} should be valid`);
+}
+
+const maxInstantStrings = [
+ "+275760-09-13T00:00Z",
+ "+275760-09-13T01:00+01:00",
+ "+275760-09-13T23:59:59.999999999+23:59:59.999999999",
+];
+
+for (const str of maxInstantStrings) {
+ assert.sameValue(Temporal.Instant.from(str).epochNanoseconds, maxInstant.epochNanoseconds, `instant string ${str} should be valid`);
+}
+
+const outOfRangeInstantStrings = [
+ "-271821-04-19T23:59:59.999999999Z",
+ "-271821-04-19T23:00-00:59:59.999999999",
+ "-271821-04-19T00:00:00-23:59:59.999999999",
+ "-271821-04-19T00:00:00-24:00",
+ "+275760-09-13T00:00:00.000000001Z",
+ "+275760-09-13T01:00+00:59:59.999999999",
+ "+275760-09-14T00:00+23:59:59.999999999",
+ "+275760-09-14T00:00+24:00",
+];
+
+for (const str of outOfRangeInstantStrings) {
+ assert.throws(RangeError, () => Temporal.Instant.from(str), `instant string ${str} should not be valid`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/from/instant-string-multiple-offsets.js b/js/src/tests/test262/built-ins/Temporal/Instant/from/instant-string-multiple-offsets.js
new file mode 100644
index 0000000000..3b863adcf1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/from/instant-string-multiple-offsets.js
@@ -0,0 +1,16 @@
+// |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.instant.from
+description: Instant strings with UTC offset fractional part are not confused with time fractional part
+features: [Temporal]
+---*/
+
+const str = "1970-01-01T00:02:00.000000000+00:02[+01:30]";
+
+const result = Temporal.Instant.from(str);
+assert.sameValue(result.epochNanoseconds, 0n, "UTC offset determined from offset part of string");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/from/instant-string-sub-minute-offset.js b/js/src/tests/test262/built-ins/Temporal/Instant/from/instant-string-sub-minute-offset.js
new file mode 100644
index 0000000000..fb1591d7ac
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/from/instant-string-sub-minute-offset.js
@@ -0,0 +1,65 @@
+// |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.instant.from
+description: Temporal.Instant string with sub-minute offset
+features: [Temporal]
+---*/
+
+const str = "1970-01-01T00:19:32.37+00:19:32.37";
+const result = Temporal.Instant.from(str);
+assert.sameValue(result.epochNanoseconds, 0n, "if present, sub-minute offset is accepted exactly");
+
+[
+ "2021-08-19T17:30-07:00:01[-07:00:01]",
+ "2021-08-19T17:30-07:00:00[-07:00:00]",
+ "2021-08-19T17:30-07:00:00.1[-07:00:00.1]",
+ "2021-08-19T17:30-07:00:00.0[-07:00:00.0]",
+ "2021-08-19T17:30-07:00:00.01[-07:00:00.01]",
+ "2021-08-19T17:30-07:00:00.00[-07:00:00.00]",
+ "2021-08-19T17:30-07:00:00.001[-07:00:00.001]",
+ "2021-08-19T17:30-07:00:00.000[-07:00:00.000]",
+ "2021-08-19T17:30-07:00:00.0001[-07:00:00.0001]",
+ "2021-08-19T17:30-07:00:00.0000[-07:00:00.0000]",
+ "2021-08-19T17:30-07:00:00.00001[-07:00:00.00001]",
+ "2021-08-19T17:30-07:00:00.00000[-07:00:00.00000]",
+ "2021-08-19T17:30-07:00:00.000001[-07:00:00.000001]",
+ "2021-08-19T17:30-07:00:00.000000[-07:00:00.000000]",
+ "2021-08-19T17:30-07:00:00.0000001[-07:00:00.0000001]",
+ "2021-08-19T17:30-07:00:00.0000000[-07:00:00.0000000]",
+ "2021-08-19T17:30-07:00:00.00000001[-07:00:00.00000001]",
+ "2021-08-19T17:30-07:00:00.00000000[-07:00:00.00000000]",
+ "2021-08-19T17:30-07:00:00.000000001[-07:00:00.000000001]",
+ "2021-08-19T17:30-07:00:00.000000000[-07:00:00.000000000]",
+
+ "2021-08-19T17:30-07:00:01[-070001]",
+ "2021-08-19T17:30-07:00:00[-070000]",
+ "2021-08-19T17:30-07:00:00.1[-070000.1]",
+ "2021-08-19T17:30-07:00:00.0[-070000.0]",
+ "2021-08-19T17:30-07:00:00.01[-070000.01]",
+ "2021-08-19T17:30-07:00:00.00[-070000.00]",
+ "2021-08-19T17:30-07:00:00.001[-070000.001]",
+ "2021-08-19T17:30-07:00:00.000[-070000.000]",
+ "2021-08-19T17:30-07:00:00.0001[-070000.0001]",
+ "2021-08-19T17:30-07:00:00.0000[-070000.0000]",
+ "2021-08-19T17:30-07:00:00.00001[-070000.00001]",
+ "2021-08-19T17:30-07:00:00.00000[-070000.00000]",
+ "2021-08-19T17:30-07:00:00.000001[-070000.000001]",
+ "2021-08-19T17:30-07:00:00.000000[-070000.000000]",
+ "2021-08-19T17:30-07:00:00.0000001[-070000.0000001]",
+ "2021-08-19T17:30-07:00:00.0000000[-070000.0000000]",
+ "2021-08-19T17:30-07:00:00.00000001[-070000.00000001]",
+ "2021-08-19T17:30-07:00:00.00000000[-070000.00000000]",
+ "2021-08-19T17:30-07:00:00.000000001[-070000.000000001]",
+ "2021-08-19T17:30-07:00:00.000000000[-070000.000000000]"
+].forEach((str) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.Instant.from(str),
+ `ISO strings cannot have sub-minute offsets in time zone annotations: ${str}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/from/instant-string.js b/js/src/tests/test262/built-ins/Temporal/Instant/from/instant-string.js
new file mode 100644
index 0000000000..422e2a1d40
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/from/instant-string.js
@@ -0,0 +1,44 @@
+// |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.instant.from
+description: Conversion of ISO date-time strings to Temporal.Instant instances
+features: [Temporal]
+---*/
+
+let str = "1970-01-01T00:00";
+assert.throws(RangeError, () => Temporal.Instant.from(str), "bare date-time string is not an instant");
+str = "1970-01-01T00:00[UTC]";
+assert.throws(RangeError, () => Temporal.Instant.from(str), "date-time + IANA annotation is not an instant");
+
+str = "1970-01-01T00:00Z";
+const result1 = Temporal.Instant.from(str);
+assert.sameValue(result1.epochNanoseconds, 0n, "date-time + Z preserves exact time");
+
+str = "1970-01-01T00:00+01:00";
+const result2 = Temporal.Instant.from(str);
+assert.sameValue(result2.epochNanoseconds, -3600_000_000_000n, "date-time + offset preserves exact time with offset");
+
+str = "1970-01-01T00:00Z[Etc/Ignored]";
+const result3 = Temporal.Instant.from(str);
+assert.sameValue(result3.epochNanoseconds, 0n, "date-time + Z + IANA annotation ignores the IANA annotation");
+
+str = "1970-01-01T00:00+01:00[Etc/Ignored]";
+const result4 = Temporal.Instant.from(str);
+assert.sameValue(result4.epochNanoseconds, -3600_000_000_000n, "date-time + offset + IANA annotation ignores the IANA annotation");
+
+str = "1970-01-01T00:00Z[u-ca=hebrew]";
+const result6 = Temporal.Instant.from(str);
+assert.sameValue(result6.epochNanoseconds, 0n, "date-time + Z + Calendar ignores the Calendar");
+
+str = "1970-01-01T00:00+01:00[u-ca=hebrew]";
+const result7 = Temporal.Instant.from(str);
+assert.sameValue(result7.epochNanoseconds, -3600_000_000_000n, "date-time + offset + Calendar ignores the Calendar");
+
+str = "1970-01-01T00:00+01:00[Etc/Ignored][u-ca=hebrew]";
+const result8 = Temporal.Instant.from(str);
+assert.sameValue(result8.epochNanoseconds, -3600_000_000_000n, "date-time + offset + IANA annotation + Calendar ignores the Calendar and IANA annotation");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/from/leap-second.js b/js/src/tests/test262/built-ins/Temporal/Instant/from/leap-second.js
new file mode 100644
index 0000000000..7214562cc2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/from/leap-second.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.instant.from
+description: Leap second is a valid ISO string for Instant
+features: [Temporal]
+---*/
+
+const arg = "2016-12-31T23:59:60Z";
+const result = Temporal.Instant.from(arg);
+assert.sameValue(
+ result.epochNanoseconds,
+ 1_483_228_799_000_000_000n,
+ "leap second is a valid ISO string for Instant"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/from/length.js b/js/src/tests/test262/built-ins/Temporal/Instant/from/length.js
new file mode 100644
index 0000000000..8f7095405f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/from/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.instant.from
+description: Temporal.Instant.from.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.Instant.from, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/from/name.js b/js/src/tests/test262/built-ins/Temporal/Instant/from/name.js
new file mode 100644
index 0000000000..77d8fe0ae5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/from/name.js
@@ -0,0 +1,26 @@
+// |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.instant.from
+description: Temporal.Instant.from.name is "from"
+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.Instant.from, "name", {
+ value: "from",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/from/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Instant/from/not-a-constructor.js
new file mode 100644
index 0000000000..0deb2264a2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/from/not-a-constructor.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.instant.from
+description: Temporal.Instant.from 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.Instant.from();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Instant.from), false,
+ "isConstructor(Temporal.Instant.from)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/from/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Instant/from/prop-desc.js
new file mode 100644
index 0000000000..be325649b4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/from/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.instant.from
+description: The "from" property of Temporal.Instant
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Instant.from,
+ "function",
+ "`typeof Instant.from` is `function`"
+);
+
+verifyProperty(Temporal.Instant, "from", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/from/shell.js b/js/src/tests/test262/built-ins/Temporal/Instant/from/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/from/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/Instant/from/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/Instant/from/subclassing-ignored.js
new file mode 100644
index 0000000000..8f3fee45a1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/from/subclassing-ignored.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.instant.from
+description: The receiver is never called when calling from()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnoredStatic(
+ Temporal.Instant,
+ "from",
+ ["1976-11-18T14:23:30.123456789Z"],
+ (result) => assert.sameValue(result.epochNanoseconds, 217_175_010_123_456_789n, "epochNanoseconds result"),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/from/timezone-custom.js b/js/src/tests/test262/built-ins/Temporal/Instant/from/timezone-custom.js
new file mode 100644
index 0000000000..7b3b88da93
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/from/timezone-custom.js
@@ -0,0 +1,22 @@
+// |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.instant.from
+description: Time zone annotation is ignored in input ISO string
+features: [Temporal]
+---*/
+
+const dateTimeString = "1975-02-02T14:25:36.123456789";
+
+Object.defineProperty(Temporal.TimeZone, "from", {
+ get() {
+ throw new Test262Error("should not get Temporal.TimeZone.from");
+ },
+});
+
+const instant = Temporal.Instant.from(dateTimeString + "+01:00[Custom/TimeZone]");
+assert.sameValue(instant.epochMilliseconds, 160579536123);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/from/year-zero.js b/js/src/tests/test262/built-ins/Temporal/Instant/from/year-zero.js
new file mode 100644
index 0000000000..9f9e8f53e7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/from/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.instant.from
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-03-30T00:45Z",
+ "-000000-03-30T01:45+01:00",
+ "-000000-03-30T01:45:00+00:00[UTC]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.Instant.from(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMicroseconds/basic.js b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMicroseconds/basic.js
new file mode 100644
index 0000000000..b655727092
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMicroseconds/basic.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.instant.fromepochmicroseconds
+description: Basic tests for Instant.fromEpochMicroseconds().
+features: [BigInt, Temporal]
+---*/
+
+const afterEpoch = Temporal.Instant.fromEpochMicroseconds(217175010_123_456n);
+assert.sameValue(afterEpoch.epochNanoseconds, 217175010_123_456_000n, "fromEpochMicroseconds post epoch");
+
+const beforeEpoch = Temporal.Instant.fromEpochMicroseconds(-217175010_876_543n);
+assert.sameValue(beforeEpoch.epochNanoseconds, -217175010_876_543_000n, "fromEpochMicroseconds pre epoch");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMicroseconds/browser.js b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMicroseconds/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMicroseconds/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMicroseconds/builtin.js b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMicroseconds/builtin.js
new file mode 100644
index 0000000000..90de190231
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMicroseconds/builtin.js
@@ -0,0 +1,33 @@
+// |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.instant.fromepochmicroseconds
+description: Tests that Temporal.Instant.fromEpochMicroseconds meets the requirements for built-in objects
+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.Instant.fromEpochMicroseconds),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Instant.fromEpochMicroseconds),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Instant.fromEpochMicroseconds),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Instant.fromEpochMicroseconds.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMicroseconds/length.js b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMicroseconds/length.js
new file mode 100644
index 0000000000..4183b69445
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMicroseconds/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.instant.fromepochmicroseconds
+description: Temporal.Instant.fromEpochMicroseconds.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.Instant.fromEpochMicroseconds, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMicroseconds/name.js b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMicroseconds/name.js
new file mode 100644
index 0000000000..49e743f6ec
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMicroseconds/name.js
@@ -0,0 +1,26 @@
+// |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.instant.fromepochmicroseconds
+description: Temporal.Instant.fromEpochMicroseconds.name is "fromEpochMicroseconds"
+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.Instant.fromEpochMicroseconds, "name", {
+ value: "fromEpochMicroseconds",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMicroseconds/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMicroseconds/not-a-constructor.js
new file mode 100644
index 0000000000..488e6a64ca
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMicroseconds/not-a-constructor.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.instant.fromepochmicroseconds
+description: Temporal.Instant.fromEpochMicroseconds 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.Instant.fromEpochMicroseconds();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Instant.fromEpochMicroseconds), false,
+ "isConstructor(Temporal.Instant.fromEpochMicroseconds)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMicroseconds/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMicroseconds/prop-desc.js
new file mode 100644
index 0000000000..f34b066d68
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMicroseconds/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.instant.fromepochmicroseconds
+description: The "fromEpochMicroseconds" property of Temporal.Instant
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Instant.fromEpochMicroseconds,
+ "function",
+ "`typeof Instant.fromEpochMicroseconds` is `function`"
+);
+
+verifyProperty(Temporal.Instant, "fromEpochMicroseconds", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMicroseconds/shell.js b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMicroseconds/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMicroseconds/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/Instant/fromEpochMicroseconds/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMicroseconds/subclassing-ignored.js
new file mode 100644
index 0000000000..8c6a81e20f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMicroseconds/subclassing-ignored.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.instant.fromepochmicroseconds
+description: The receiver is never called by fromEpochMicroseconds()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnoredStatic(
+ Temporal.Instant,
+ "fromEpochMicroseconds",
+ [10n],
+ (result) => {
+ assert.sameValue(result.epochNanoseconds, 10_000n, "epochNanoseconds result");
+ },
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMilliseconds/basic.js b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMilliseconds/basic.js
new file mode 100644
index 0000000000..11bc35eae2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMilliseconds/basic.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.instant.fromepochmilliseconds
+description: Basic tests for Instant.fromEpochMilliseconds().
+features: [BigInt, Temporal]
+---*/
+
+const afterEpoch = Temporal.Instant.fromEpochMilliseconds(217175010_123);
+assert.sameValue(afterEpoch.epochNanoseconds, 217175010_123_000_000n, "fromEpochMilliseconds post epoch");
+
+const beforeEpoch = Temporal.Instant.fromEpochMilliseconds(-217175010_876);
+assert.sameValue(beforeEpoch.epochNanoseconds, -217175010_876_000_000n, "fromEpochMilliseconds pre epoch");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMilliseconds/browser.js b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMilliseconds/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMilliseconds/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMilliseconds/builtin.js b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMilliseconds/builtin.js
new file mode 100644
index 0000000000..9cc78ccf6c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMilliseconds/builtin.js
@@ -0,0 +1,33 @@
+// |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.instant.fromepochmilliseconds
+description: Tests that Temporal.Instant.fromEpochMilliseconds meets the requirements for built-in objects
+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.Instant.fromEpochMilliseconds),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Instant.fromEpochMilliseconds),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Instant.fromEpochMilliseconds),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Instant.fromEpochMilliseconds.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMilliseconds/length.js b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMilliseconds/length.js
new file mode 100644
index 0000000000..2218ee5ec1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMilliseconds/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.instant.fromepochmilliseconds
+description: Temporal.Instant.fromEpochMilliseconds.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.Instant.fromEpochMilliseconds, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMilliseconds/name.js b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMilliseconds/name.js
new file mode 100644
index 0000000000..1f109dfa03
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMilliseconds/name.js
@@ -0,0 +1,26 @@
+// |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.instant.fromepochmilliseconds
+description: Temporal.Instant.fromEpochMilliseconds.name is "fromEpochMilliseconds"
+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.Instant.fromEpochMilliseconds, "name", {
+ value: "fromEpochMilliseconds",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMilliseconds/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMilliseconds/not-a-constructor.js
new file mode 100644
index 0000000000..51b5612f8b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMilliseconds/not-a-constructor.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.instant.fromepochmilliseconds
+description: Temporal.Instant.fromEpochMilliseconds 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.Instant.fromEpochMilliseconds();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Instant.fromEpochMilliseconds), false,
+ "isConstructor(Temporal.Instant.fromEpochMilliseconds)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMilliseconds/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMilliseconds/prop-desc.js
new file mode 100644
index 0000000000..3fbac3b691
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMilliseconds/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.instant.fromepochmilliseconds
+description: The "fromEpochMilliseconds" property of Temporal.Instant
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Instant.fromEpochMilliseconds,
+ "function",
+ "`typeof Instant.fromEpochMilliseconds` is `function`"
+);
+
+verifyProperty(Temporal.Instant, "fromEpochMilliseconds", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMilliseconds/shell.js b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMilliseconds/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMilliseconds/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/Instant/fromEpochMilliseconds/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMilliseconds/subclassing-ignored.js
new file mode 100644
index 0000000000..72a5544be2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochMilliseconds/subclassing-ignored.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.instant.fromepochmilliseconds
+description: The receiver is never called by fromEpochMilliseconds()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnoredStatic(
+ Temporal.Instant,
+ "fromEpochMilliseconds",
+ [10],
+ (result) => {
+ assert.sameValue(result.epochNanoseconds, 10_000_000n, "epochNanoseconds result");
+ },
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochNanoseconds/basic.js b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochNanoseconds/basic.js
new file mode 100644
index 0000000000..67589a74b2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochNanoseconds/basic.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.instant.fromepochnanoseconds
+description: Basic tests for Instant.fromEpochNanoseconds().
+features: [BigInt, Temporal]
+---*/
+
+const afterEpoch = Temporal.Instant.fromEpochNanoseconds(217175010_123_456_789n);
+assert.sameValue(afterEpoch.epochNanoseconds, 217175010_123_456_789n, "fromEpochNanoseconds post epoch");
+
+const beforeEpoch = Temporal.Instant.fromEpochNanoseconds(-217175010_876_543_211n);
+assert.sameValue(beforeEpoch.epochNanoseconds, -217175010_876_543_211n, "fromEpochNanoseconds pre epoch");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochNanoseconds/browser.js b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochNanoseconds/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochNanoseconds/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochNanoseconds/builtin.js b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochNanoseconds/builtin.js
new file mode 100644
index 0000000000..2927a54bbe
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochNanoseconds/builtin.js
@@ -0,0 +1,33 @@
+// |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.instant.fromepochnanoseconds
+description: Tests that Temporal.Instant.fromEpochNanoseconds meets the requirements for built-in objects
+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.Instant.fromEpochNanoseconds),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Instant.fromEpochNanoseconds),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Instant.fromEpochNanoseconds),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Instant.fromEpochNanoseconds.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochNanoseconds/length.js b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochNanoseconds/length.js
new file mode 100644
index 0000000000..7eece6fc52
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochNanoseconds/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.instant.fromepochnanoseconds
+description: Temporal.Instant.fromEpochNanoseconds.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.Instant.fromEpochNanoseconds, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochNanoseconds/name.js b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochNanoseconds/name.js
new file mode 100644
index 0000000000..6fc9ea9601
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochNanoseconds/name.js
@@ -0,0 +1,26 @@
+// |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.instant.fromepochnanoseconds
+description: Temporal.Instant.fromEpochNanoseconds.name is "fromEpochNanoseconds"
+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.Instant.fromEpochNanoseconds, "name", {
+ value: "fromEpochNanoseconds",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochNanoseconds/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochNanoseconds/not-a-constructor.js
new file mode 100644
index 0000000000..3d39ba0bc7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochNanoseconds/not-a-constructor.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.instant.fromepochnanoseconds
+description: Temporal.Instant.fromEpochNanoseconds 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.Instant.fromEpochNanoseconds();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Instant.fromEpochNanoseconds), false,
+ "isConstructor(Temporal.Instant.fromEpochNanoseconds)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochNanoseconds/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochNanoseconds/prop-desc.js
new file mode 100644
index 0000000000..bf8229aadf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochNanoseconds/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.instant.fromepochnanoseconds
+description: The "fromEpochNanoseconds" property of Temporal.Instant
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Instant.fromEpochNanoseconds,
+ "function",
+ "`typeof Instant.fromEpochNanoseconds` is `function`"
+);
+
+verifyProperty(Temporal.Instant, "fromEpochNanoseconds", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochNanoseconds/shell.js b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochNanoseconds/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochNanoseconds/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/Instant/fromEpochNanoseconds/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochNanoseconds/subclassing-ignored.js
new file mode 100644
index 0000000000..7e78286793
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochNanoseconds/subclassing-ignored.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.instant.fromepochnanoseconds
+description: The receiver is never called by fromEpochNanoseconds()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnoredStatic(
+ Temporal.Instant,
+ "fromEpochNanoseconds",
+ [10n],
+ (result) => {
+ assert.sameValue(result.epochNanoseconds, 10n, "epochNanoseconds result");
+ },
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochSeconds/basic.js b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochSeconds/basic.js
new file mode 100644
index 0000000000..b00f7b10cb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochSeconds/basic.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.instant.fromepochseconds
+description: Basic tests for Instant.fromEpochSeconds().
+features: [BigInt, Temporal]
+---*/
+
+const afterEpoch = Temporal.Instant.fromEpochSeconds(217175010);
+assert.sameValue(afterEpoch.epochNanoseconds, 217175010_000_000_000n, "fromEpochSeconds post epoch");
+
+const beforeEpoch = Temporal.Instant.fromEpochSeconds(-217175010);
+assert.sameValue(beforeEpoch.epochNanoseconds, -217175010_000_000_000n, "fromEpochSeconds pre epoch");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochSeconds/browser.js b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochSeconds/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochSeconds/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochSeconds/builtin.js b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochSeconds/builtin.js
new file mode 100644
index 0000000000..15ac4ebe6a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochSeconds/builtin.js
@@ -0,0 +1,33 @@
+// |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.instant.fromepochseconds
+description: Tests that Temporal.Instant.fromEpochSeconds meets the requirements for built-in objects
+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.Instant.fromEpochSeconds),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Instant.fromEpochSeconds),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Instant.fromEpochSeconds),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Instant.fromEpochSeconds.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochSeconds/length.js b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochSeconds/length.js
new file mode 100644
index 0000000000..0c7f3e57bc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochSeconds/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.instant.fromepochseconds
+description: Temporal.Instant.fromEpochSeconds.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.Instant.fromEpochSeconds, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochSeconds/name.js b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochSeconds/name.js
new file mode 100644
index 0000000000..fa70b2f52e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochSeconds/name.js
@@ -0,0 +1,26 @@
+// |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.instant.fromepochseconds
+description: Temporal.Instant.fromEpochSeconds.name is "fromEpochSeconds"
+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.Instant.fromEpochSeconds, "name", {
+ value: "fromEpochSeconds",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochSeconds/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochSeconds/not-a-constructor.js
new file mode 100644
index 0000000000..ab09491e49
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochSeconds/not-a-constructor.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.instant.fromepochseconds
+description: Temporal.Instant.fromEpochSeconds 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.Instant.fromEpochSeconds();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Instant.fromEpochSeconds), false,
+ "isConstructor(Temporal.Instant.fromEpochSeconds)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochSeconds/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochSeconds/prop-desc.js
new file mode 100644
index 0000000000..95389899c9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochSeconds/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.instant.fromepochseconds
+description: The "fromEpochSeconds" property of Temporal.Instant
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Instant.fromEpochSeconds,
+ "function",
+ "`typeof Instant.fromEpochSeconds` is `function`"
+);
+
+verifyProperty(Temporal.Instant, "fromEpochSeconds", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochSeconds/shell.js b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochSeconds/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochSeconds/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/Instant/fromEpochSeconds/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochSeconds/subclassing-ignored.js
new file mode 100644
index 0000000000..3ec35427df
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochSeconds/subclassing-ignored.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.instant.fromepochseconds
+description: The receiver is never called by fromEpochSeconds()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnoredStatic(
+ Temporal.Instant,
+ "fromEpochSeconds",
+ [10],
+ (result) => {
+ assert.sameValue(result.epochNanoseconds, 10_000_000_000n, "epochNanoseconds result");
+ },
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/length.js b/js/src/tests/test262/built-ins/Temporal/Instant/length.js
new file mode 100644
index 0000000000..022d2307a3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/length.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.instant
+description: Temporal.Instant.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.Instant, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/name.js b/js/src/tests/test262/built-ins/Temporal/Instant/name.js
new file mode 100644
index 0000000000..3104b9c8f9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/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.instant
+description: Temporal.Instant.name is "Instant"
+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.Instant, "name", {
+ value: "Instant",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Instant/prop-desc.js
new file mode 100644
index 0000000000..ea4b300bbb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/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.instant
+description: The "Instant" property of Temporal
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Instant,
+ "function",
+ "`typeof Instant` is `function`"
+);
+
+verifyProperty(Temporal, "Instant", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/argument-duration-max.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/argument-duration-max.js
new file mode 100644
index 0000000000..ed141574d0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/argument-duration-max.js
@@ -0,0 +1,41 @@
+// |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.instant.prototype.add
+description: Maximum allowed duration
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(0n);
+
+const maxCases = [
+ ["PT2400000000H", "string with max hours"],
+ [{ hours: 2400000000 }, "property bag with max hours"],
+ ["PT144000000000M", "string with max minutes"],
+ [{ minutes: 144000000000 }, "property bag with max minutes"],
+ ["PT8640000000000S", "string with max seconds"],
+ [{ seconds: 8640000000000 }, "property bag with max seconds"],
+];
+
+for (const [arg, descr] of maxCases) {
+ const result = instance.add(arg);
+ assert.sameValue(result.epochNanoseconds, 8640000000000000000000n, `operation succeeds with ${descr}`);
+}
+
+const minCases = [
+ ["-PT2400000000H", "string with min hours"],
+ [{ hours: -2400000000 }, "property bag with min hours"],
+ ["-PT144000000000M", "string with min minutes"],
+ [{ minutes: -144000000000 }, "property bag with min minutes"],
+ ["-PT8640000000000S", "string with min seconds"],
+ [{ seconds: -8640000000000 }, "property bag with min seconds"],
+];
+
+for (const [arg, descr] of minCases) {
+ const result = instance.add(arg);
+ assert.sameValue(result.epochNanoseconds, -8640000000000000000000n, `operation succeeds with ${descr}`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/argument-duration-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/argument-duration-out-of-range.js
new file mode 100644
index 0000000000..88bf4b6c79
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/argument-duration-out-of-range.js
@@ -0,0 +1,75 @@
+// |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.instant.prototype.add
+description: Duration-like argument that is out of range
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(0n);
+
+const cases = [
+ // 2^32 = 4294967296
+ ["P4294967296Y", "string with years > max"],
+ [{ years: 4294967296 }, "property bag with years > max"],
+ ["-P4294967296Y", "string with years < min"],
+ [{ years: -4294967296 }, "property bag with years < min"],
+ ["P4294967296M", "string with months > max"],
+ [{ months: 4294967296 }, "property bag with months > max"],
+ ["-P4294967296M", "string with months < min"],
+ [{ months: -4294967296 }, "property bag with months < min"],
+ ["P4294967296W", "string with weeks > max"],
+ [{ weeks: 4294967296 }, "property bag with weeks > max"],
+ ["-P4294967296W", "string with weeks < min"],
+ [{ weeks: -4294967296 }, "property bag with weeks < min"],
+
+ // ceil(max safe integer / 86400) = 104249991375
+ ["P104249991375D", "string with days > max"],
+ [{ days: 104249991375 }, "property bag with days > max"],
+ ["P104249991374DT24H", "string where hours balance into days > max"],
+ [{ days: 104249991374, hours: 24 }, "property bag where hours balance into days > max"],
+ ["-P104249991375D", "string with days < min"],
+ [{ days: -104249991375 }, "property bag with days < min"],
+ ["-P104249991374DT24H", "string where hours balance into days < min"],
+ [{ days: -104249991374, hours: -24 }, "property bag where hours balance into days < min"],
+
+ // ceil(max safe integer / 3600) = 2501999792984
+ ["PT2501999792984H", "string with hours > max"],
+ [{ hours: 2501999792984 }, "property bag with hours > max"],
+ ["PT2501999792983H60M", "string where minutes balance into hours > max"],
+ [{ hours: 2501999792983, minutes: 60 }, "property bag where minutes balance into hours > max"],
+ ["-PT2501999792984H", "string with hours < min"],
+ [{ hours: -2501999792984 }, "property bag with hours < min"],
+ ["-PT2501999792983H60M", "string where minutes balance into hours < min"],
+ [{ hours: -2501999792983, minutes: -60 }, "property bag where minutes balance into hours < min"],
+
+ // ceil(max safe integer / 60) = 150119987579017
+ ["PT150119987579017M", "string with minutes > max"],
+ [{ minutes: 150119987579017 }, "property bag with minutes > max"],
+ ["PT150119987579016M60S", "string where seconds balance into minutes > max"],
+ [{ minutes: 150119987579016, seconds: 60 }, "property bag where seconds balance into minutes > max"],
+ ["-PT150119987579017M", "string with minutes < min"],
+ [{ minutes: -150119987579017 }, "property bag with minutes < min"],
+ ["-PT150119987579016M60S", "string where seconds balance into minutes < min"],
+ [{ minutes: -150119987579016, seconds: -60 }, "property bag where seconds balance into minutes < min"],
+
+ // 2^53 = 9007199254740992
+ ["PT9007199254740992S", "string with seconds > max"],
+ [{ seconds: 9007199254740992 }, "property bag with seconds > max"],
+ [{ seconds: 9007199254740991, milliseconds: 1000 }, "property bag where milliseconds balance into seconds > max"],
+ [{ seconds: 9007199254740991, microseconds: 1000000 }, "property bag where microseconds balance into seconds > max"],
+ [{ seconds: 9007199254740991, nanoseconds: 1000000000 }, "property bag where nanoseconds balance into seconds > max"],
+ ["-PT9007199254740992S", "string with seconds < min"],
+ [{ seconds: -9007199254740992 }, "property bag with seconds < min"],
+ [{ seconds: -9007199254740991, milliseconds: -1000 }, "property bag where milliseconds balance into seconds < min"],
+ [{ seconds: -9007199254740991, microseconds: -1000000 }, "property bag where microseconds balance into seconds < min"],
+ [{ seconds: -9007199254740991, nanoseconds: -1000000000 }, "property bag where nanoseconds balance into seconds < min"],
+];
+
+for (const [arg, descr] of cases) {
+ assert.throws(RangeError, () => instance.add(arg), `${descr} is out of range`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/argument-invalid-property.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/argument-invalid-property.js
new file mode 100644
index 0000000000..4ee3a0ebcb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/argument-invalid-property.js
@@ -0,0 +1,31 @@
+// |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.instant.prototype.add
+description: temporalDurationLike object must contain at least one correctly spelled property
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(1_000_000_000_000_000_000n);
+
+assert.throws(
+ TypeError,
+ () => instance.add({}),
+ "Throws TypeError if no property is present"
+);
+
+assert.throws(
+ TypeError,
+ () => instance.add({ nonsense: true }),
+ "Throws TypeError if no recognized property is present"
+);
+
+assert.throws(
+ TypeError,
+ () => instance.add({ sign: 1 }),
+ "Sign property is not recognized"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/argument-mixed-sign.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/argument-mixed-sign.js
new file mode 100644
index 0000000000..b3d5f3c4d7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/argument-mixed-sign.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.instant.prototype.add
+description: Positive and negative values in the temporalDurationLike argument are not acceptable
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(1_000_000_000_000_000_000n);
+
+assert.throws(
+ RangeError,
+ () => instance.add({ hours: 1, minutes: -30 }),
+ `mixed positive and negative values always throw`
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/argument-not-object.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/argument-not-object.js
new file mode 100644
index 0000000000..d5b928e2c0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/argument-not-object.js
@@ -0,0 +1,22 @@
+// |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.instant.prototype.add
+description: Passing a primitive other than string to add() throws
+features: [Symbol, Temporal]
+---*/
+
+const instance = new Temporal.Instant(1_000_000_000_000_000_000n);
+assert.throws(TypeError, () => instance.add(undefined), "undefined");
+assert.throws(TypeError, () => instance.add(null), "null");
+assert.throws(TypeError, () => instance.add(true), "boolean");
+assert.throws(RangeError, () => instance.add(""), "empty string");
+assert.throws(TypeError, () => instance.add(Symbol()), "Symbol");
+assert.throws(TypeError, () => instance.add(7), "number");
+assert.throws(TypeError, () => instance.add(7n), "bigint");
+assert.throws(TypeError, () => instance.add([]), "array");
+assert.throws(TypeError, () => instance.add(() => {}), "function");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/argument-singular-properties.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/argument-singular-properties.js
new file mode 100644
index 0000000000..a6f6ffe2ef
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/argument-singular-properties.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.instant.prototype.add
+description: Singular properties in the property bag are always ignored
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(1_000_000_000_000_000_000n);
+
+[
+ { year: 1 },
+ { month: 2 },
+ { week: 3 },
+ { day: 4 },
+ { hour: 5 },
+ { minute: 6 },
+ { second: 7 },
+ { millisecond: 8 },
+ { microsecond: 9 },
+ { nanosecond: 10 },
+].forEach((badObject) => {
+ assert.throws(TypeError, () => instance.add(badObject),
+ "Throw TypeError if temporalDurationLike is not valid");
+});
+
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/argument-string-fractional-units-rounding-mode.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/argument-string-fractional-units-rounding-mode.js
new file mode 100644
index 0000000000..245062df3a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/argument-string-fractional-units-rounding-mode.js
@@ -0,0 +1,18 @@
+// |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.instant.prototype.add
+description: Strings with fractional duration units are rounded with the correct rounding mode
+features: [Temporal]
+---*/
+
+const epoch = new Temporal.Instant(0n);
+
+assert.sameValue(epoch.add("PT1.03125H").epochNanoseconds, 3712_500_000_000n,
+ "positive fractional units rounded with correct rounding mode");
+assert.sameValue(epoch.add("-PT1.03125H").epochNanoseconds, -3712_500_000_000n,
+ "negative fractional units rounded with correct rounding mode");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/argument-string-negative-fractional-units.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/argument-string-negative-fractional-units.js
new file mode 100644
index 0000000000..01e08a4788
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/argument-string-negative-fractional-units.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.instant.prototype.add
+description: Strings with fractional duration units are treated with the correct sign
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(1_000_000_000_000_000_000n);
+
+const resultHours = instance.add("-PT24.567890123H");
+assert.sameValue(resultHours.epochNanoseconds, 999_911_555_595_557_200n, "negative fractional hours");
+
+const resultMinutes = instance.add("-PT1440.567890123M");
+assert.sameValue(resultMinutes.epochNanoseconds, 999_913_565_926_592_620n, "negative fractional minutes");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/argument-string.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/argument-string.js
new file mode 100644
index 0000000000..f115073838
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/argument-string.js
@@ -0,0 +1,15 @@
+// |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.instant.prototype.add
+description: A string is parsed into the correct object when passed as the argument
+features: [Temporal]
+---*/
+
+const instance = Temporal.Instant.fromEpochSeconds(10);
+const result = instance.add("PT3H");
+assert.sameValue(result.epochNanoseconds, 10_810_000_000_000n, "epochNanoseconds result");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/basic.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/basic.js
new file mode 100644
index 0000000000..5d817fabdd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/basic.js
@@ -0,0 +1,75 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.add
+description: Basic functionality of Temporal.Instant.prototype.add()
+info: |
+ 1. Let instant be the this value.
+ 2. Perform ? RequireInternalSlot(instant, [[InitializedTemporalInstant]]).
+ 3. Let duration be ? ToLimitedTemporalDuration(temporalDurationLike, « "years", "months", "weeks", "days" »).
+ 4. Let ns be ? AddInstant(instant.[[EpochNanoseconds]], duration.[[Hours]], duration.[[Minutes]], duration.[[Seconds]], duration.[[Milliseconds]], duration.[[Microseconds]], duration.[[Nanoseconds]]).
+ 5. Return ! CreateTemporalInstant(ns).
+features: [Temporal]
+---*/
+
+const inst = new Temporal.Instant(50000n);
+
+let result = inst.add(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 3, 2, 1));
+assert.sameValue(
+ 3052001n,
+ result.epochNanoseconds,
+ "add positive sub-seconds"
+);
+
+result = inst.add(new Temporal.Duration(0, 0, 0, 0, 0, 0, 4, 3, 2, 1));
+assert.sameValue(
+ BigInt(4 * 1e9) + 3052001n,
+ result.epochNanoseconds,
+ "add positive seconds"
+);
+
+result = inst.add(new Temporal.Duration(0, 0, 0, 0, 0, 5, 4, 3, 2, 1));
+assert.sameValue(
+ BigInt(5 * 60 + 4) * 1000000000n + 3052001n,
+ result.epochNanoseconds,
+ "add positive minutes"
+);
+
+result = inst.add(new Temporal.Duration(0, 0, 0, 0, 6, 5, 4, 3, 2, 1));
+assert.sameValue(
+ BigInt(6 * 3600 + 5 * 60 + 4) * 1000000000n + 3052001n,
+ result.epochNanoseconds,
+ "add positive hours"
+);
+
+result = inst.add(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, -3, -2, -1));
+assert.sameValue(
+ -2952001n,
+ result.epochNanoseconds,
+ "add negative sub-seconds"
+);
+
+result = inst.add(new Temporal.Duration(0, 0, 0, 0, 0, 0, -4, -3, -2, -1));
+assert.sameValue(
+ BigInt(-4 * 1e9) - 2952001n,
+ result.epochNanoseconds,
+ "add negative seconds"
+);
+
+result = inst.add(new Temporal.Duration(0, 0, 0, 0, 0, -5, -4, -3, -2, -1));
+assert.sameValue(
+ BigInt(5 * 60 + 4) * -1000000000n - 2952001n,
+ result.epochNanoseconds,
+ "add negative minutes"
+);
+
+result = inst.add(new Temporal.Duration(0, 0, 0, 0, -6, -5, -4, -3, -2, -1));
+assert.sameValue(
+ BigInt(6 * 3600 + 5 * 60 + 4) * -1000000000n - 2952001n,
+ result.epochNanoseconds,
+ "add negative hours"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/branding.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/branding.js
new file mode 100644
index 0000000000..e9da556c79
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/branding.js
@@ -0,0 +1,27 @@
+// |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.instant.prototype.add
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const add = Temporal.Instant.prototype.add;
+
+assert.sameValue(typeof add, "function");
+
+const args = [new Temporal.Duration(0, 0, 0, 0, 5)];
+
+assert.throws(TypeError, () => add.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => add.apply(null, args), "null");
+assert.throws(TypeError, () => add.apply(true, args), "true");
+assert.throws(TypeError, () => add.apply("", args), "empty string");
+assert.throws(TypeError, () => add.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => add.apply(1, args), "1");
+assert.throws(TypeError, () => add.apply({}, args), "plain object");
+assert.throws(TypeError, () => add.apply(Temporal.Instant, args), "Temporal.Instant");
+assert.throws(TypeError, () => add.apply(Temporal.Instant.prototype, args), "Temporal.Instant.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/browser.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/builtin.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/builtin.js
new file mode 100644
index 0000000000..e36db6b75c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/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.instant.prototype.add
+description: >
+ Tests that Temporal.Instant.prototype.add
+ 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.Instant.prototype.add),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Instant.prototype.add),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Instant.prototype.add),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Instant.prototype.add.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/disallowed-duration-units.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/disallowed-duration-units.js
new file mode 100644
index 0000000000..ff19dc2b3d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/disallowed-duration-units.js
@@ -0,0 +1,34 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.add
+description: |
+ Temporal.Instant.prototype.add() throws RangeError when the duration has
+ non-zero years, months, weeks, or days.
+info: |
+ 1. Let instant be the this value.
+ 3. Let duration be ? ToLimitedTemporalDuration(temporalDurationLike, « "years", "months", "weeks", "days" »).
+features: [Temporal]
+---*/
+
+const inst = new Temporal.Instant(500000n);
+assert.throws(RangeError, () => inst.add(new Temporal.Duration(1)),
+ "should throw RangeError when the duration has non-zero years (positive)");
+assert.throws(RangeError, () => inst.add(new Temporal.Duration(0, 2)),
+ "should throw RangeError when the duration has non-zero months (positive)");
+assert.throws(RangeError, () => inst.add(new Temporal.Duration(0, 0, 3)),
+ "should throw RangeError when the duration has non-zero weeks (positive)");
+assert.throws(RangeError, () => inst.add(new Temporal.Duration(0, 0, 0, 4)),
+ "should throw RangeError when the duration has non-zero days (positive)");
+assert.throws(RangeError, () => inst.add(new Temporal.Duration(-1)),
+ "should throw RangeError when the duration has non-zero years (negative)");
+assert.throws(RangeError, () => inst.add(new Temporal.Duration(0, -2)),
+ "should throw RangeError when the duration has non-zero months (negative)");
+assert.throws(RangeError, () => inst.add(new Temporal.Duration(0, 0, -3)),
+ "should throw RangeError when the duration has non-zero weeks (negative)");
+assert.throws(RangeError, () => inst.add(new Temporal.Duration(0, 0, 0, -4)),
+ "should throw RangeError when the duration has non-zero days (negative)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..b093b8bd0e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/infinity-throws-rangeerror.js
@@ -0,0 +1,33 @@
+// |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.
+
+/*---
+description: Temporal.Instant.prototype.add throws a RangeError if any value in a property bag is Infinity
+esid: sec-temporal.instant.prototype.add
+features: [Temporal]
+---*/
+
+const fields = ["hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"];
+
+const instance = Temporal.Instant.fromEpochSeconds(10);
+
+fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.add({ [field]: Infinity }));
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return Infinity;
+ }
+};
+
+fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => instance.add({ [field]: obj }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/length.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/length.js
new file mode 100644
index 0000000000..a1ce135c57
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/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.instant.prototype.add
+description: Temporal.Instant.prototype.add.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.Instant.prototype.add, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/minimum-maximum-instant.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/minimum-maximum-instant.js
new file mode 100644
index 0000000000..c3f72f84d1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/minimum-maximum-instant.js
@@ -0,0 +1,63 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.add
+description: >
+ Instant is minimum/maximum instant.
+features: [Temporal]
+---*/
+
+let min = new Temporal.Instant(-86_40000_00000_00000_00000n);
+let max = new Temporal.Instant(86_40000_00000_00000_00000n);
+
+let zero = Temporal.Duration.from({nanoseconds: 0});
+let one = Temporal.Duration.from({nanoseconds: 1});
+let minusOne = Temporal.Duration.from({nanoseconds: -1});
+
+assert.sameValue(min.add(zero).epochNanoseconds, min.epochNanoseconds,
+ "Adding zero to the minimum instant");
+
+assert.sameValue(max.add(zero).epochNanoseconds, max.epochNanoseconds,
+ "Adding zero to the maximum instant");
+
+assert.throws(RangeError, () => min.add(minusOne),
+ "Subtracting one from the minimum instant");
+
+assert.throws(RangeError, () => max.add(one),
+ "Adding one to the maximum instant");
+
+assert.sameValue(min.add(one).epochNanoseconds, min.epochNanoseconds + 1n,
+ "Adding one to the minimum instant");
+
+assert.sameValue(max.add(minusOne).epochNanoseconds, max.epochNanoseconds - 1n,
+ "Subtracting one from the maximum instant");
+
+// From minimum to maximum instant.
+assert.sameValue(min.add({nanoseconds: 86_40000_00000_00000_00000 * 2}).epochNanoseconds, max.epochNanoseconds,
+ "Minimum to maximum instant by adding nanoseconds");
+
+assert.sameValue(min.add({microseconds: 8640_00000_00000_00000 * 2}).epochNanoseconds, max.epochNanoseconds,
+ "Minimum to maximum instant by adding microseconds");
+
+assert.sameValue(min.add({milliseconds: 8_64000_00000_00000 * 2}).epochNanoseconds, max.epochNanoseconds,
+ "Minimum to maximum instant by adding milliseconds");
+
+assert.sameValue(min.add({seconds: 864_00000_00000 * 2}).epochNanoseconds, max.epochNanoseconds,
+ "Minimum to maximum instant by adding seconds");
+
+// From maximum to minimum instant.
+assert.sameValue(max.add({nanoseconds: -86_40000_00000_00000_00000 * 2}).epochNanoseconds, min.epochNanoseconds,
+ "Maximum to minimum instant by adding nanoseconds");
+
+assert.sameValue(max.add({microseconds: -8640_00000_00000_00000 * 2}).epochNanoseconds, min.epochNanoseconds,
+ "Maximum to minimum instant by adding microseconds");
+
+assert.sameValue(max.add({milliseconds: -8_64000_00000_00000 * 2}).epochNanoseconds, min.epochNanoseconds,
+ "Maximum to minimum instant by adding milliseconds");
+
+assert.sameValue(max.add({seconds: -864_00000_00000 * 2}).epochNanoseconds, min.epochNanoseconds,
+ "Maximum to minimum instant by adding seconds");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/name.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/name.js
new file mode 100644
index 0000000000..3eb53f48cf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/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.instant.prototype.add
+description: Temporal.Instant.prototype.add.name is "add".
+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.Instant.prototype.add, "name", {
+ value: "add",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/negative-infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/negative-infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..425c8cca25
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/negative-infinity-throws-rangeerror.js
@@ -0,0 +1,33 @@
+// |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.
+
+/*---
+description: Temporal.Instant.prototype.add throws a RangeError if any value in a property bag is -Infinity
+esid: sec-temporal.instant.prototype.add
+features: [Temporal]
+---*/
+
+const fields = ["hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"];
+
+const instance = Temporal.Instant.fromEpochSeconds(10);
+
+fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.add({ [field]: -Infinity }));
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return -Infinity;
+ }
+};
+
+fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => instance.add({ [field]: obj }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/non-integer-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/non-integer-throws-rangeerror.js
new file mode 100644
index 0000000000..a7333de66d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/non-integer-throws-rangeerror.js
@@ -0,0 +1,29 @@
+// |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.instant.prototype.add
+description: A non-integer value for any recognized property in the property bag, throws a RangeError
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(1_000_000_000_000_000_000n);
+const fields = [
+ "years",
+ "months",
+ "weeks",
+ "days",
+ "hours",
+ "minutes",
+ "seconds",
+ "milliseconds",
+ "microseconds",
+ "nanoseconds",
+];
+fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.add({ [field]: 1.5 }));
+ assert.throws(RangeError, () => instance.add({ [field]: -1.5 }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/not-a-constructor.js
new file mode 100644
index 0000000000..40d88e065f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/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.instant.prototype.add
+description: >
+ Temporal.Instant.prototype.add 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.Instant.prototype.add();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Instant.prototype.add), false,
+ "isConstructor(Temporal.Instant.prototype.add)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/order-of-operations.js
new file mode 100644
index 0000000000..7e76b6de5f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/order-of-operations.js
@@ -0,0 +1,50 @@
+// |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.instant.prototype.add
+description: Properties on an object passed to add() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(10n);
+const expected = [
+ "get fields.days",
+ "get fields.hours",
+ "get fields.hours.valueOf",
+ "call fields.hours.valueOf",
+ "get fields.microseconds",
+ "get fields.microseconds.valueOf",
+ "call fields.microseconds.valueOf",
+ "get fields.milliseconds",
+ "get fields.milliseconds.valueOf",
+ "call fields.milliseconds.valueOf",
+ "get fields.minutes",
+ "get fields.minutes.valueOf",
+ "call fields.minutes.valueOf",
+ "get fields.months",
+ "get fields.nanoseconds",
+ "get fields.nanoseconds.valueOf",
+ "call fields.nanoseconds.valueOf",
+ "get fields.seconds",
+ "get fields.seconds.valueOf",
+ "call fields.seconds.valueOf",
+ "get fields.weeks",
+ "get fields.years",
+];
+const actual = [];
+const fields = TemporalHelpers.propertyBagObserver(actual, {
+ hours: 1,
+ minutes: 1,
+ seconds: 1,
+ milliseconds: 1,
+ microseconds: 1,
+ nanoseconds: 1,
+}, "fields");
+const result = instance.add(fields);
+assert.sameValue(result.epochNanoseconds, 3661001001011n, "epochNanoseconds result");
+assert.compareArray(actual, expected, "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/prop-desc.js
new file mode 100644
index 0000000000..3b4b6a1997
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/prop-desc.js
@@ -0,0 +1,24 @@
+// |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.instant.prototype.add
+description: The "add" property of Temporal.Instant.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Instant.prototype.add,
+ "function",
+ "`typeof Instant.prototype.add` is `function`"
+);
+
+verifyProperty(Temporal.Instant.prototype, "add", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/result-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/result-out-of-range.js
new file mode 100644
index 0000000000..4ccf2a77e8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/result-out-of-range.js
@@ -0,0 +1,33 @@
+// |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.instant.prototype.add
+description: RangeError thrown if result is outside representable range
+features: [Temporal]
+---*/
+
+const fields = ["hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"];
+
+const latest = Temporal.Instant.fromEpochNanoseconds(8640000_000_000_000_000_000n);
+
+fields.forEach((field) => {
+ assert.throws(
+ RangeError,
+ () => latest.add({ [field]: 1 }),
+ `adding ${field} with result out of range (positive)`
+ );
+});
+
+const earliest = Temporal.Instant.fromEpochNanoseconds(-8640000_000_000_000_000_000n);
+
+fields.forEach((field) => {
+ assert.throws(
+ RangeError,
+ () => earliest.add({ [field]: -1 }),
+ `adding ${field} with result out of range (negative)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/shell.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/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/Instant/prototype/add/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/subclassing-ignored.js
new file mode 100644
index 0000000000..cb25386107
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/add/subclassing-ignored.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.instant.prototype.add
+description: Objects of a subclass are never created as return values for add()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.Instant,
+ [10n],
+ "add",
+ [{ nanoseconds: 5 }],
+ (result) => {
+ assert.sameValue(result.epochNanoseconds, 15n, "epochNanoseconds result");
+ },
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/browser.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/builtin.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/builtin.js
new file mode 100644
index 0000000000..d65ac2183a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/builtin.js
@@ -0,0 +1,35 @@
+// |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-instant-objects
+description: Temporal.Instant.prototype meets the requirements for built-in objects
+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]
+---*/
+
+const { Instant } = Temporal;
+
+assert.sameValue(Object.isExtensible(Instant.prototype), true,
+ "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Instant.prototype),
+ "[object Temporal.Instant]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Instant.prototype), Object.prototype,
+ "Built-in prototype objects must have Object.prototype as their prototype.");
+
+assert.sameValue(Instant.prototype.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/constructor.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/constructor.js
new file mode 100644
index 0000000000..19e51b8d2b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/constructor.js
@@ -0,0 +1,20 @@
+// |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.instant.prototype.constructor
+description: Test for Temporal.Instant.prototype.constructor.
+info: The initial value of Temporal.Instant.prototype.constructor is %Temporal.Instant%.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Instant.prototype, "constructor", {
+ value: Temporal.Instant,
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochMicroseconds/basic.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochMicroseconds/basic.js
new file mode 100644
index 0000000000..bad6d3b236
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochMicroseconds/basic.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-get-temporal.instant.prototype.epochmicroseconds
+description: Basic tests for epochMicroseconds.
+features: [BigInt, Temporal]
+---*/
+
+const afterEpoch = new Temporal.Instant(217175010_123_456_789n);
+assert.sameValue(afterEpoch.epochMicroseconds, 217175010_123_456n, "epochMicroseconds post epoch");
+assert.sameValue(typeof afterEpoch.epochMicroseconds, "bigint", "epochMicroseconds value is a bigint");
+
+const beforeEpoch = new Temporal.Instant(-217175010_876_543_211n);
+assert.sameValue(beforeEpoch.epochMicroseconds, -217175010_876_544n, "epochMicroseconds pre epoch");
+assert.sameValue(typeof beforeEpoch.epochMicroseconds, "bigint", "epochMicroseconds value is a bigint");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochMicroseconds/branding.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochMicroseconds/branding.js
new file mode 100644
index 0000000000..e589ee3d3a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochMicroseconds/branding.js
@@ -0,0 +1,25 @@
+// |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-get-temporal.instant.prototype.epochmicroseconds
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const epochMicroseconds = Object.getOwnPropertyDescriptor(Temporal.Instant.prototype, "epochMicroseconds").get;
+
+assert.sameValue(typeof epochMicroseconds, "function");
+
+assert.throws(TypeError, () => epochMicroseconds.call(undefined), "undefined");
+assert.throws(TypeError, () => epochMicroseconds.call(null), "null");
+assert.throws(TypeError, () => epochMicroseconds.call(true), "true");
+assert.throws(TypeError, () => epochMicroseconds.call(""), "empty string");
+assert.throws(TypeError, () => epochMicroseconds.call(Symbol()), "symbol");
+assert.throws(TypeError, () => epochMicroseconds.call(1), "1");
+assert.throws(TypeError, () => epochMicroseconds.call({}), "plain object");
+assert.throws(TypeError, () => epochMicroseconds.call(Temporal.Instant), "Temporal.Instant");
+assert.throws(TypeError, () => epochMicroseconds.call(Temporal.Instant.prototype), "Temporal.Instant.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochMicroseconds/browser.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochMicroseconds/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochMicroseconds/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochMicroseconds/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochMicroseconds/prop-desc.js
new file mode 100644
index 0000000000..e0d19f3fb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochMicroseconds/prop-desc.js
@@ -0,0 +1,17 @@
+// |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-get-temporal.instant.prototype.epochmicroseconds
+description: The "epochMicroseconds" property of Temporal.Instant.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.Instant.prototype, "epochMicroseconds");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochMicroseconds/shell.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochMicroseconds/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochMicroseconds/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochMilliseconds/basic.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochMilliseconds/basic.js
new file mode 100644
index 0000000000..fe70f7acb4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochMilliseconds/basic.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-get-temporal.instant.prototype.epochmilliseconds
+description: Basic tests for epochMilliseconds.
+features: [BigInt, Temporal]
+---*/
+
+const afterEpoch = new Temporal.Instant(217175010_123_456_789n);
+assert.sameValue(afterEpoch.epochMilliseconds, 217175010_123, "epochMilliseconds post epoch");
+assert.sameValue(typeof afterEpoch.epochMilliseconds, "number", "epochMilliseconds value is a number");
+
+const beforeEpoch = new Temporal.Instant(-217175010_876_543_211n);
+assert.sameValue(beforeEpoch.epochMilliseconds, -217175010_877, "epochMilliseconds pre epoch");
+assert.sameValue(typeof beforeEpoch.epochMilliseconds, "number", "epochMilliseconds value is a number");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochMilliseconds/branding.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochMilliseconds/branding.js
new file mode 100644
index 0000000000..5fb34cfc13
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochMilliseconds/branding.js
@@ -0,0 +1,25 @@
+// |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-get-temporal.instant.prototype.epochmilliseconds
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const epochMilliseconds = Object.getOwnPropertyDescriptor(Temporal.Instant.prototype, "epochMilliseconds").get;
+
+assert.sameValue(typeof epochMilliseconds, "function");
+
+assert.throws(TypeError, () => epochMilliseconds.call(undefined), "undefined");
+assert.throws(TypeError, () => epochMilliseconds.call(null), "null");
+assert.throws(TypeError, () => epochMilliseconds.call(true), "true");
+assert.throws(TypeError, () => epochMilliseconds.call(""), "empty string");
+assert.throws(TypeError, () => epochMilliseconds.call(Symbol()), "symbol");
+assert.throws(TypeError, () => epochMilliseconds.call(1), "1");
+assert.throws(TypeError, () => epochMilliseconds.call({}), "plain object");
+assert.throws(TypeError, () => epochMilliseconds.call(Temporal.Instant), "Temporal.Instant");
+assert.throws(TypeError, () => epochMilliseconds.call(Temporal.Instant.prototype), "Temporal.Instant.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochMilliseconds/browser.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochMilliseconds/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochMilliseconds/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochMilliseconds/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochMilliseconds/prop-desc.js
new file mode 100644
index 0000000000..2f7017d307
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochMilliseconds/prop-desc.js
@@ -0,0 +1,17 @@
+// |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-get-temporal.instant.prototype.epochmilliseconds
+description: The "epochMilliseconds" property of Temporal.Instant.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.Instant.prototype, "epochMilliseconds");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochMilliseconds/shell.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochMilliseconds/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochMilliseconds/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochNanoseconds/basic.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochNanoseconds/basic.js
new file mode 100644
index 0000000000..ff8a387d6c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochNanoseconds/basic.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-get-temporal.instant.prototype.epochnanoseconds
+description: Basic tests for epochNanoseconds.
+features: [BigInt, Temporal]
+---*/
+
+const afterEpoch = new Temporal.Instant(217175010_123_456_789n);
+assert.sameValue(afterEpoch.epochNanoseconds, 217175010_123_456_789n, "epochNanoseconds post epoch");
+assert.sameValue(typeof afterEpoch.epochNanoseconds, "bigint", "epochNanoseconds value is a bigint");
+
+const beforeEpoch = new Temporal.Instant(-217175010_876_543_211n);
+assert.sameValue(beforeEpoch.epochNanoseconds, -217175010_876_543_211n, "epochNanoseconds pre epoch");
+assert.sameValue(typeof beforeEpoch.epochNanoseconds, "bigint", "epochNanoseconds value is a bigint");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochNanoseconds/branding.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochNanoseconds/branding.js
new file mode 100644
index 0000000000..4f4aa86f92
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochNanoseconds/branding.js
@@ -0,0 +1,25 @@
+// |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-get-temporal.instant.prototype.epochnanoseconds
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const epochNanoseconds = Object.getOwnPropertyDescriptor(Temporal.Instant.prototype, "epochNanoseconds").get;
+
+assert.sameValue(typeof epochNanoseconds, "function");
+
+assert.throws(TypeError, () => epochNanoseconds.call(undefined), "undefined");
+assert.throws(TypeError, () => epochNanoseconds.call(null), "null");
+assert.throws(TypeError, () => epochNanoseconds.call(true), "true");
+assert.throws(TypeError, () => epochNanoseconds.call(""), "empty string");
+assert.throws(TypeError, () => epochNanoseconds.call(Symbol()), "symbol");
+assert.throws(TypeError, () => epochNanoseconds.call(1), "1");
+assert.throws(TypeError, () => epochNanoseconds.call({}), "plain object");
+assert.throws(TypeError, () => epochNanoseconds.call(Temporal.Instant), "Temporal.Instant");
+assert.throws(TypeError, () => epochNanoseconds.call(Temporal.Instant.prototype), "Temporal.Instant.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochNanoseconds/browser.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochNanoseconds/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochNanoseconds/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochNanoseconds/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochNanoseconds/prop-desc.js
new file mode 100644
index 0000000000..d6c81c90d1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochNanoseconds/prop-desc.js
@@ -0,0 +1,17 @@
+// |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-get-temporal.instant.prototype.epochnanoseconds
+description: The "epochNanoseconds" property of Temporal.Instant.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.Instant.prototype, "epochNanoseconds");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochNanoseconds/shell.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochNanoseconds/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochNanoseconds/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochSeconds/basic.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochSeconds/basic.js
new file mode 100644
index 0000000000..56b20821e0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochSeconds/basic.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-get-temporal.instant.prototype.epochseconds
+description: Basic tests for epochSeconds.
+features: [BigInt, Temporal]
+---*/
+
+const afterEpoch = new Temporal.Instant(217175010_123_456_789n);
+assert.sameValue(afterEpoch.epochSeconds, 217175010, "epochSeconds post epoch");
+assert.sameValue(typeof afterEpoch.epochSeconds, "number", "epochSeconds value is a number");
+
+const beforeEpoch = new Temporal.Instant(-217175010_876_543_211n);
+assert.sameValue(beforeEpoch.epochSeconds, -217175011, "epochSeconds pre epoch");
+assert.sameValue(typeof beforeEpoch.epochSeconds, "number", "epochSeconds value is a number");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochSeconds/branding.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochSeconds/branding.js
new file mode 100644
index 0000000000..88baf06af8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochSeconds/branding.js
@@ -0,0 +1,25 @@
+// |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-get-temporal.instant.prototype.epochseconds
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const epochSeconds = Object.getOwnPropertyDescriptor(Temporal.Instant.prototype, "epochSeconds").get;
+
+assert.sameValue(typeof epochSeconds, "function");
+
+assert.throws(TypeError, () => epochSeconds.call(undefined), "undefined");
+assert.throws(TypeError, () => epochSeconds.call(null), "null");
+assert.throws(TypeError, () => epochSeconds.call(true), "true");
+assert.throws(TypeError, () => epochSeconds.call(""), "empty string");
+assert.throws(TypeError, () => epochSeconds.call(Symbol()), "symbol");
+assert.throws(TypeError, () => epochSeconds.call(1), "1");
+assert.throws(TypeError, () => epochSeconds.call({}), "plain object");
+assert.throws(TypeError, () => epochSeconds.call(Temporal.Instant), "Temporal.Instant");
+assert.throws(TypeError, () => epochSeconds.call(Temporal.Instant.prototype), "Temporal.Instant.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochSeconds/browser.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochSeconds/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochSeconds/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochSeconds/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochSeconds/prop-desc.js
new file mode 100644
index 0000000000..1a156df0b4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochSeconds/prop-desc.js
@@ -0,0 +1,17 @@
+// |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-get-temporal.instant.prototype.epochseconds
+description: The "epochSeconds" property of Temporal.Instant.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.Instant.prototype, "epochSeconds");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochSeconds/shell.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochSeconds/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/epochSeconds/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/argument-object-tostring.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/argument-object-tostring.js
new file mode 100644
index 0000000000..379c097135
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/argument-object-tostring.js
@@ -0,0 +1,22 @@
+// |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.instant.prototype.equals
+description: Object is converted to a string, then to Temporal.Instant
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(0n);
+
+const arg = {};
+assert.throws(RangeError, () => instance.equals(arg), "[object Object] is not a valid ISO string");
+
+arg.toString = function() {
+ return "1970-01-01T00:00Z";
+};
+const result = instance.equals(arg);
+assert.sameValue(result, true, "result of toString is interpreted as ISO string");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..97a8ba4d97
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/argument-string-calendar-annotation.js
@@ -0,0 +1,33 @@
+// |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.instant.prototype.equals
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["1970-01-01T00:00Z[u-ca=iso8601]", "without time zone"],
+ ["1970-01-01T00:00Z[UTC][u-ca=gregory]", "with time zone"],
+ ["1970-01-01T00:00Z[!u-ca=hebrew]", "with ! and no time zone"],
+ ["1970-01-01T00:00Z[UTC][!u-ca=chinese]", "with ! and time zone"],
+ ["1970-01-01T00:00Z[u-ca=discord]", "annotation is ignored"],
+ ["1970-01-01T00:00Z[!u-ca=discord]", "annotation with ! is ignored"],
+ ["1970-01-01T00:00Z[u-ca=iso8601][u-ca=discord]", "two annotations are ignored"],
+];
+
+const instance = new Temporal.Instant(0n);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.equals(arg);
+
+ assert.sameValue(
+ result,
+ true,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..431cabec16
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/argument-string-critical-unknown-annotation.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.instant.prototype.equals
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01T00:00Z[!foo=bar]",
+ "1970-01-01T00:00Z[UTC][!foo=bar]",
+ "1970-01-01T00:00Z[u-ca=iso8601][!foo=bar]",
+ "1970-01-01T00:00Z[UTC][!foo=bar][u-ca=iso8601]",
+ "1970-01-01T00:00Z[foo=bar][!_foo-bar0=Dont-Ignore-This-99999999999]",
+];
+const instance = new Temporal.Instant(0n);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..b462ed2668
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/argument-string-date-with-utc-offset.js
@@ -0,0 +1,52 @@
+// |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.instant.prototype.equals
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(0n);
+
+const validStrings = [
+ "1970-01-01T00Z",
+ "1970-01-01T00Z[UTC]",
+ "1970-01-01T00Z[!UTC]",
+ "1970-01-01T00Z[Europe/Vienna]",
+ "1970-01-01T00+00:00",
+ "1970-01-01T00+00:00[UTC]",
+ "1970-01-01T00+00:00[!UTC]",
+ "1969-12-31T16-08:00[America/Vancouver]",
+];
+
+for (const arg of validStrings) {
+ const result = instance.equals(arg);
+
+ assert.sameValue(
+ result,
+ true,
+ `"${arg}" is a valid UTC offset with time for Instant`
+ );
+}
+
+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.equals(arg),
+ `"${arg}" UTC offset without time is not valid for Instant`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/argument-string-invalid.js
new file mode 100644
index 0000000000..468390345e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/argument-string-invalid.js
@@ -0,0 +1,69 @@
+// |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.instant.prototype.equals
+description: >
+ RangeError thrown if an invalid ISO string (or syntactically valid ISO string
+ that is not supported) is used as an Instant
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ // invalid ISO strings:
+ "",
+ "invalid iso8601",
+ "2020-01-00T00:00Z",
+ "2020-01-32T00:00Z",
+ "2020-02-30T00:00Z",
+ "2021-02-29T00:00Z",
+ "2020-00-01T00:00Z",
+ "2020-13-01T00:00Z",
+ "2020-01-01TZ",
+ "2020-01-01T25:00:00Z",
+ "2020-01-01T01:60:00Z",
+ "2020-01-01T01:60:61Z",
+ "2020-01-01T00:00Zjunk",
+ "2020-01-01T00:00:00Zjunk",
+ "2020-01-01T00:00:00.000000000Zjunk",
+ "2020-01-01T00:00:00+00:00junk",
+ "2020-01-01T00:00:00+00:00[UTC]junk",
+ "2020-01-01T00:00:00+00:00[UTC][u-ca=iso8601]junk",
+ "02020-01-01T00:00Z",
+ "2020-001-01T00:00Z",
+ "2020-01-001T00:00Z",
+ "2020-01-01T001Z",
+ "2020-01-01T01:001Z",
+ "2020-01-01T01:01:001Z",
+ // valid, but forms not supported in Temporal:
+ "2020-W01-1T00:00Z",
+ "2020-001T00:00Z",
+ "+0002020-01-01T00:00Z",
+ // may be valid in other contexts, but insufficient information for Instant:
+ "2020-01",
+ "+002020-01",
+ "01-01",
+ "2020-W01",
+ "P1Y",
+ "-P12Y",
+ "2020-01-01",
+ "2020-01-01T00",
+ "2020-01-01T00:00",
+ "2020-01-01T00:00:00",
+ "2020-01-01T00:00:00.000000000",
+ // valid, but outside the supported range:
+ "-999999-01-01T00:00Z",
+ "+999999-01-01T00:00Z",
+];
+
+const instance = new Temporal.Instant(0n);
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ `"${arg}" should not be a valid ISO string for an Instant`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..382b13e209
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/argument-string-multiple-calendar.js
@@ -0,0 +1,28 @@
+// |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.instant.prototype.equals
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01T00:00Z[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00Z[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01T00:00Z[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00Z[u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+];
+const instance = new Temporal.Instant(0n);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..edbd73a3bc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/argument-string-multiple-time-zone.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.instant.prototype.equals
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01T00:00Z[UTC][UTC]",
+ "1970-01-01T00:00Z[!UTC][UTC]",
+ "1970-01-01T00:00Z[UTC][!UTC]",
+ "1970-01-01T00:00Z[UTC][u-ca=iso8601][UTC]",
+ "1970-01-01T00:00Z[UTC][foo=bar][UTC]",
+];
+const instance = new Temporal.Instant(0n);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/argument-string-time-separators.js
new file mode 100644
index 0000000000..06941e3933
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/argument-string-time-separators.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.instant.prototype.equals
+description: Time separator in string argument can vary
+features: [Temporal]
+---*/
+
+const tests = [
+ ["1970-01-01T00:00Z", "uppercase T"],
+ ["1970-01-01t00:00Z", "lowercase T"],
+ ["1970-01-01 00:00Z", "space between date and time"],
+];
+
+const instance = new Temporal.Instant(0n);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.equals(arg);
+
+ assert.sameValue(
+ result,
+ true,
+ `variant time separators (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..46ae6d3609
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/argument-string-time-zone-annotation.js
@@ -0,0 +1,34 @@
+// |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.instant.prototype.equals
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["1970-01-01T00:00Z[Asia/Kolkata]", "named, with Z"],
+ ["1970-01-01T00:00Z[!Europe/Vienna]", "named, with Z and !"],
+ ["1970-01-01T00:00Z[+00:00]", "numeric, with Z"],
+ ["1970-01-01T00:00Z[!-02:30]", "numeric, with Z and !"],
+ ["1970-01-01T00:00+00:00[UTC]", "named, with offset"],
+ ["1970-01-01T00:00+00:00[!Africa/Abidjan]", "named, with offset and !"],
+ ["1970-01-01T00:00+00:00[-08:00]", "numeric, with offset"],
+ ["1970-01-01T00:00+00:00[!+01:00]", "numeric, with offset and !"],
+];
+
+const instance = new Temporal.Instant(0n);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.equals(arg);
+
+ assert.sameValue(
+ result,
+ true,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..697dcf737e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/argument-string-unknown-annotation.js
@@ -0,0 +1,31 @@
+// |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.instant.prototype.equals
+description: Various forms of unknown annotation
+features: [Temporal]
+---*/
+
+const tests = [
+ ["1970-01-01T00:00Z[foo=bar]", "alone"],
+ ["1970-01-01T00:00Z[UTC][foo=bar]", "with time zone"],
+ ["1970-01-01T00:00Z[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["1970-01-01T00:00Z[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["1970-01-01T00:00Z[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+const instance = new Temporal.Instant(0n);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.equals(arg);
+
+ assert.sameValue(
+ result,
+ true,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/argument-wrong-type.js
new file mode 100644
index 0000000000..147c3d493e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/argument-wrong-type.js
@@ -0,0 +1,46 @@
+// |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.instant.prototype.equals
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ for Instant
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.Instant(0n);
+
+const primitiveTests = [
+ [undefined, "undefined"],
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [19761118, "number that would convert to a valid ISO string in other contexts"],
+ [1n, "bigint"],
+ [{}, "plain object"],
+ [Temporal.Instant, "Temporal.Instant, object"],
+];
+
+for (const [arg, description] of primitiveTests) {
+ assert.throws(
+ typeof arg === "string" || (typeof arg === "object" && arg !== null) || typeof arg === "function"
+ ? RangeError
+ : TypeError,
+ () => instance.equals(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [Temporal.Instant.prototype, "Temporal.Instant.prototype, object"], // fails brand check in toString()
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.equals(arg), `${description} does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/argument-zoneddatetime.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/argument-zoneddatetime.js
new file mode 100644
index 0000000000..d7ada9255e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/argument-zoneddatetime.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.instant.prototype.until
+description: Fast path for converting Temporal.ZonedDateTime to Temporal.Instant
+info: |
+ sec-temporal.instant.prototype.until step 3:
+ 3. Set _other_ to ? ToTemporalInstant(_other_).
+ sec-temporal-totemporalinstant step 1.b:
+ b. If _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return ! CreateTemporalInstant(_item_.[[Nanoseconds]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalInstantFastPath((datetime) => {
+ const instant = new Temporal.Instant(1_000_000_000_987_654_321n);
+ assert(instant.equals(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/branding.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/branding.js
new file mode 100644
index 0000000000..fcda123776
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/branding.js
@@ -0,0 +1,27 @@
+// |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.instant.prototype.equals
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const equals = Temporal.Instant.prototype.equals;
+
+assert.sameValue(typeof equals, "function");
+
+const args = [new Temporal.Instant(123456n)];
+
+assert.throws(TypeError, () => equals.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => equals.apply(null, args), "null");
+assert.throws(TypeError, () => equals.apply(true, args), "true");
+assert.throws(TypeError, () => equals.apply("", args), "empty string");
+assert.throws(TypeError, () => equals.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => equals.apply(1, args), "1");
+assert.throws(TypeError, () => equals.apply({}, args), "plain object");
+assert.throws(TypeError, () => equals.apply(Temporal.Instant, args), "Temporal.Instant");
+assert.throws(TypeError, () => equals.apply(Temporal.Instant.prototype, args), "Temporal.Instant.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/browser.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/builtin.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/builtin.js
new file mode 100644
index 0000000000..c13b467fa1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/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.instant.prototype.equals
+description: >
+ Tests that Temporal.Instant.prototype.equals
+ 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.Instant.prototype.equals),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Instant.prototype.equals),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Instant.prototype.equals),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Instant.prototype.equals.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/instant-string-limits.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/instant-string-limits.js
new file mode 100644
index 0000000000..3b301bae92
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/instant-string-limits.js
@@ -0,0 +1,48 @@
+// |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.instant.prototype.equals
+description: String arguments at the limit of the representable range
+features: [Temporal]
+---*/
+
+const minInstant = new Temporal.Instant(-86400_00000000_000_000_000n);
+const maxInstant = new Temporal.Instant(86400_00000000_000_000_000n);
+
+const minInstantStrings = [
+ "-271821-04-20T00:00Z",
+ "-271821-04-19T23:00-01:00",
+ "-271821-04-19T00:00:00.000000001-23:59:59.999999999",
+];
+for (const str of minInstantStrings) {
+ assert.sameValue(minInstant.equals(str), true, `instant string ${str} should be valid`);
+}
+
+const maxInstantStrings = [
+ "+275760-09-13T00:00Z",
+ "+275760-09-13T01:00+01:00",
+ "+275760-09-13T23:59:59.999999999+23:59:59.999999999",
+];
+
+for (const str of maxInstantStrings) {
+ assert.sameValue(maxInstant.equals(str), true, `instant string ${str} should be valid`);
+}
+
+const outOfRangeInstantStrings = [
+ "-271821-04-19T23:59:59.999999999Z",
+ "-271821-04-19T23:00-00:59:59.999999999",
+ "-271821-04-19T00:00:00-23:59:59.999999999",
+ "-271821-04-19T00:00:00-24:00",
+ "+275760-09-13T00:00:00.000000001Z",
+ "+275760-09-13T01:00+00:59:59.999999999",
+ "+275760-09-14T00:00+23:59:59.999999999",
+ "+275760-09-14T00:00+24:00",
+];
+
+for (const str of outOfRangeInstantStrings) {
+ assert.throws(RangeError, () => minInstant.equals(str), `instant string ${str} should not be valid`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/instant-string-multiple-offsets.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/instant-string-multiple-offsets.js
new file mode 100644
index 0000000000..9ab098382d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/instant-string-multiple-offsets.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.instant.prototype.equals
+description: Instant strings with UTC offset fractional part are not confused with time fractional part
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(0n);
+const str = "1970-01-01T00:02:00.000000000+00:02[+01:30]";
+
+const result = instance.equals(str);
+assert.sameValue(result, true, "UTC offset determined from offset part of string");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/instant-string-sub-minute-offset.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/instant-string-sub-minute-offset.js
new file mode 100644
index 0000000000..2c7f981e59
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/instant-string-sub-minute-offset.js
@@ -0,0 +1,67 @@
+// |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.instant.prototype.equals
+description: Temporal.Instant string with sub-minute offset
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(0n);
+
+const str = "1970-01-01T00:19:32.37+00:19:32.37";
+const result = instance.equals(str);
+assert.sameValue(result, true, "if present, sub-minute offset is accepted exactly");
+
+[
+ "2021-08-19T17:30-07:00:01[-07:00:01]",
+ "2021-08-19T17:30-07:00:00[-07:00:00]",
+ "2021-08-19T17:30-07:00:00.1[-07:00:00.1]",
+ "2021-08-19T17:30-07:00:00.0[-07:00:00.0]",
+ "2021-08-19T17:30-07:00:00.01[-07:00:00.01]",
+ "2021-08-19T17:30-07:00:00.00[-07:00:00.00]",
+ "2021-08-19T17:30-07:00:00.001[-07:00:00.001]",
+ "2021-08-19T17:30-07:00:00.000[-07:00:00.000]",
+ "2021-08-19T17:30-07:00:00.0001[-07:00:00.0001]",
+ "2021-08-19T17:30-07:00:00.0000[-07:00:00.0000]",
+ "2021-08-19T17:30-07:00:00.00001[-07:00:00.00001]",
+ "2021-08-19T17:30-07:00:00.00000[-07:00:00.00000]",
+ "2021-08-19T17:30-07:00:00.000001[-07:00:00.000001]",
+ "2021-08-19T17:30-07:00:00.000000[-07:00:00.000000]",
+ "2021-08-19T17:30-07:00:00.0000001[-07:00:00.0000001]",
+ "2021-08-19T17:30-07:00:00.0000000[-07:00:00.0000000]",
+ "2021-08-19T17:30-07:00:00.00000001[-07:00:00.00000001]",
+ "2021-08-19T17:30-07:00:00.00000000[-07:00:00.00000000]",
+ "2021-08-19T17:30-07:00:00.000000001[-07:00:00.000000001]",
+ "2021-08-19T17:30-07:00:00.000000000[-07:00:00.000000000]",
+
+ "2021-08-19T17:30-07:00:01[-070001]",
+ "2021-08-19T17:30-07:00:00[-070000]",
+ "2021-08-19T17:30-07:00:00.1[-070000.1]",
+ "2021-08-19T17:30-07:00:00.0[-070000.0]",
+ "2021-08-19T17:30-07:00:00.01[-070000.01]",
+ "2021-08-19T17:30-07:00:00.00[-070000.00]",
+ "2021-08-19T17:30-07:00:00.001[-070000.001]",
+ "2021-08-19T17:30-07:00:00.000[-070000.000]",
+ "2021-08-19T17:30-07:00:00.0001[-070000.0001]",
+ "2021-08-19T17:30-07:00:00.0000[-070000.0000]",
+ "2021-08-19T17:30-07:00:00.00001[-070000.00001]",
+ "2021-08-19T17:30-07:00:00.00000[-070000.00000]",
+ "2021-08-19T17:30-07:00:00.000001[-070000.000001]",
+ "2021-08-19T17:30-07:00:00.000000[-070000.000000]",
+ "2021-08-19T17:30-07:00:00.0000001[-070000.0000001]",
+ "2021-08-19T17:30-07:00:00.0000000[-070000.0000000]",
+ "2021-08-19T17:30-07:00:00.00000001[-070000.00000001]",
+ "2021-08-19T17:30-07:00:00.00000000[-070000.00000000]",
+ "2021-08-19T17:30-07:00:00.000000001[-070000.000000001]",
+ "2021-08-19T17:30-07:00:00.000000000[-070000.000000000]"
+].forEach((str) => {
+ assert.throws(
+ RangeError,
+ () => instance.equals(str),
+ `ISO strings cannot have sub-minute offsets in time zone annotations: ${str}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/instant-string.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/instant-string.js
new file mode 100644
index 0000000000..56bcc4555a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/instant-string.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.instant.prototype.equals
+description: Conversion of ISO date-time strings to Temporal.Instant instances
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(0n);
+
+let str = "1970-01-01T00:00";
+assert.throws(RangeError, () => instance.equals(str), "bare date-time string is not an instant");
+str = "1970-01-01T00:00[UTC]";
+assert.throws(RangeError, () => instance.equals(str), "date-time + IANA annotation is not an instant");
+
+str = "1970-01-01T00:00Z";
+const result1 = instance.equals(str);
+assert.sameValue(result1, true, "date-time + Z preserves exact time");
+
+str = "1970-01-01T00:00+01:00";
+const result2 = instance.equals(str);
+assert.sameValue(result2, false, "date-time + offset preserves exact time with offset");
+
+str = "1970-01-01T00:00Z[Etc/Ignored]";
+const result3 = instance.equals(str);
+assert.sameValue(result3, true, "date-time + Z + IANA annotation ignores the IANA annotation");
+
+str = "1970-01-01T00:00+01:00[Etc/Ignored]";
+const result4 = instance.equals(str);
+assert.sameValue(result4, false, "date-time + offset + IANA annotation ignores the IANA annotation");
+
+str = "1970-01-01T00:00Z[u-ca=hebrew]";
+const result6 = instance.equals(str);
+assert.sameValue(result6, true, "date-time + Z + Calendar ignores the Calendar");
+
+str = "1970-01-01T00:00+01:00[u-ca=hebrew]";
+const result5 = instance.equals(str);
+assert.sameValue(result5, false, "date-time + offset + Calendar ignores the Calendar");
+
+str = "1970-01-01T00:00+01:00[Etc/Ignored][u-ca=hebrew]";
+const result7 = instance.equals(str);
+assert.sameValue(result7, false, "date-time + offset + IANA annotation + Calendar ignores the Calendar and IANA annotation");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/leap-second.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/leap-second.js
new file mode 100644
index 0000000000..c0abe679b1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/leap-second.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.instant.prototype.equals
+description: Leap second is a valid ISO string for Instant
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(1_483_228_799_000_000_000n);
+
+const arg = "2016-12-31T23:59:60Z";
+const result = instance.equals(arg);
+assert.sameValue(
+ result,
+ true,
+ "leap second is a valid ISO string for Instant"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/length.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/length.js
new file mode 100644
index 0000000000..d8485d5737
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/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.instant.prototype.equals
+description: Temporal.Instant.prototype.equals.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.Instant.prototype.equals, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/name.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/name.js
new file mode 100644
index 0000000000..32354b2fec
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/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.instant.prototype.equals
+description: Temporal.Instant.prototype.equals.name is "equals".
+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.Instant.prototype.equals, "name", {
+ value: "equals",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/not-a-constructor.js
new file mode 100644
index 0000000000..77c91260bc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/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.instant.prototype.equals
+description: >
+ Temporal.Instant.prototype.equals 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.Instant.prototype.equals();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Instant.prototype.equals), false,
+ "isConstructor(Temporal.Instant.prototype.equals)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/prop-desc.js
new file mode 100644
index 0000000000..727e659818
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/prop-desc.js
@@ -0,0 +1,24 @@
+// |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.instant.prototype.equals
+description: The "equals" property of Temporal.Instant.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Instant.prototype.equals,
+ "function",
+ "`typeof Instant.prototype.equals` is `function`"
+);
+
+verifyProperty(Temporal.Instant.prototype, "equals", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/shell.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/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/Instant/prototype/equals/year-zero.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/year-zero.js
new file mode 100644
index 0000000000..25a4953520
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/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.instant.prototype.equals
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-03-30T00:45Z",
+ "-000000-03-30T01:45+01:00",
+ "-000000-03-30T01:45:00+00:00[UTC]",
+];
+const instance = new Temporal.Instant(0n);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/prop-desc.js
new file mode 100644
index 0000000000..b8fa2a6640
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/prop-desc.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-instant-prototype
+description: The "prototype" property of Temporal.Instant
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(typeof Temporal.Instant.prototype, "object");
+assert.notSameValue(Temporal.Instant.prototype, null);
+
+verifyProperty(Temporal.Instant, "prototype", {
+ writable: false,
+ enumerable: false,
+ configurable: false,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/branding.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/branding.js
new file mode 100644
index 0000000000..a8ba137e06
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/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.instant.prototype.round
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const round = Temporal.Instant.prototype.round;
+
+assert.sameValue(typeof round, "function");
+
+const args = ["hour"];
+
+assert.throws(TypeError, () => round.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => round.apply(null, args), "null");
+assert.throws(TypeError, () => round.apply(true, args), "true");
+assert.throws(TypeError, () => round.apply("", args), "empty string");
+assert.throws(TypeError, () => round.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => round.apply(1, args), "1");
+assert.throws(TypeError, () => round.apply({}, args), "plain object");
+assert.throws(TypeError, () => round.apply(Temporal.Instant, args), "Temporal.Instant");
+assert.throws(TypeError, () => round.apply(Temporal.Instant.prototype, args), "Temporal.Instant.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/browser.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/builtin.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/builtin.js
new file mode 100644
index 0000000000..b51180ad0d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/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.instant.prototype.round
+description: >
+ Tests that Temporal.Instant.prototype.round
+ 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.Instant.prototype.round),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Instant.prototype.round),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Instant.prototype.round),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Instant.prototype.round.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/length.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/length.js
new file mode 100644
index 0000000000..6399f72197
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/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.instant.prototype.round
+description: Temporal.Instant.prototype.round.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.Instant.prototype.round, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/name.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/name.js
new file mode 100644
index 0000000000..3553efd3c3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/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.instant.prototype.round
+description: Temporal.Instant.prototype.round.name is "round".
+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.Instant.prototype.round, "name", {
+ value: "round",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/not-a-constructor.js
new file mode 100644
index 0000000000..c9c372df89
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/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.instant.prototype.round
+description: >
+ Temporal.Instant.prototype.round 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.Instant.prototype.round();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Instant.prototype.round), false,
+ "isConstructor(Temporal.Instant.prototype.round)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/options-wrong-type.js
new file mode 100644
index 0000000000..d3ba48468b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/options-wrong-type.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.instant.prototype.round
+description: TypeError thrown when options argument is missing or a non-string primitive
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const badOptions = [
+ undefined,
+ null,
+ true,
+ Symbol(),
+ 1,
+ 2n,
+];
+
+const instance = new Temporal.Instant(0n);
+assert.throws(TypeError, () => instance.round(), "TypeError on missing options argument");
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.round(value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/prop-desc.js
new file mode 100644
index 0000000000..d6c0e2bd3c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/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.instant.prototype.round
+description: The "round" property of Temporal.Instant.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Instant.prototype.round,
+ "function",
+ "`typeof Instant.prototype.round` is `function`"
+);
+
+verifyProperty(Temporal.Instant.prototype, "round", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/rounding-direction.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/rounding-direction.js
new file mode 100644
index 0000000000..b45d956630
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/rounding-direction.js
@@ -0,0 +1,33 @@
+// |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.instant.prototype.round
+description: Rounding down is towards the Big Bang, not the epoch or 1 BCE
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(-65_261_246_399_500_000_000n); // -000099-12-15T12:00:00.5Z
+assert.sameValue(
+ instance.round({ smallestUnit: "second", roundingMode: "floor" }).epochNanoseconds,
+ -65_261_246_400_000_000_000n, // -000099-12-15T12:00:00Z
+ "Rounding down is towards the Big Bang, not the epoch or 1 BCE (roundingMode floor)"
+);
+assert.sameValue(
+ instance.round({ smallestUnit: "second", roundingMode: "trunc" }).epochNanoseconds,
+ -65_261_246_400_000_000_000n, // -000099-12-15T12:00:00Z
+ "Rounding down is towards the Big Bang, not the epoch or 1 BCE (roundingMode trunc)"
+);
+assert.sameValue(
+ instance.round({ smallestUnit: "second", roundingMode: "ceil" }).epochNanoseconds,
+ -65_261_246_399_000_000_000n, // -000099-12-15T12:00:01Z
+ "Rounding up is away from the Big Bang, not the epoch or 1 BCE (roundingMode ceil)"
+);
+assert.sameValue(
+ instance.round({ smallestUnit: "second", roundingMode: "halfExpand" }).epochNanoseconds,
+ -65_261_246_399_000_000_000n, // -000099-12-15T12:00:01Z
+ "Rounding up is away from the Big Bang, not the epoch or 1 BCE (roundingMode halfExpand)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingincrement-nan.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingincrement-nan.js
new file mode 100644
index 0000000000..4d769b96de
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingincrement-nan.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.instant.prototype.round
+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.instant.prototype.round step 13:
+ 13. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *true*).
+features: [Temporal]
+---*/
+
+const instant = new Temporal.Instant(1_000_000_000_987_654_321n);
+assert.throws(RangeError, () => instant.round({ smallestUnit: 'second', roundingIncrement: NaN }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingincrement-non-integer.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingincrement-non-integer.js
new file mode 100644
index 0000000000..ff2d8c6bec
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingincrement-non-integer.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.instant.prototype.round
+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_.
+features: [Temporal]
+---*/
+
+const instant = new Temporal.Instant(1_000_000_000_000_000_005n);
+const result = instant.round({ smallestUnit: "nanosecond", roundingIncrement: 2.5, roundingMode: "expand" });
+assert.sameValue(result.epochNanoseconds, 1_000_000_000_000_000_006n, "roundingIncrement 2.5 truncates to 2");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingincrement-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingincrement-out-of-range.js
new file mode 100644
index 0000000000..8b7b47c706
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingincrement-out-of-range.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.instant.prototype.round
+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 instant = new Temporal.Instant(1_000_000_000_000_000_005n);
+assert.throws(RangeError, () => instant.round({ smallestUnit: "nanoseconds", roundingIncrement: -Infinity }));
+assert.throws(RangeError, () => instant.round({ smallestUnit: "nanoseconds", roundingIncrement: -1 }));
+assert.throws(RangeError, () => instant.round({ smallestUnit: "nanoseconds", roundingIncrement: 0 }));
+assert.throws(RangeError, () => instant.round({ smallestUnit: "nanoseconds", roundingIncrement: 0.9 }));
+assert.throws(RangeError, () => instant.round({ smallestUnit: "nanoseconds", roundingIncrement: 1e9 + 1 }));
+assert.throws(RangeError, () => instant.round({ smallestUnit: "nanoseconds", roundingIncrement: Infinity }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingincrement-undefined.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingincrement-undefined.js
new file mode 100644
index 0000000000..0dd154c415
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingincrement-undefined.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.instant.prototype.round
+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.instant.prototype.round step 13:
+ 13. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *true*).
+features: [Temporal]
+---*/
+
+const instant = new Temporal.Instant(1_000_000_000_987_654_321n);
+
+const explicit = instant.round({ smallestUnit: 'second', roundingIncrement: undefined });
+assert.sameValue(explicit.epochNanoseconds, 1_000_000_001_000_000_000n, "default roundingIncrement is 1");
+
+const implicit = instant.round({ smallestUnit: 'second' });
+assert.sameValue(implicit.epochNanoseconds, 1_000_000_001_000_000_000n, "default roundingIncrement is 1");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingincrement-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingincrement-wrong-type.js
new file mode 100644
index 0000000000..434c2751a9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingincrement-wrong-type.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.instant.prototype.round
+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.instant.prototype.round step 13:
+ 13. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*).
+includes: [temporalHelpers.js, compareArray.js]
+features: [Temporal]
+---*/
+
+const instant = new Temporal.Instant(1_000_000_000_987_654_321n);
+
+TemporalHelpers.checkRoundingIncrementOptionWrongType(
+ (roundingIncrement) => instant.round({ smallestUnit: 'second', roundingIncrement }),
+ (result, descr) => assert.sameValue(result.epochNanoseconds, 1_000_000_001_000_000_000n, descr),
+ (result, descr) => assert.sameValue(result.epochNanoseconds, 1_000_000_000_000_000_000n, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingmode-ceil.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingmode-ceil.js
new file mode 100644
index 0000000000..895e14a409
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingmode-ceil.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.instant.prototype.round
+description: Tests calculations with roundingMode "ceil".
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(217175010_123_987_500n /* 1976-11-18T14:23:30.1239875Z */);
+
+const expected = [
+ ["hour", 217177200_000_000_000n /* 1976-11-18T15:00:00Z */],
+ ["minute", 217175040_000_000_000n /* 1976-11-18T14:24:00Z */],
+ ["second", 217175011_000_000_000n /* 1976-11-18T14:23:31Z */],
+ ["millisecond", 217175010_124_000_000n /* 1976-11-18T14:23:30.124Z */],
+ ["microsecond", 217175010_123_988_000n /* 1976-11-18T14:23:30.123988Z */],
+ ["nanosecond", 217175010_123_987_500n /* 1976-11-18T14:23:30.1239875Z */],
+];
+
+const roundingMode = "ceil";
+
+expected.forEach(([smallestUnit, expected]) => {
+ assert.sameValue(
+ instance.round({ smallestUnit, roundingMode }).epochNanoseconds,
+ expected,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingmode-expand.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingmode-expand.js
new file mode 100644
index 0000000000..95a2a2077c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingmode-expand.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.instant.prototype.round
+description: Tests calculations with roundingMode "expand".
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(217175010_123_987_500n /* 1976-11-18T14:23:30.1239875Z */);
+
+const expected = [
+ ["hour", 217177200_000_000_000n /* 1976-11-18T15:00:00Z */],
+ ["minute", 217175040_000_000_000n /* 1976-11-18T14:24:00Z */],
+ ["second", 217175011_000_000_000n /* 1976-11-18T14:23:31Z */],
+ ["millisecond", 217175010_124_000_000n /* 1976-11-18T14:23:30.124Z */],
+ ["microsecond", 217175010_123_988_000n /* 1976-11-18T14:23:30.123988Z */],
+ ["nanosecond", 217175010_123_987_500n /* 1976-11-18T14:23:30.1239875Z */],
+];
+
+const roundingMode = "expand";
+
+expected.forEach(([smallestUnit, expected]) => {
+ assert.sameValue(
+ instance.round({ smallestUnit, roundingMode }).epochNanoseconds,
+ expected,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingmode-floor.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingmode-floor.js
new file mode 100644
index 0000000000..b2de72d07e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingmode-floor.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.instant.prototype.round
+description: Tests calculations with roundingMode "floor".
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(217175010_123_987_500n /* 1976-11-18T14:23:30.1239875Z */);
+
+const expected = [
+ ["hour", 217173600_000_000_000n /* 1976-11-18T14:00:00Z */],
+ ["minute", 217174980_000_000_000n /* 1976-11-18T14:23:00Z */],
+ ["second", 217175010_000_000_000n /* 1976-11-18T14:23:30Z */],
+ ["millisecond", 217175010_123_000_000n /* 1976-11-18T14:23:30.123Z */],
+ ["microsecond", 217175010_123_987_000n /* 1976-11-18T14:23:30.123987Z */],
+ ["nanosecond", 217175010_123_987_500n /* 1976-11-18T14:23:30.1239875Z */],
+];
+
+const roundingMode = "floor";
+
+expected.forEach(([smallestUnit, expected]) => {
+ assert.sameValue(
+ instance.round({ smallestUnit, roundingMode }).epochNanoseconds,
+ expected,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingmode-halfCeil.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingmode-halfCeil.js
new file mode 100644
index 0000000000..6260c1b0cc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingmode-halfCeil.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.instant.prototype.round
+description: Tests calculations with roundingMode "halfCeil".
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(217175010_123_987_500n /* 1976-11-18T14:23:30.1239875Z */);
+
+const expected = [
+ ["hour", 217173600_000_000_000n /* 1976-11-18T14:00:00Z */],
+ ["minute", 217175040_000_000_000n /* 1976-11-18T14:24:00Z */],
+ ["second", 217175010_000_000_000n /* 1976-11-18T14:23:30Z */],
+ ["millisecond", 217175010_124_000_000n /* 1976-11-18T14:23:30.124Z */],
+ ["microsecond", 217175010_123_988_000n /* 1976-11-18T14:23:30.123988Z */],
+ ["nanosecond", 217175010_123_987_500n /* 1976-11-18T14:23:30.1239875Z */],
+];
+
+const roundingMode = "halfCeil";
+
+expected.forEach(([smallestUnit, expected]) => {
+ assert.sameValue(
+ instance.round({ smallestUnit, roundingMode }).epochNanoseconds,
+ expected,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingmode-halfEven.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingmode-halfEven.js
new file mode 100644
index 0000000000..100c542c39
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingmode-halfEven.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.instant.prototype.round
+description: Tests calculations with roundingMode "halfEven".
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(217175010_123_987_500n /* 1976-11-18T14:23:30.1239875Z */);
+
+const expected = [
+ ["hour", 217173600_000_000_000n /* 1976-11-18T14:00:00Z */],
+ ["minute", 217175040_000_000_000n /* 1976-11-18T14:24:00Z */],
+ ["second", 217175010_000_000_000n /* 1976-11-18T14:23:30Z */],
+ ["millisecond", 217175010_124_000_000n /* 1976-11-18T14:23:30.124Z */],
+ ["microsecond", 217175010_123_988_000n /* 1976-11-18T14:23:30.123988Z */],
+ ["nanosecond", 217175010_123_987_500n /* 1976-11-18T14:23:30.1239875Z */],
+];
+
+const roundingMode = "halfEven";
+
+expected.forEach(([smallestUnit, expected]) => {
+ assert.sameValue(
+ instance.round({ smallestUnit, roundingMode }).epochNanoseconds,
+ expected,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingmode-halfExpand.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingmode-halfExpand.js
new file mode 100644
index 0000000000..bc029fb817
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingmode-halfExpand.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.instant.prototype.round
+description: Tests calculations with roundingMode "halfExpand".
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(217175010_123_987_500n /* 1976-11-18T14:23:30.1239875Z */);
+
+const expected = [
+ ["hour", 217173600_000_000_000n /* 1976-11-18T14:00:00Z */],
+ ["minute", 217175040_000_000_000n /* 1976-11-18T14:24:00Z */],
+ ["second", 217175010_000_000_000n /* 1976-11-18T14:23:30Z */],
+ ["millisecond", 217175010_124_000_000n /* 1976-11-18T14:23:30.124Z */],
+ ["microsecond", 217175010_123_988_000n /* 1976-11-18T14:23:30.123988Z */],
+ ["nanosecond", 217175010_123_987_500n /* 1976-11-18T14:23:30.1239875Z */],
+];
+
+const roundingMode = "halfExpand";
+
+expected.forEach(([smallestUnit, expected]) => {
+ assert.sameValue(
+ instance.round({ smallestUnit, roundingMode }).epochNanoseconds,
+ expected,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingmode-halfFloor.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingmode-halfFloor.js
new file mode 100644
index 0000000000..b5e36839e8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingmode-halfFloor.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.instant.prototype.round
+description: Tests calculations with roundingMode "halfFloor".
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(217175010_123_987_500n /* 1976-11-18T14:23:30.1239875Z */);
+
+const expected = [
+ ["hour", 217173600_000_000_000n /* 1976-11-18T14:00:00Z */],
+ ["minute", 217175040_000_000_000n /* 1976-11-18T14:24:00Z */],
+ ["second", 217175010_000_000_000n /* 1976-11-18T14:23:30Z */],
+ ["millisecond", 217175010_124_000_000n /* 1976-11-18T14:23:30.124Z */],
+ ["microsecond", 217175010_123_987_000n /* 1976-11-18T14:23:30.123987Z */],
+ ["nanosecond", 217175010_123_987_500n /* 1976-11-18T14:23:30.1239875Z */],
+];
+
+const roundingMode = "halfFloor";
+
+expected.forEach(([smallestUnit, expected]) => {
+ assert.sameValue(
+ instance.round({ smallestUnit, roundingMode }).epochNanoseconds,
+ expected,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingmode-halfTrunc.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingmode-halfTrunc.js
new file mode 100644
index 0000000000..c42664def0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingmode-halfTrunc.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.instant.prototype.round
+description: Tests calculations with roundingMode "halfTrunc".
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(217175010_123_987_500n /* 1976-11-18T14:23:30.1239875Z */);
+
+const expected = [
+ ["hour", 217173600_000_000_000n /* 1976-11-18T14:00:00Z */],
+ ["minute", 217175040_000_000_000n /* 1976-11-18T14:24:00Z */],
+ ["second", 217175010_000_000_000n /* 1976-11-18T14:23:30Z */],
+ ["millisecond", 217175010_124_000_000n /* 1976-11-18T14:23:30.124Z */],
+ ["microsecond", 217175010_123_987_000n /* 1976-11-18T14:23:30.123987Z */],
+ ["nanosecond", 217175010_123_987_500n /* 1976-11-18T14:23:30.1239875Z */],
+];
+
+const roundingMode = "halfTrunc";
+
+expected.forEach(([smallestUnit, expected]) => {
+ assert.sameValue(
+ instance.round({ smallestUnit, roundingMode }).epochNanoseconds,
+ expected,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingmode-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingmode-invalid-string.js
new file mode 100644
index 0000000000..9cf02c42e4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingmode-invalid-string.js
@@ -0,0 +1,16 @@
+// |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.instant.prototype.round
+description: RangeError thrown when roundingMode option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const instant = new Temporal.Instant(1_000_000_000_123_987_500n);
+for (const roundingMode of ["other string", "cile", "CEIL", "ce\u0131l", "auto", "halfexpand", "floor\0"]) {
+ assert.throws(RangeError, () => instant.round({ smallestUnit: "microsecond", roundingMode }));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingmode-trunc.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingmode-trunc.js
new file mode 100644
index 0000000000..bc6c4fdf41
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingmode-trunc.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.instant.prototype.round
+description: Tests calculations with roundingMode "trunc".
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(217175010_123_987_500n /* 1976-11-18T14:23:30.1239875Z */);
+
+const expected = [
+ ["hour", 217173600_000_000_000n /* 1976-11-18T14:00:00Z */],
+ ["minute", 217174980_000_000_000n /* 1976-11-18T14:23:00Z */],
+ ["second", 217175010_000_000_000n /* 1976-11-18T14:23:30Z */],
+ ["millisecond", 217175010_123_000_000n /* 1976-11-18T14:23:30.123Z */],
+ ["microsecond", 217175010_123_987_000n /* 1976-11-18T14:23:30.123987Z */],
+ ["nanosecond", 217175010_123_987_500n /* 1976-11-18T14:23:30.1239875Z */],
+];
+
+const roundingMode = "trunc";
+
+expected.forEach(([smallestUnit, expected]) => {
+ assert.sameValue(
+ instance.round({ smallestUnit, roundingMode }).epochNanoseconds,
+ expected,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingmode-undefined.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingmode-undefined.js
new file mode 100644
index 0000000000..dfae6fd41f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingmode-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.instant.prototype.round
+description: Fallback value for roundingMode option
+features: [Temporal]
+---*/
+
+const instant = new Temporal.Instant(1_000_000_000_123_987_500n);
+
+const explicit1 = instant.round({ smallestUnit: "microsecond", roundingMode: undefined });
+assert.sameValue(explicit1.epochNanoseconds, 1_000_000_000_123_988_000n, "default roundingMode is halfExpand");
+const implicit1 = instant.round({ smallestUnit: "microsecond" });
+assert.sameValue(implicit1.epochNanoseconds, 1_000_000_000_123_988_000n, "default roundingMode is halfExpand");
+
+const explicit2 = instant.round({ smallestUnit: "millisecond", roundingMode: undefined });
+assert.sameValue(explicit2.epochNanoseconds, 1_000_000_000_124_000_000n, "default roundingMode is halfExpand");
+const implicit2 = instant.round({ smallestUnit: "millisecond" });
+assert.sameValue(implicit2.epochNanoseconds, 1_000_000_000_124_000_000n, "default roundingMode is halfExpand");
+
+const explicit3 = instant.round({ smallestUnit: "second", roundingMode: undefined });
+assert.sameValue(explicit3.epochNanoseconds, 1_000_000_000_000_000_000n, "default roundingMode is halfExpand");
+const implicit3 = instant.round({ smallestUnit: "second" });
+assert.sameValue(implicit3.epochNanoseconds, 1_000_000_000_000_000_000n, "default roundingMode is halfExpand");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingmode-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingmode-wrong-type.js
new file mode 100644
index 0000000000..38477cb46b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundingmode-wrong-type.js
@@ -0,0 +1,18 @@
+// |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.instant.prototype.round
+description: Type conversions for roundingMode option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instant = new Temporal.Instant(1_000_000_000_123_987_500n);
+TemporalHelpers.checkStringOptionWrongType("roundingMode", "halfExpand",
+ (roundingMode) => instant.round({ smallestUnit: "microsecond", roundingMode }),
+ (result, descr) => assert.sameValue(result.epochNanoseconds, 1_000_000_000_123_988_000n, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundto-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundto-invalid-string.js
new file mode 100644
index 0000000000..453f1cefbe
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/roundto-invalid-string.js
@@ -0,0 +1,38 @@
+// |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.instant.prototype.round
+description: RangeError thrown when smallestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const instant = new Temporal.Instant(1_000_000_000_123_987_500n);
+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, () => instant.round(smallestUnit),
+ `"${smallestUnit}" is not a valid value for smallest unit`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/shell.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/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/Instant/prototype/round/smallestunit-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/smallestunit-invalid-string.js
new file mode 100644
index 0000000000..f008c0e7b8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/smallestunit-invalid-string.js
@@ -0,0 +1,38 @@
+// |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.instant.prototype.round
+description: RangeError thrown when smallestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const instant = new Temporal.Instant(1_000_000_000_123_987_500n);
+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, () => instant.round({ smallestUnit }),
+ `"${smallestUnit}" is not a valid value for smallest unit`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/smallestunit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/smallestunit-plurals-accepted.js
new file mode 100644
index 0000000000..4f6ed0d6e0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/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.instant.prototype.round
+description: Plural units are accepted as well for the smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal, arrow-function]
+---*/
+
+const instant = new Temporal.Instant(1_000_000_000_987_654_321n);
+const validUnits = [
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+];
+TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => instant.round({ smallestUnit }), validUnits);
+TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => instant.round(smallestUnit), validUnits);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/smallestunit-string-shorthand.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/smallestunit-string-shorthand.js
new file mode 100644
index 0000000000..d8387f3d02
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/smallestunit-string-shorthand.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.instant.prototype.round
+description: String as first argument is equivalent to options bag with smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal, arrow-function]
+---*/
+
+const instance = new Temporal.Instant(1_000_000_000_987_654_321n);
+const validUnits = [
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+];
+validUnits.forEach((smallestUnit) => {
+ const full = instance.round({ smallestUnit });
+ const shorthand = instance.round(smallestUnit);
+ TemporalHelpers.assertInstantsEqual(shorthand, full, `"${smallestUnit}" as first argument to round is equivalent to options bag`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/smallestunit-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/smallestunit-wrong-type.js
new file mode 100644
index 0000000000..024ea43229
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/smallestunit-wrong-type.js
@@ -0,0 +1,18 @@
+// |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.instant.prototype.round
+description: Type conversions for smallestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instant = new Temporal.Instant(1_000_000_000_123_987_500n);
+TemporalHelpers.checkStringOptionWrongType("smallestUnit", "microsecond",
+ (smallestUnit) => instant.round({ smallestUnit }),
+ (result, descr) => assert.sameValue(result.epochNanoseconds, 1_000_000_000_123_988_000n, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/subclassing-ignored.js
new file mode 100644
index 0000000000..e9121d0db8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/subclassing-ignored.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.instant.prototype.round
+description: Objects of a subclass are never created as return values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.Instant,
+ [10n],
+ "round",
+ [{ smallestUnit: 'second', roundingMode: 'ceil' }],
+ (result) => {
+ assert.sameValue(result.epochNanoseconds, 1_000_000_000n, "epochNanoseconds result");
+ },
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/shell.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/argument-object-tostring.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/argument-object-tostring.js
new file mode 100644
index 0000000000..5fced4e7c1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/argument-object-tostring.js
@@ -0,0 +1,23 @@
+// |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.instant.prototype.since
+description: Object is converted to a string, then to Temporal.Instant
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(0n);
+
+const arg = {};
+assert.throws(RangeError, () => instance.since(arg), "[object Object] is not a valid ISO string");
+
+arg.toString = function() {
+ return "1970-01-01T00:00Z";
+};
+const result = instance.since(arg);
+TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "result of toString is interpreted as ISO string");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..0ba540267e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/argument-string-calendar-annotation.js
@@ -0,0 +1,34 @@
+// |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.instant.prototype.since
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["1970-01-01T00:00Z[u-ca=iso8601]", "without time zone"],
+ ["1970-01-01T00:00Z[UTC][u-ca=gregory]", "with time zone"],
+ ["1970-01-01T00:00Z[!u-ca=hebrew]", "with ! and no time zone"],
+ ["1970-01-01T00:00Z[UTC][!u-ca=chinese]", "with ! and time zone"],
+ ["1970-01-01T00:00Z[u-ca=discord]", "annotation is ignored"],
+ ["1970-01-01T00:00Z[!u-ca=discord]", "annotation with ! is ignored"],
+ ["1970-01-01T00:00Z[u-ca=iso8601][u-ca=discord]", "two annotations are ignored"],
+];
+
+const instance = new Temporal.Instant(0n);
+
+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/Instant/prototype/since/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..2f0239e794
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/argument-string-critical-unknown-annotation.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.instant.prototype.since
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01T00:00Z[!foo=bar]",
+ "1970-01-01T00:00Z[UTC][!foo=bar]",
+ "1970-01-01T00:00Z[u-ca=iso8601][!foo=bar]",
+ "1970-01-01T00:00Z[UTC][!foo=bar][u-ca=iso8601]",
+ "1970-01-01T00:00Z[foo=bar][!_foo-bar0=Dont-Ignore-This-99999999999]",
+];
+const instance = new Temporal.Instant(0n);
+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/Instant/prototype/since/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..3fc7c68324
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/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.instant.prototype.since
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const instance = new Temporal.Instant(0n);
+
+const validStrings = [
+ "1970-01-01T00Z",
+ "1970-01-01T00Z[UTC]",
+ "1970-01-01T00Z[!UTC]",
+ "1970-01-01T00Z[Europe/Vienna]",
+ "1970-01-01T00+00:00",
+ "1970-01-01T00+00:00[UTC]",
+ "1970-01-01T00+00:00[!UTC]",
+ "1969-12-31T16-08:00[America/Vancouver]",
+];
+
+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 Instant`
+ );
+}
+
+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 Instant`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/argument-string-invalid.js
new file mode 100644
index 0000000000..01928f171f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/argument-string-invalid.js
@@ -0,0 +1,69 @@
+// |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.instant.prototype.since
+description: >
+ RangeError thrown if an invalid ISO string (or syntactically valid ISO string
+ that is not supported) is used as an Instant
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ // invalid ISO strings:
+ "",
+ "invalid iso8601",
+ "2020-01-00T00:00Z",
+ "2020-01-32T00:00Z",
+ "2020-02-30T00:00Z",
+ "2021-02-29T00:00Z",
+ "2020-00-01T00:00Z",
+ "2020-13-01T00:00Z",
+ "2020-01-01TZ",
+ "2020-01-01T25:00:00Z",
+ "2020-01-01T01:60:00Z",
+ "2020-01-01T01:60:61Z",
+ "2020-01-01T00:00Zjunk",
+ "2020-01-01T00:00:00Zjunk",
+ "2020-01-01T00:00:00.000000000Zjunk",
+ "2020-01-01T00:00:00+00:00junk",
+ "2020-01-01T00:00:00+00:00[UTC]junk",
+ "2020-01-01T00:00:00+00:00[UTC][u-ca=iso8601]junk",
+ "02020-01-01T00:00Z",
+ "2020-001-01T00:00Z",
+ "2020-01-001T00:00Z",
+ "2020-01-01T001Z",
+ "2020-01-01T01:001Z",
+ "2020-01-01T01:01:001Z",
+ // valid, but forms not supported in Temporal:
+ "2020-W01-1T00:00Z",
+ "2020-001T00:00Z",
+ "+0002020-01-01T00:00Z",
+ // may be valid in other contexts, but insufficient information for Instant:
+ "2020-01",
+ "+002020-01",
+ "01-01",
+ "2020-W01",
+ "P1Y",
+ "-P12Y",
+ "2020-01-01",
+ "2020-01-01T00",
+ "2020-01-01T00:00",
+ "2020-01-01T00:00:00",
+ "2020-01-01T00:00:00.000000000",
+ // valid, but outside the supported range:
+ "-999999-01-01T00:00Z",
+ "+999999-01-01T00:00Z",
+];
+
+const instance = new Temporal.Instant(0n);
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => instance.since(arg),
+ `"${arg}" should not be a valid ISO string for an Instant`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..6b69e7d4f5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/argument-string-multiple-calendar.js
@@ -0,0 +1,28 @@
+// |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.instant.prototype.since
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01T00:00Z[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00Z[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01T00:00Z[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00Z[u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+];
+const instance = new Temporal.Instant(0n);
+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/Instant/prototype/since/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..5a5db65759
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/argument-string-multiple-time-zone.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.instant.prototype.since
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01T00:00Z[UTC][UTC]",
+ "1970-01-01T00:00Z[!UTC][UTC]",
+ "1970-01-01T00:00Z[UTC][!UTC]",
+ "1970-01-01T00:00Z[UTC][u-ca=iso8601][UTC]",
+ "1970-01-01T00:00Z[UTC][foo=bar][UTC]",
+];
+const instance = new Temporal.Instant(0n);
+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/Instant/prototype/since/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/argument-string-time-separators.js
new file mode 100644
index 0000000000..df7bd9446e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/argument-string-time-separators.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.instant.prototype.since
+description: Time separator in string argument can vary
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["1970-01-01T00:00Z", "uppercase T"],
+ ["1970-01-01t00:00Z", "lowercase T"],
+ ["1970-01-01 00:00Z", "space between date and time"],
+];
+
+const instance = new Temporal.Instant(0n);
+
+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/Instant/prototype/since/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..d8ff2b935f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/argument-string-time-zone-annotation.js
@@ -0,0 +1,35 @@
+// |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.instant.prototype.since
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["1970-01-01T00:00Z[Asia/Kolkata]", "named, with Z"],
+ ["1970-01-01T00:00Z[!Europe/Vienna]", "named, with Z and !"],
+ ["1970-01-01T00:00Z[+00:00]", "numeric, with Z"],
+ ["1970-01-01T00:00Z[!-02:30]", "numeric, with Z and !"],
+ ["1970-01-01T00:00+00:00[UTC]", "named, with offset"],
+ ["1970-01-01T00:00+00:00[!Africa/Abidjan]", "named, with offset and !"],
+ ["1970-01-01T00:00+00:00[-08:00]", "numeric, with offset"],
+ ["1970-01-01T00:00+00:00[!+01:00]", "numeric, with offset and !"],
+];
+
+const instance = new Temporal.Instant(0n);
+
+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/Instant/prototype/since/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..381ce49bf3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/argument-string-unknown-annotation.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.instant.prototype.since
+description: Various forms of unknown annotation
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["1970-01-01T00:00Z[foo=bar]", "alone"],
+ ["1970-01-01T00:00Z[UTC][foo=bar]", "with time zone"],
+ ["1970-01-01T00:00Z[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["1970-01-01T00:00Z[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["1970-01-01T00:00Z[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+const instance = new Temporal.Instant(0n);
+
+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/Instant/prototype/since/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/argument-wrong-type.js
new file mode 100644
index 0000000000..6cf69bac37
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/argument-wrong-type.js
@@ -0,0 +1,46 @@
+// |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.instant.prototype.since
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ for Instant
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.Instant(0n);
+
+const primitiveTests = [
+ [undefined, "undefined"],
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [19761118, "number that would convert to a valid ISO string in other contexts"],
+ [1n, "bigint"],
+ [{}, "plain object"],
+ [Temporal.Instant, "Temporal.Instant, object"],
+];
+
+for (const [arg, description] of primitiveTests) {
+ assert.throws(
+ typeof arg === "string" || (typeof arg === "object" && arg !== null) || typeof arg === "function"
+ ? RangeError
+ : TypeError,
+ () => instance.since(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [Temporal.Instant.prototype, "Temporal.Instant.prototype, object"], // fails brand check in toString()
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.since(arg), `${description} does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/argument-zoneddatetime.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/argument-zoneddatetime.js
new file mode 100644
index 0000000000..a94ea782b2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/argument-zoneddatetime.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.instant.prototype.since
+description: Fast path for converting Temporal.ZonedDateTime to Temporal.Instant
+info: |
+ sec-temporal.instant.prototype.since step 3:
+ 3. Set _other_ to ? ToTemporalInstant(_other_).
+ sec-temporal-totemporalinstant step 1.b:
+ b. If _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return ! CreateTemporalInstant(_item_.[[Nanoseconds]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalInstantFastPath((datetime) => {
+ const instant = new Temporal.Instant(1_000_000_000_000_000_000n);
+ const result = instant.since(datetime);
+ assert.sameValue(result.total({ unit: "nanoseconds" }), -987654321, "nanoseconds result");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/branding.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/branding.js
new file mode 100644
index 0000000000..d4f769a0bd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/branding.js
@@ -0,0 +1,27 @@
+// |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.instant.prototype.since
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const since = Temporal.Instant.prototype.since;
+
+assert.sameValue(typeof since, "function");
+
+const args = [new Temporal.Instant(123456n)];
+
+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.Instant, args), "Temporal.Instant");
+assert.throws(TypeError, () => since.apply(Temporal.Instant.prototype, args), "Temporal.Instant.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/browser.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/builtin.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/builtin.js
new file mode 100644
index 0000000000..19b9af41c1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/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.instant.prototype.since
+description: >
+ Tests that Temporal.Instant.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.Instant.prototype.since),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Instant.prototype.since),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Instant.prototype.since),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Instant.prototype.since.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/instant-string-limits.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/instant-string-limits.js
new file mode 100644
index 0000000000..7327bec3ef
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/instant-string-limits.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.instant.prototype.since
+description: String arguments at the limit of the representable range
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const minInstant = new Temporal.Instant(-86400_00000000_000_000_000n);
+const maxInstant = new Temporal.Instant(86400_00000000_000_000_000n);
+
+const minInstantStrings = [
+ "-271821-04-20T00:00Z",
+ "-271821-04-19T23:00-01:00",
+ "-271821-04-19T00:00:00.000000001-23:59:59.999999999",
+];
+for (const str of minInstantStrings) {
+ TemporalHelpers.assertDuration(minInstant.since(str), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, `instant string ${str} should be valid`);
+}
+
+const maxInstantStrings = [
+ "+275760-09-13T00:00Z",
+ "+275760-09-13T01:00+01:00",
+ "+275760-09-13T23:59:59.999999999+23:59:59.999999999",
+];
+
+for (const str of maxInstantStrings) {
+ TemporalHelpers.assertDuration(maxInstant.since(str), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, `instant string ${str} should be valid`);
+}
+
+const outOfRangeInstantStrings = [
+ "-271821-04-19T23:59:59.999999999Z",
+ "-271821-04-19T23:00-00:59:59.999999999",
+ "-271821-04-19T00:00:00-23:59:59.999999999",
+ "-271821-04-19T00:00:00-24:00",
+ "+275760-09-13T00:00:00.000000001Z",
+ "+275760-09-13T01:00+00:59:59.999999999",
+ "+275760-09-14T00:00+23:59:59.999999999",
+ "+275760-09-14T00:00+24:00",
+];
+
+for (const str of outOfRangeInstantStrings) {
+ assert.throws(RangeError, () => minInstant.since(str), `instant string ${str} should not be valid`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/instant-string-multiple-offsets.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/instant-string-multiple-offsets.js
new file mode 100644
index 0000000000..7dd2034509
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/instant-string-multiple-offsets.js
@@ -0,0 +1,18 @@
+// |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.instant.prototype.since
+description: Instant strings with UTC offset fractional part are not confused with time fractional part
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(0n);
+const str = "1970-01-01T00:02:00.000000000+00:02[+01:30]";
+
+const result = instance.since(str);
+TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "UTC offset determined from offset part of string");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/instant-string-sub-minute-offset.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/instant-string-sub-minute-offset.js
new file mode 100644
index 0000000000..47b01a161f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/instant-string-sub-minute-offset.js
@@ -0,0 +1,68 @@
+// |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.instant.prototype.since
+description: Temporal.Instant string with sub-minute offset
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(0n);
+
+const str = "1970-01-01T00:19:32.37+00:19:32.37";
+const result = instance.since(str);
+TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "if present, sub-minute offset is accepted exactly");
+
+[
+ "2021-08-19T17:30-07:00:01[-07:00:01]",
+ "2021-08-19T17:30-07:00:00[-07:00:00]",
+ "2021-08-19T17:30-07:00:00.1[-07:00:00.1]",
+ "2021-08-19T17:30-07:00:00.0[-07:00:00.0]",
+ "2021-08-19T17:30-07:00:00.01[-07:00:00.01]",
+ "2021-08-19T17:30-07:00:00.00[-07:00:00.00]",
+ "2021-08-19T17:30-07:00:00.001[-07:00:00.001]",
+ "2021-08-19T17:30-07:00:00.000[-07:00:00.000]",
+ "2021-08-19T17:30-07:00:00.0001[-07:00:00.0001]",
+ "2021-08-19T17:30-07:00:00.0000[-07:00:00.0000]",
+ "2021-08-19T17:30-07:00:00.00001[-07:00:00.00001]",
+ "2021-08-19T17:30-07:00:00.00000[-07:00:00.00000]",
+ "2021-08-19T17:30-07:00:00.000001[-07:00:00.000001]",
+ "2021-08-19T17:30-07:00:00.000000[-07:00:00.000000]",
+ "2021-08-19T17:30-07:00:00.0000001[-07:00:00.0000001]",
+ "2021-08-19T17:30-07:00:00.0000000[-07:00:00.0000000]",
+ "2021-08-19T17:30-07:00:00.00000001[-07:00:00.00000001]",
+ "2021-08-19T17:30-07:00:00.00000000[-07:00:00.00000000]",
+ "2021-08-19T17:30-07:00:00.000000001[-07:00:00.000000001]",
+ "2021-08-19T17:30-07:00:00.000000000[-07:00:00.000000000]",
+
+ "2021-08-19T17:30-07:00:01[-070001]",
+ "2021-08-19T17:30-07:00:00[-070000]",
+ "2021-08-19T17:30-07:00:00.1[-070000.1]",
+ "2021-08-19T17:30-07:00:00.0[-070000.0]",
+ "2021-08-19T17:30-07:00:00.01[-070000.01]",
+ "2021-08-19T17:30-07:00:00.00[-070000.00]",
+ "2021-08-19T17:30-07:00:00.001[-070000.001]",
+ "2021-08-19T17:30-07:00:00.000[-070000.000]",
+ "2021-08-19T17:30-07:00:00.0001[-070000.0001]",
+ "2021-08-19T17:30-07:00:00.0000[-070000.0000]",
+ "2021-08-19T17:30-07:00:00.00001[-070000.00001]",
+ "2021-08-19T17:30-07:00:00.00000[-070000.00000]",
+ "2021-08-19T17:30-07:00:00.000001[-070000.000001]",
+ "2021-08-19T17:30-07:00:00.000000[-070000.000000]",
+ "2021-08-19T17:30-07:00:00.0000001[-070000.0000001]",
+ "2021-08-19T17:30-07:00:00.0000000[-070000.0000000]",
+ "2021-08-19T17:30-07:00:00.00000001[-070000.00000001]",
+ "2021-08-19T17:30-07:00:00.00000000[-070000.00000000]",
+ "2021-08-19T17:30-07:00:00.000000001[-070000.000000001]",
+ "2021-08-19T17:30-07:00:00.000000000[-070000.000000000]"
+].forEach((str) => {
+ assert.throws(
+ RangeError,
+ () => instance.since(str),
+ `ISO strings cannot have sub-minute offsets in time zone annotations: ${str}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/instant-string.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/instant-string.js
new file mode 100644
index 0000000000..39ca9c8040
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/instant-string.js
@@ -0,0 +1,47 @@
+// |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.instant.prototype.since
+description: Conversion of ISO date-time strings to Temporal.Instant instances
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(0n);
+
+let str = "1970-01-01T00:00";
+assert.throws(RangeError, () => instance.since(str), "bare date-time string is not an instant");
+str = "1970-01-01T00:00[UTC]";
+assert.throws(RangeError, () => instance.since(str), "date-time + IANA annotation is not an instant");
+
+str = "1970-01-01T00:00Z";
+const result1 = instance.since(str);
+TemporalHelpers.assertDuration(result1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "date-time + Z preserves exact time");
+
+str = "1970-01-01T00:00+01:00";
+const result2 = instance.since(str);
+TemporalHelpers.assertDuration(result2, 0, 0, 0, 0, 0, 0, 3600, 0, 0, 0, "date-time + offset preserves exact time with offset");
+
+str = "1970-01-01T00:00Z[Etc/Ignored]";
+const result3 = instance.since(str);
+TemporalHelpers.assertDuration(result3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "date-time + Z + IANA annotation ignores the IANA annotation");
+
+str = "1970-01-01T00:00+01:00[Etc/Ignored]";
+const result4 = instance.since(str);
+TemporalHelpers.assertDuration(result4, 0, 0, 0, 0, 0, 0, 3600, 0, 0, 0, "date-time + offset + IANA annotation ignores the IANA annotation");
+
+str = "1970-01-01T00:00Z[u-ca=hebrew]";
+const result6 = instance.since(str);
+TemporalHelpers.assertDuration(result6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "date-time + Z + Calendar ignores the Calendar");
+
+str = "1970-01-01T00:00+01:00[u-ca=hebrew]";
+const result5 = instance.since(str);
+TemporalHelpers.assertDuration(result5, 0, 0, 0, 0, 0, 0, 3600, 0, 0, 0, "date-time + offset + Calendar ignores the Calendar");
+
+str = "1970-01-01T00:00+01:00[Etc/Ignored][u-ca=hebrew]";
+const result7 = instance.since(str);
+TemporalHelpers.assertDuration(result7, 0, 0, 0, 0, 0, 0, 3600, 0, 0, 0, "date-time + offset + IANA annotation + Calendar ignores the Calendar and IANA annotation");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/largestunit-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/largestunit-invalid-string.js
new file mode 100644
index 0000000000..e4468aea5b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/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.instant.prototype.since
+description: RangeError thrown when largestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(1_000_000_000_000_000_000n);
+const later = new Temporal.Instant(1_000_090_061_987_654_321n);
+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/Instant/prototype/since/largestunit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/largestunit-plurals-accepted.js
new file mode 100644
index 0000000000..dddaec630d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/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.instant.prototype.since
+description: Plural units are accepted as well for the largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(1_000_000_000_987_654_321n);
+const later = new Temporal.Instant(1_086_403_661_988_655_322n);
+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/Instant/prototype/since/largestunit-smallestunit-mismatch.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/largestunit-smallestunit-mismatch.js
new file mode 100644
index 0000000000..8e79b001ba
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/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.instant.prototype.since
+description: RangeError thrown when smallestUnit is larger than largestUnit
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(1_000_000_000_000_000_000n);
+const later = new Temporal.Instant(1_000_090_061_987_654_321n);
+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/Instant/prototype/since/largestunit-undefined.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/largestunit-undefined.js
new file mode 100644
index 0000000000..82f5947ae2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/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.instant.prototype.since
+description: Fallback value for largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(1_000_000_000_000_000_000n);
+const later = new Temporal.Instant(1_000_090_061_987_654_321n);
+
+const explicit = later.since(earlier, { largestUnit: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 0, 0, 0, 90061, 987, 654, 321, "default largestUnit is second");
+const implicit = later.since(earlier, {});
+TemporalHelpers.assertDuration(implicit, 0, 0, 0, 0, 0, 0, 90061, 987, 654, 321, "default largestUnit is second");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/largestunit-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/largestunit-wrong-type.js
new file mode 100644
index 0000000000..ef774d8d2b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/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.instant.prototype.since
+description: Type conversions for largestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(1_000_000_000_000_000_000n);
+const later = new Temporal.Instant(1_000_090_061_987_654_321n);
+TemporalHelpers.checkStringOptionWrongType("largestUnit", "hour",
+ (largestUnit) => later.since(earlier, { largestUnit }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 25, 1, 1, 987, 654, 321, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/largestunit.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/largestunit.js
new file mode 100644
index 0000000000..1ec1efbbd3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/largestunit.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.instant.prototype.since
+description: Specify behavior of Instant.since when largest specified unit is specified
+includes: [temporalHelpers.js]
+features: [Temporal, BigInt]
+---*/
+const thePast = new Temporal.Instant(1234567890123456789n);
+const theFuture = new Temporal.Instant(2345678901234567890n);
+TemporalHelpers.assertDuration(theFuture.since(thePast), 0, 0, 0, 0, 0, 0, 1111111011, 111, 111, 101, 'does not include higher units than necessary (largest unit unspecified)');
+TemporalHelpers.assertDuration(theFuture.since(thePast, { largestUnit: 'hours' }), 0, 0, 0, 0, 308641, 56, 51, 111, 111, 101, 'does not include higher units than necessary (largest unit is hours)');
+TemporalHelpers.assertDuration(theFuture.since(thePast, { largestUnit: 'minutes' }), 0, 0, 0, 0, 0, 18518516, 51, 111, 111, 101, 'does not include higher units than necessary (largest unit is minutes)');
+TemporalHelpers.assertDuration(theFuture.since(thePast, { largestUnit: 'seconds' }), 0, 0, 0, 0, 0, 0, 1111111011, 111, 111, 101, 'does not include higher units than necessary (largest unit is seconds)');
+TemporalHelpers.assertDuration(theFuture.since(thePast, { largestUnit: 'milliseconds' }), 0, 0, 0, 0, 0, 0, 0, 1111111011111, 111, 101, 'does not include higher units than necessary (largest unit is milliseconds)');
+TemporalHelpers.assertDuration(theFuture.since(thePast, { largestUnit: 'microseconds' }), 0, 0, 0, 0, 0, 0, 0, 0, 1111111011111111, 101, 'does not include higher units than necessary (largest unit is microseconds)');
+TemporalHelpers.assertDuration(theFuture.since(thePast, { largestUnit: 'nanoseconds' }), 0, 0, 0, 0, 0, 0, 0, 0, 0, 1111111011111111000, 'does not include higher units than necessary (largest unit is nanoseconds)');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/leap-second.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/leap-second.js
new file mode 100644
index 0000000000..06339d9afd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/leap-second.js
@@ -0,0 +1,22 @@
+// |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.instant.prototype.since
+description: Leap second is a valid ISO string for Instant
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(1_483_228_799_000_000_000n);
+
+const arg = "2016-12-31T23:59:60Z";
+const result = instance.since(arg);
+TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "leap second is a valid ISO string for Instant"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/length.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/length.js
new file mode 100644
index 0000000000..b05c60121e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/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.instant.prototype.since
+description: Temporal.Instant.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.Instant.prototype.since, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/name.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/name.js
new file mode 100644
index 0000000000..403a495f4f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/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.instant.prototype.since
+description: Temporal.Instant.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.Instant.prototype.since, "name", {
+ value: "since",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/not-a-constructor.js
new file mode 100644
index 0000000000..da893a2425
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/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.instant.prototype.since
+description: >
+ Temporal.Instant.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.Instant.prototype.since();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Instant.prototype.since), false,
+ "isConstructor(Temporal.Instant.prototype.since)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/options-object.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/options-object.js
new file mode 100644
index 0000000000..4ed05107a0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/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.instant.prototype.since
+description: Empty or a function object may be used as options
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(0n);
+
+const result1 = instance.since(new Temporal.Instant(3600_000_000_000n), {});
+TemporalHelpers.assertDuration(
+ result1, 0, 0, 0, 0, 0, 0, -3600, 0, 0, 0,
+ "options may be an empty plain object"
+);
+
+const result2 = instance.since(new Temporal.Instant(3600_000_000_000n), () => {});
+TemporalHelpers.assertDuration(
+ result2, 0, 0, 0, 0, 0, 0, -3600, 0, 0, 0,
+ "options may be a function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/options-undefined.js
new file mode 100644
index 0000000000..96eda9465e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/options-undefined.js
@@ -0,0 +1,34 @@
+// |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.instant.prototype.since
+description: Verify that undefined options are handled correctly.
+features: [BigInt, Temporal]
+---*/
+
+const earlier = new Temporal.Instant(957270896_987_654_321n);
+const later = new Temporal.Instant(959949296_987_654_322n);
+
+const explicit = later.since(earlier, undefined);
+assert.sameValue(explicit.years, 0, "default largest unit is seconds");
+assert.sameValue(explicit.months, 0, "default largest unit is seconds");
+assert.sameValue(explicit.weeks, 0, "default largest unit is seconds");
+assert.sameValue(explicit.days, 0, "default largest unit is seconds");
+assert.sameValue(explicit.hours, 0, "default largest unit is seconds");
+assert.sameValue(explicit.minutes, 0, "default largest unit is seconds");
+assert.sameValue(explicit.seconds, 2678400, "default largest unit is seconds");
+assert.sameValue(explicit.nanoseconds, 1, "default smallest unit is nanoseconds and no rounding");
+
+const implicit = later.since(earlier);
+assert.sameValue(implicit.years, 0, "default largest unit is seconds");
+assert.sameValue(implicit.months, 0, "default largest unit is seconds");
+assert.sameValue(implicit.weeks, 0, "default largest unit is seconds");
+assert.sameValue(implicit.days, 0, "default largest unit is seconds");
+assert.sameValue(implicit.hours, 0, "default largest unit is seconds");
+assert.sameValue(implicit.minutes, 0, "default largest unit is seconds");
+assert.sameValue(implicit.seconds, 2678400, "default largest unit is seconds");
+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/Instant/prototype/since/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/options-wrong-type.js
new file mode 100644
index 0000000000..e137fc7c1c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/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.instant.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.Instant(0n);
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.since(new Temporal.Instant(3600_000_000_000n), value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/order-of-operations.js
new file mode 100644
index 0000000000..2928e29101
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/order-of-operations.js
@@ -0,0 +1,57 @@
+// |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.instant.prototype.since
+description: Properties on objects passed to since() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "get other.toString",
+ "call other.toString",
+ "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",
+ "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.Instant(1_000_000_000_000_000_000n);
+const options = TemporalHelpers.propertyBagObserver(actual, {
+ roundingIncrement: 1,
+ roundingMode: "halfExpand",
+ largestUnit: "hours",
+ smallestUnit: "minutes",
+ additional: true,
+}, "options");
+
+instance.since(TemporalHelpers.toPrimitiveObserver(actual, "1970-01-01T00:00Z", "other"), options);
+assert.compareArray(actual, expected, "order of operations");
+
+actual.splice(0); // clear
+
+// short-circuit does not skip reading options
+instance.since(TemporalHelpers.toPrimitiveObserver(actual, "2001-09-09T01:46:40Z", "other"), options);
+assert.compareArray(actual, expected, "order of operations with identical instants");
+
+actual.splice(0); // clear
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/prop-desc.js
new file mode 100644
index 0000000000..94692b5364
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/prop-desc.js
@@ -0,0 +1,24 @@
+// |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.instant.prototype.since
+description: The "since" property of Temporal.Instant.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Instant.prototype.since,
+ "function",
+ "`typeof Instant.prototype.since` is `function`"
+);
+
+verifyProperty(Temporal.Instant.prototype, "since", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/round-cross-unit-boundary.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/round-cross-unit-boundary.js
new file mode 100644
index 0000000000..c0e3bd15ff
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/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.instant.prototype.since
+description: Rounding can cross unit boundaries up to largestUnit
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(0n);
+const later = new Temporal.Instant(7199_000_000_000n);
+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:59 balances to -2 hours");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/roundingincrement-nan.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/roundingincrement-nan.js
new file mode 100644
index 0000000000..06b5bde452
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/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.instant.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.instant.prototype.since step 11:
+ 11. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*).
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(1_000_000_000_987_654_321n);
+const later = new Temporal.Instant(1_000_090_061_988_655_322n);
+assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: NaN }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/roundingincrement-non-integer.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/roundingincrement-non-integer.js
new file mode 100644
index 0000000000..646407da38
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/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.instant.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.Instant(1_000_000_000_000_000_000n);
+const later = new Temporal.Instant(1_000_000_000_000_000_005n);
+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/Instant/prototype/since/roundingincrement-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/roundingincrement-out-of-range.js
new file mode 100644
index 0000000000..e0c277c753
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/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.instant.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.Instant(1_000_000_000_000_000_000n);
+const later = new Temporal.Instant(1_000_000_000_000_000_005n);
+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/Instant/prototype/since/roundingincrement-undefined.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/roundingincrement-undefined.js
new file mode 100644
index 0000000000..430983c478
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/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.instant.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.instant.prototype.since step 11:
+ 11. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(1_000_000_000_987_654_321n);
+const later = new Temporal.Instant(1_000_090_061_988_655_322n);
+
+const explicit = later.since(earlier, { roundingIncrement: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 0, 0, 0, 90061, 1, 1, 1, "default roundingIncrement is 1");
+
+const implicit = later.since(earlier, {});
+TemporalHelpers.assertDuration(implicit, 0, 0, 0, 0, 0, 0, 90061, 1, 1, 1, "default roundingIncrement is 1");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/roundingincrement-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/roundingincrement-wrong-type.js
new file mode 100644
index 0000000000..54cf07917b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/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.instant.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.instant.prototype.since step 11:
+ 11. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*).
+includes: [temporalHelpers.js, compareArray.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(1_000_000_000_987_654_321n);
+const later = new Temporal.Instant(1_000_090_061_988_655_322n);
+
+TemporalHelpers.checkRoundingIncrementOptionWrongType(
+ (roundingIncrement) => later.since(earlier, { roundingIncrement }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 90061, 1, 1, 1, descr),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 90061, 1, 1, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/roundingmode-ceil.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/roundingmode-ceil.js
new file mode 100644
index 0000000000..11dfa78bb6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/roundingmode-ceil.js
@@ -0,0 +1,42 @@
+// |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.instant.prototype.since
+description: Tests calculations with roundingMode "ceil".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(217178610_123_456_789n /* 1976-11-18T15:23:30.123456789Z */);
+const later = new Temporal.Instant(1572345998_271_986_289n /* 2019-10-29T10:46:38.271986289Z */);
+
+const expected = [
+ ["hours", [0, 0, 0, 0, 376436], [0, 0, 0, 0, -376435]],
+ ["minutes", [0, 0, 0, 0, 376435, 24], [0, 0, 0, 0, -376435, -23]],
+ ["seconds", [0, 0, 0, 0, 376435, 23, 9], [0, 0, 0, 0, -376435, -23, -8]],
+ ["milliseconds", [0, 0, 0, 0, 376435, 23, 8, 149], [0, 0, 0, 0, -376435, -23, -8, -148]],
+ ["microseconds", [0, 0, 0, 0, 376435, 23, 8, 148, 530], [0, 0, 0, 0, -376435, -23, -8, -148, -529]],
+ ["nanoseconds", [0, 0, 0, 0, 376435, 23, 8, 148, 529, 500], [0, 0, 0, 0, -376435, -23, -8, -148, -529, -500]],
+];
+
+const roundingMode = "ceil";
+const largestUnit = "hours";
+
+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, { largestUnit, 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, { largestUnit, 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/Instant/prototype/since/roundingmode-expand.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/roundingmode-expand.js
new file mode 100644
index 0000000000..5a8072438f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/roundingmode-expand.js
@@ -0,0 +1,42 @@
+// |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.instant.prototype.since
+description: Tests calculations with roundingMode "expand".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(217178610_123_456_789n /* 1976-11-18T15:23:30.123456789Z */);
+const later = new Temporal.Instant(1572345998_271_986_289n /* 2019-10-29T10:46:38.271986289Z */);
+
+const expected = [
+ ["hours", [0, 0, 0, 0, 376436], [0, 0, 0, 0, -376436]],
+ ["minutes", [0, 0, 0, 0, 376435, 24], [0, 0, 0, 0, -376435, -24]],
+ ["seconds", [0, 0, 0, 0, 376435, 23, 9], [0, 0, 0, 0, -376435, -23, -9]],
+ ["milliseconds", [0, 0, 0, 0, 376435, 23, 8, 149], [0, 0, 0, 0, -376435, -23, -8, -149]],
+ ["microseconds", [0, 0, 0, 0, 376435, 23, 8, 148, 530], [0, 0, 0, 0, -376435, -23, -8, -148, -530]],
+ ["nanoseconds", [0, 0, 0, 0, 376435, 23, 8, 148, 529, 500], [0, 0, 0, 0, -376435, -23, -8, -148, -529, -500]],
+];
+
+const roundingMode = "expand";
+const largestUnit = "hours";
+
+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, { largestUnit, 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, { largestUnit, 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/Instant/prototype/since/roundingmode-floor.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/roundingmode-floor.js
new file mode 100644
index 0000000000..01ce9ffbb4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/roundingmode-floor.js
@@ -0,0 +1,42 @@
+// |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.instant.prototype.since
+description: Tests calculations with roundingMode "floor".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(217178610_123_456_789n /* 1976-11-18T15:23:30.123456789Z */);
+const later = new Temporal.Instant(1572345998_271_986_289n /* 2019-10-29T10:46:38.271986289Z */);
+
+const expected = [
+ ["hours", [0, 0, 0, 0, 376435], [0, 0, 0, 0, -376436]],
+ ["minutes", [0, 0, 0, 0, 376435, 23], [0, 0, 0, 0, -376435, -24]],
+ ["seconds", [0, 0, 0, 0, 376435, 23, 8], [0, 0, 0, 0, -376435, -23, -9]],
+ ["milliseconds", [0, 0, 0, 0, 376435, 23, 8, 148], [0, 0, 0, 0, -376435, -23, -8, -149]],
+ ["microseconds", [0, 0, 0, 0, 376435, 23, 8, 148, 529], [0, 0, 0, 0, -376435, -23, -8, -148, -530]],
+ ["nanoseconds", [0, 0, 0, 0, 376435, 23, 8, 148, 529, 500], [0, 0, 0, 0, -376435, -23, -8, -148, -529, -500]],
+];
+
+const roundingMode = "floor";
+const largestUnit = "hours";
+
+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, { largestUnit, 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, { largestUnit, 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/Instant/prototype/since/roundingmode-halfCeil.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/roundingmode-halfCeil.js
new file mode 100644
index 0000000000..494f86fd5b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/roundingmode-halfCeil.js
@@ -0,0 +1,42 @@
+// |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.instant.prototype.since
+description: Tests calculations with roundingMode "halfCeil".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(217178610_123_456_789n /* 1976-11-18T15:23:30.123456789Z */);
+const later = new Temporal.Instant(1572345998_271_986_289n /* 2019-10-29T10:46:38.271986289Z */);
+
+const expected = [
+ ["hours", [0, 0, 0, 0, 376435], [0, 0, 0, 0, -376435]],
+ ["minutes", [0, 0, 0, 0, 376435, 23], [0, 0, 0, 0, -376435, -23]],
+ ["seconds", [0, 0, 0, 0, 376435, 23, 8], [0, 0, 0, 0, -376435, -23, -8]],
+ ["milliseconds", [0, 0, 0, 0, 376435, 23, 8, 149], [0, 0, 0, 0, -376435, -23, -8, -149]],
+ ["microseconds", [0, 0, 0, 0, 376435, 23, 8, 148, 530], [0, 0, 0, 0, -376435, -23, -8, -148, -529]],
+ ["nanoseconds", [0, 0, 0, 0, 376435, 23, 8, 148, 529, 500], [0, 0, 0, 0, -376435, -23, -8, -148, -529, -500]],
+];
+
+const roundingMode = "halfCeil";
+const largestUnit = "hours";
+
+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, { largestUnit, 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, { largestUnit, 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/Instant/prototype/since/roundingmode-halfEven.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/roundingmode-halfEven.js
new file mode 100644
index 0000000000..10ad707521
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/roundingmode-halfEven.js
@@ -0,0 +1,42 @@
+// |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.instant.prototype.since
+description: Tests calculations with roundingMode "halfEven".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(217178610_123_456_789n /* 1976-11-18T15:23:30.123456789Z */);
+const later = new Temporal.Instant(1572345998_271_986_289n /* 2019-10-29T10:46:38.271986289Z */);
+
+const expected = [
+ ["hours", [0, 0, 0, 0, 376435], [0, 0, 0, 0, -376435]],
+ ["minutes", [0, 0, 0, 0, 376435, 23], [0, 0, 0, 0, -376435, -23]],
+ ["seconds", [0, 0, 0, 0, 376435, 23, 8], [0, 0, 0, 0, -376435, -23, -8]],
+ ["milliseconds", [0, 0, 0, 0, 376435, 23, 8, 149], [0, 0, 0, 0, -376435, -23, -8, -149]],
+ ["microseconds", [0, 0, 0, 0, 376435, 23, 8, 148, 530], [0, 0, 0, 0, -376435, -23, -8, -148, -530]],
+ ["nanoseconds", [0, 0, 0, 0, 376435, 23, 8, 148, 529, 500], [0, 0, 0, 0, -376435, -23, -8, -148, -529, -500]],
+];
+
+const roundingMode = "halfEven";
+const largestUnit = "hours";
+
+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, { largestUnit, 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, { largestUnit, 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/Instant/prototype/since/roundingmode-halfExpand.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/roundingmode-halfExpand.js
new file mode 100644
index 0000000000..877825cb6f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/roundingmode-halfExpand.js
@@ -0,0 +1,42 @@
+// |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.instant.prototype.since
+description: Tests calculations with roundingMode "halfExpand".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(217178610_123_456_789n /* 1976-11-18T15:23:30.123456789Z */);
+const later = new Temporal.Instant(1572345998_271_986_289n /* 2019-10-29T10:46:38.271986289Z */);
+
+const expected = [
+ ["hours", [0, 0, 0, 0, 376435], [0, 0, 0, 0, -376435]],
+ ["minutes", [0, 0, 0, 0, 376435, 23], [0, 0, 0, 0, -376435, -23]],
+ ["seconds", [0, 0, 0, 0, 376435, 23, 8], [0, 0, 0, 0, -376435, -23, -8]],
+ ["milliseconds", [0, 0, 0, 0, 376435, 23, 8, 149], [0, 0, 0, 0, -376435, -23, -8, -149]],
+ ["microseconds", [0, 0, 0, 0, 376435, 23, 8, 148, 530], [0, 0, 0, 0, -376435, -23, -8, -148, -530]],
+ ["nanoseconds", [0, 0, 0, 0, 376435, 23, 8, 148, 529, 500], [0, 0, 0, 0, -376435, -23, -8, -148, -529, -500]],
+];
+
+const roundingMode = "halfExpand";
+const largestUnit = "hours";
+
+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, { largestUnit, 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, { largestUnit, 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/Instant/prototype/since/roundingmode-halfFloor.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/roundingmode-halfFloor.js
new file mode 100644
index 0000000000..98b0d621ed
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/roundingmode-halfFloor.js
@@ -0,0 +1,42 @@
+// |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.instant.prototype.since
+description: Tests calculations with roundingMode "halfFloor".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(217178610_123_456_789n /* 1976-11-18T15:23:30.123456789Z */);
+const later = new Temporal.Instant(1572345998_271_986_289n /* 2019-10-29T10:46:38.271986289Z */);
+
+const expected = [
+ ["hours", [0, 0, 0, 0, 376435], [0, 0, 0, 0, -376435]],
+ ["minutes", [0, 0, 0, 0, 376435, 23], [0, 0, 0, 0, -376435, -23]],
+ ["seconds", [0, 0, 0, 0, 376435, 23, 8], [0, 0, 0, 0, -376435, -23, -8]],
+ ["milliseconds", [0, 0, 0, 0, 376435, 23, 8, 149], [0, 0, 0, 0, -376435, -23, -8, -149]],
+ ["microseconds", [0, 0, 0, 0, 376435, 23, 8, 148, 529], [0, 0, 0, 0, -376435, -23, -8, -148, -530]],
+ ["nanoseconds", [0, 0, 0, 0, 376435, 23, 8, 148, 529, 500], [0, 0, 0, 0, -376435, -23, -8, -148, -529, -500]],
+];
+
+const roundingMode = "halfFloor";
+const largestUnit = "hours";
+
+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, { largestUnit, 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, { largestUnit, 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/Instant/prototype/since/roundingmode-halfTrunc.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/roundingmode-halfTrunc.js
new file mode 100644
index 0000000000..4fa53d517e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/roundingmode-halfTrunc.js
@@ -0,0 +1,42 @@
+// |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.instant.prototype.since
+description: Tests calculations with roundingMode "halfTrunc".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(217178610_123_456_789n /* 1976-11-18T15:23:30.123456789Z */);
+const later = new Temporal.Instant(1572345998_271_986_289n /* 2019-10-29T10:46:38.271986289Z */);
+
+const expected = [
+ ["hours", [0, 0, 0, 0, 376435], [0, 0, 0, 0, -376435]],
+ ["minutes", [0, 0, 0, 0, 376435, 23], [0, 0, 0, 0, -376435, -23]],
+ ["seconds", [0, 0, 0, 0, 376435, 23, 8], [0, 0, 0, 0, -376435, -23, -8]],
+ ["milliseconds", [0, 0, 0, 0, 376435, 23, 8, 149], [0, 0, 0, 0, -376435, -23, -8, -149]],
+ ["microseconds", [0, 0, 0, 0, 376435, 23, 8, 148, 529], [0, 0, 0, 0, -376435, -23, -8, -148, -529]],
+ ["nanoseconds", [0, 0, 0, 0, 376435, 23, 8, 148, 529, 500], [0, 0, 0, 0, -376435, -23, -8, -148, -529, -500]],
+];
+
+const roundingMode = "halfTrunc";
+const largestUnit = "hours";
+
+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, { largestUnit, 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, { largestUnit, 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/Instant/prototype/since/roundingmode-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/roundingmode-invalid-string.js
new file mode 100644
index 0000000000..40864eff60
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/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.instant.prototype.since
+description: RangeError thrown when roundingMode option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(1_000_000_000_000_000_000n);
+const later = new Temporal.Instant(1_000_090_061_123_987_500n);
+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/Instant/prototype/since/roundingmode-trunc.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/roundingmode-trunc.js
new file mode 100644
index 0000000000..55a8553558
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/roundingmode-trunc.js
@@ -0,0 +1,42 @@
+// |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.instant.prototype.since
+description: Tests calculations with roundingMode "trunc".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(217178610_123_456_789n /* 1976-11-18T15:23:30.123456789Z */);
+const later = new Temporal.Instant(1572345998_271_986_289n /* 2019-10-29T10:46:38.271986289Z */);
+
+const expected = [
+ ["hours", [0, 0, 0, 0, 376435], [0, 0, 0, 0, -376435]],
+ ["minutes", [0, 0, 0, 0, 376435, 23], [0, 0, 0, 0, -376435, -23]],
+ ["seconds", [0, 0, 0, 0, 376435, 23, 8], [0, 0, 0, 0, -376435, -23, -8]],
+ ["milliseconds", [0, 0, 0, 0, 376435, 23, 8, 148], [0, 0, 0, 0, -376435, -23, -8, -148]],
+ ["microseconds", [0, 0, 0, 0, 376435, 23, 8, 148, 529], [0, 0, 0, 0, -376435, -23, -8, -148, -529]],
+ ["nanoseconds", [0, 0, 0, 0, 376435, 23, 8, 148, 529, 500], [0, 0, 0, 0, -376435, -23, -8, -148, -529, -500]],
+];
+
+const roundingMode = "trunc";
+const largestUnit = "hours";
+
+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, { largestUnit, 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, { largestUnit, 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/Instant/prototype/since/roundingmode-undefined.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/roundingmode-undefined.js
new file mode 100644
index 0000000000..5fce1dc364
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/roundingmode-undefined.js
@@ -0,0 +1,30 @@
+// |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.instant.prototype.since
+description: Fallback value for roundingMode option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(1_000_000_000_000_000_000n);
+const later = new Temporal.Instant(1_000_090_061_123_987_500n);
+
+const explicit1 = later.since(earlier, { smallestUnit: "microsecond", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit1, 0, 0, 0, 0, 0, 0, 90061, 123, 987, 0, "default roundingMode is trunc");
+const implicit1 = later.since(earlier, { smallestUnit: "microsecond" });
+TemporalHelpers.assertDuration(implicit1, 0, 0, 0, 0, 0, 0, 90061, 123, 987, 0, "default roundingMode is trunc");
+
+const explicit2 = later.since(earlier, { smallestUnit: "millisecond", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit2, 0, 0, 0, 0, 0, 0, 90061, 123, 0, 0, "default roundingMode is trunc");
+const implicit2 = later.since(earlier, { smallestUnit: "millisecond" });
+TemporalHelpers.assertDuration(implicit2, 0, 0, 0, 0, 0, 0, 90061, 123, 0, 0, "default roundingMode is trunc");
+
+const explicit3 = later.since(earlier, { smallestUnit: "second", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit3, 0, 0, 0, 0, 0, 0, 90061, 0, 0, 0, "default roundingMode is trunc");
+const implicit3 = later.since(earlier, { smallestUnit: "second" });
+TemporalHelpers.assertDuration(implicit3, 0, 0, 0, 0, 0, 0, 90061, 0, 0, 0, "default roundingMode is trunc");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/roundingmode-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/roundingmode-wrong-type.js
new file mode 100644
index 0000000000..f1321842f8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/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.instant.prototype.since
+description: Type conversions for roundingMode option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(1_000_000_000_000_000_000n);
+const later = new Temporal.Instant(1_000_090_061_123_987_500n);
+TemporalHelpers.checkStringOptionWrongType("roundingMode", "trunc",
+ (roundingMode) => later.since(earlier, { smallestUnit: "microsecond", roundingMode }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 90061, 123, 987, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/shell.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/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/Instant/prototype/since/smallestunit-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/smallestunit-invalid-string.js
new file mode 100644
index 0000000000..3d40904d59
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/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.instant.prototype.since
+description: RangeError thrown when smallestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(1_000_000_000_000_000_000n);
+const later = new Temporal.Instant(1_000_090_061_987_654_321n);
+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/Instant/prototype/since/smallestunit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/smallestunit-plurals-accepted.js
new file mode 100644
index 0000000000..63babfe414
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/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.instant.prototype.since
+description: Plural units are accepted as well for the smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(1_000_000_000_987_654_321n);
+const later = new Temporal.Instant(1_086_403_661_988_655_322n);
+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/Instant/prototype/since/smallestunit-undefined.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/smallestunit-undefined.js
new file mode 100644
index 0000000000..5c53a7cc9e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/smallestunit-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.instant.prototype.since
+description: Fallback value for smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(1_000_000_000_000_000_000n);
+const later = new Temporal.Instant(1_000_090_061_987_654_321n);
+
+const explicit = later.since(earlier, { smallestUnit: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 0, 0, 0, 90061, 987, 654, 321, "default smallestUnit is nanosecond");
+const implicit = later.since(earlier, {});
+TemporalHelpers.assertDuration(implicit, 0, 0, 0, 0, 0, 0, 90061, 987, 654, 321, "default smallestUnit is nanosecond");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/smallestunit-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/smallestunit-wrong-type.js
new file mode 100644
index 0000000000..1d240650c3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/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.instant.prototype.since
+description: Type conversions for smallestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(1_000_000_000_000_000_000n);
+const later = new Temporal.Instant(1_000_090_061_987_654_321n);
+TemporalHelpers.checkStringOptionWrongType("smallestUnit", "microsecond",
+ (smallestUnit) => later.since(earlier, { smallestUnit }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 90061, 987, 654, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/year-zero.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/year-zero.js
new file mode 100644
index 0000000000..5ecac41473
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/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.instant.prototype.since
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-03-30T00:45Z",
+ "-000000-03-30T01:45+01:00",
+ "-000000-03-30T01:45:00+00:00[UTC]",
+];
+const instance = new Temporal.Instant(0n);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.since(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/argument-duration-max.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/argument-duration-max.js
new file mode 100644
index 0000000000..381ec6bde0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/argument-duration-max.js
@@ -0,0 +1,41 @@
+// |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.instant.prototype.subtract
+description: Maximum allowed duration
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(0n);
+
+const maxCases = [
+ ["PT2400000000H", "string with max hours"],
+ [{ hours: 2400000000 }, "property bag with max hours"],
+ ["PT144000000000M", "string with max minutes"],
+ [{ minutes: 144000000000 }, "property bag with max minutes"],
+ ["PT8640000000000S", "string with max seconds"],
+ [{ seconds: 8640000000000 }, "property bag with max seconds"],
+];
+
+for (const [arg, descr] of maxCases) {
+ const result = instance.subtract(arg);
+ assert.sameValue(result.epochNanoseconds, -8640000000000000000000n, `operation succeeds with ${descr}`);
+}
+
+const minCases = [
+ ["-PT2400000000H", "string with min hours"],
+ [{ hours: -2400000000 }, "property bag with min hours"],
+ ["-PT144000000000M", "string with min minutes"],
+ [{ minutes: -144000000000 }, "property bag with min minutes"],
+ ["-PT8640000000000S", "string with min seconds"],
+ [{ seconds: -8640000000000 }, "property bag with min seconds"],
+];
+
+for (const [arg, descr] of minCases) {
+ const result = instance.subtract(arg);
+ assert.sameValue(result.epochNanoseconds, 8640000000000000000000n, `operation succeeds with ${descr}`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/argument-duration-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/argument-duration-out-of-range.js
new file mode 100644
index 0000000000..9ac3021693
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/argument-duration-out-of-range.js
@@ -0,0 +1,75 @@
+// |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.instant.prototype.subtract
+description: Duration-like argument that is out of range
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(0n);
+
+const cases = [
+ // 2^32 = 4294967296
+ ["P4294967296Y", "string with years > max"],
+ [{ years: 4294967296 }, "property bag with years > max"],
+ ["-P4294967296Y", "string with years < min"],
+ [{ years: -4294967296 }, "property bag with years < min"],
+ ["P4294967296M", "string with months > max"],
+ [{ months: 4294967296 }, "property bag with months > max"],
+ ["-P4294967296M", "string with months < min"],
+ [{ months: -4294967296 }, "property bag with months < min"],
+ ["P4294967296W", "string with weeks > max"],
+ [{ weeks: 4294967296 }, "property bag with weeks > max"],
+ ["-P4294967296W", "string with weeks < min"],
+ [{ weeks: -4294967296 }, "property bag with weeks < min"],
+
+ // ceil(max safe integer / 86400) = 104249991375
+ ["P104249991375D", "string with days > max"],
+ [{ days: 104249991375 }, "property bag with days > max"],
+ ["P104249991374DT24H", "string where hours balance into days > max"],
+ [{ days: 104249991374, hours: 24 }, "property bag where hours balance into days > max"],
+ ["-P104249991375D", "string with days < min"],
+ [{ days: -104249991375 }, "property bag with days < min"],
+ ["-P104249991374DT24H", "string where hours balance into days < min"],
+ [{ days: -104249991374, hours: -24 }, "property bag where hours balance into days < min"],
+
+ // ceil(max safe integer / 3600) = 2501999792984
+ ["PT2501999792984H", "string with hours > max"],
+ [{ hours: 2501999792984 }, "property bag with hours > max"],
+ ["PT2501999792983H60M", "string where minutes balance into hours > max"],
+ [{ hours: 2501999792983, minutes: 60 }, "property bag where minutes balance into hours > max"],
+ ["-PT2501999792984H", "string with hours < min"],
+ [{ hours: -2501999792984 }, "property bag with hours < min"],
+ ["-PT2501999792983H60M", "string where minutes balance into hours < min"],
+ [{ hours: -2501999792983, minutes: -60 }, "property bag where minutes balance into hours < min"],
+
+ // ceil(max safe integer / 60) = 150119987579017
+ ["PT150119987579017M", "string with minutes > max"],
+ [{ minutes: 150119987579017 }, "property bag with minutes > max"],
+ ["PT150119987579016M60S", "string where seconds balance into minutes > max"],
+ [{ minutes: 150119987579016, seconds: 60 }, "property bag where seconds balance into minutes > max"],
+ ["-PT150119987579017M", "string with minutes < min"],
+ [{ minutes: -150119987579017 }, "property bag with minutes < min"],
+ ["-PT150119987579016M60S", "string where seconds balance into minutes < min"],
+ [{ minutes: -150119987579016, seconds: -60 }, "property bag where seconds balance into minutes < min"],
+
+ // 2^53 = 9007199254740992
+ ["PT9007199254740992S", "string with seconds > max"],
+ [{ seconds: 9007199254740992 }, "property bag with seconds > max"],
+ [{ seconds: 9007199254740991, milliseconds: 1000 }, "property bag where milliseconds balance into seconds > max"],
+ [{ seconds: 9007199254740991, microseconds: 1000000 }, "property bag where microseconds balance into seconds > max"],
+ [{ seconds: 9007199254740991, nanoseconds: 1000000000 }, "property bag where nanoseconds balance into seconds > max"],
+ ["-PT9007199254740992S", "string with seconds < min"],
+ [{ seconds: -9007199254740992 }, "property bag with seconds < min"],
+ [{ seconds: -9007199254740991, milliseconds: -1000 }, "property bag where milliseconds balance into seconds < min"],
+ [{ seconds: -9007199254740991, microseconds: -1000000 }, "property bag where microseconds balance into seconds < min"],
+ [{ seconds: -9007199254740991, nanoseconds: -1000000000 }, "property bag where nanoseconds balance into seconds < min"],
+];
+
+for (const [arg, descr] of cases) {
+ assert.throws(RangeError, () => instance.subtract(arg), `${descr} is out of range`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/argument-invalid-property.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/argument-invalid-property.js
new file mode 100644
index 0000000000..1bf3dd7682
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/argument-invalid-property.js
@@ -0,0 +1,31 @@
+// |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.instant.prototype.subtract
+description: temporalDurationLike object must contain at least one correctly spelled property
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(1_000_000_000_000_000_000n);
+
+assert.throws(
+ TypeError,
+ () => instance.subtract({}),
+ "Throws TypeError if no property is present"
+);
+
+assert.throws(
+ TypeError,
+ () => instance.subtract({ nonsense: true }),
+ "Throws TypeError if no recognized property is present"
+);
+
+assert.throws(
+ TypeError,
+ () => instance.subtract({ sign: 1 }),
+ "Sign property is not recognized"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/argument-mixed-sign.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/argument-mixed-sign.js
new file mode 100644
index 0000000000..f7013503d0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/argument-mixed-sign.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.instant.prototype.subtract
+description: Positive and negative values in the temporalDurationLike argument are not acceptable
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(1_000_000_000_000_000_000n);
+
+assert.throws(
+ RangeError,
+ () => instance.subtract({ hours: 1, minutes: -30 }),
+ `mixed positive and negative values always throw`
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/argument-not-object.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/argument-not-object.js
new file mode 100644
index 0000000000..58e12c8337
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/argument-not-object.js
@@ -0,0 +1,22 @@
+// |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.instant.prototype.subtract
+description: Passing a primitive other than string to subtract() throws
+features: [Symbol, Temporal]
+---*/
+
+const instance = new Temporal.Instant(1_000_000_000_000_000_000n);
+assert.throws(TypeError, () => instance.subtract(undefined), "undefined");
+assert.throws(TypeError, () => instance.subtract(null), "null");
+assert.throws(TypeError, () => instance.subtract(true), "boolean");
+assert.throws(RangeError, () => instance.subtract(""), "empty string");
+assert.throws(TypeError, () => instance.subtract(Symbol()), "Symbol");
+assert.throws(TypeError, () => instance.subtract(7), "number");
+assert.throws(TypeError, () => instance.subtract(7n), "bigint");
+assert.throws(TypeError, () => instance.subtract([]), "array");
+assert.throws(TypeError, () => instance.subtract(() => {}), "function");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/argument-singular-properties.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/argument-singular-properties.js
new file mode 100644
index 0000000000..9a32b6d94b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/argument-singular-properties.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.instant.prototype.subtract
+description: Singular properties in the property bag are always ignored
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(1_000_000_000_000_000_000n);
+
+[
+ { year: 1 },
+ { month: 2 },
+ { week: 3 },
+ { day: 4 },
+ { hour: 5 },
+ { minute: 6 },
+ { second: 7 },
+ { millisecond: 8 },
+ { microsecond: 9 },
+ { nanosecond: 10 },
+].forEach((badObject) => {
+ assert.throws(TypeError, () => instance.subtract(badObject),
+ "Throw TypeError if temporalDurationLike is not valid");
+});
+
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/argument-string-fractional-units-rounding-mode.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/argument-string-fractional-units-rounding-mode.js
new file mode 100644
index 0000000000..b224f22571
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/argument-string-fractional-units-rounding-mode.js
@@ -0,0 +1,18 @@
+// |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.instant.prototype.subtract
+description: Strings with fractional duration units are rounded with the correct rounding mode
+features: [Temporal]
+---*/
+
+const epoch = new Temporal.Instant(0n);
+
+assert.sameValue(epoch.subtract("PT1.03125H").epochNanoseconds, -3712_500_000_000n,
+ "positive fractional units rounded with correct rounding mode");
+assert.sameValue(epoch.subtract("-PT1.03125H").epochNanoseconds, 3712_500_000_000n,
+ "negative fractional units rounded with correct rounding mode");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/argument-string-negative-fractional-units.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/argument-string-negative-fractional-units.js
new file mode 100644
index 0000000000..45e364cebf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/argument-string-negative-fractional-units.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.instant.prototype.subtract
+description: Strings with fractional duration units are treated with the correct sign
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(1_000_000_000_000_000_000n);
+
+const resultHours = instance.subtract("-PT24.567890123H");
+assert.sameValue(resultHours.epochNanoseconds, 1_000_088_444_404_442_800n, "negative fractional hours");
+
+const resultMinutes = instance.subtract("-PT1440.567890123M");
+assert.sameValue(resultMinutes.epochNanoseconds, 1_000_086_434_073_407_380n, "negative fractional minutes");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/argument-string.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/argument-string.js
new file mode 100644
index 0000000000..7562fe6e00
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/argument-string.js
@@ -0,0 +1,15 @@
+// |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.instant.prototype.subtract
+description: A string is parsed into the correct object when passed as the argument
+features: [Temporal]
+---*/
+
+const instance = Temporal.Instant.fromEpochSeconds(10);
+const result = instance.subtract("PT3H");
+assert.sameValue(result.epochNanoseconds, -10_790_000_000_000n, "epochNanoseconds result");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/basic.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/basic.js
new file mode 100644
index 0000000000..49adee0779
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/basic.js
@@ -0,0 +1,75 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.subtract
+description: Basic functionality of Temporal.Instant.prototype.subtract()
+info: |
+ 1. Let instant be the this value.
+ 2. Perform ? RequireInternalSlot(instant, [[InitializedTemporalInstant]]).
+ 3. Let duration be ? ToLimitedTemporalDuration(temporalDurationLike, « "years", "months", "weeks", "days" »).
+ 4. Let ns be ? AddInstant(instant.[[EpochNanoseconds]], duration.[[Hours]], duration.[[Minutes]], duration.[[Seconds]], duration.[[Milliseconds]], duration.[[Microseconds]], duration.[[Nanoseconds]]).
+ 5. Return ! CreateTemporalInstant(ns).
+features: [Temporal]
+---*/
+
+const inst = new Temporal.Instant(50000n);
+
+let result = inst.subtract(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 3, 2, 1));
+assert.sameValue(
+ -2952001n,
+ result.epochNanoseconds,
+ "subtract positive sub-seconds"
+);
+
+result = inst.subtract(new Temporal.Duration(0, 0, 0, 0, 0, 0, 4, 3, 2, 1));
+assert.sameValue(
+ BigInt(-4 * 1e9) - 2952001n,
+ result.epochNanoseconds,
+ "subtract positive seconds"
+);
+
+result = inst.subtract(new Temporal.Duration(0, 0, 0, 0, 0, 5, 4, 3, 2, 1));
+assert.sameValue(
+ BigInt(5 * 60 + 4) * -1000000000n - 2952001n,
+ result.epochNanoseconds,
+ "subtract positive minutes"
+);
+
+result = inst.subtract(new Temporal.Duration(0, 0, 0, 0, 6, 5, 4, 3, 2, 1));
+assert.sameValue(
+ BigInt(6 * 3600 + 5 * 60 + 4) * -1000000000n - 2952001n,
+ result.epochNanoseconds,
+ "subtract positive hours"
+);
+
+result = inst.subtract(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, -3, -2, -1));
+assert.sameValue(
+ 3052001n,
+ result.epochNanoseconds,
+ "subtract negative sub-seconds"
+);
+
+result = inst.subtract(new Temporal.Duration(0, 0, 0, 0, 0, 0, -4, -3, -2, -1));
+assert.sameValue(
+ BigInt(4 * 1e9) + 3052001n,
+ result.epochNanoseconds,
+ "subtract negative seconds"
+);
+
+result = inst.subtract(new Temporal.Duration(0, 0, 0, 0, 0, -5, -4, -3, -2, -1));
+assert.sameValue(
+ BigInt(5 * 60 + 4) * 1000000000n + 3052001n,
+ result.epochNanoseconds,
+ "subtract negative minutes"
+);
+
+result = inst.subtract(new Temporal.Duration(0, 0, 0, 0, -6, -5, -4, -3, -2, -1));
+assert.sameValue(
+ BigInt(6 * 3600 + 5 * 60 + 4) * 1000000000n + 3052001n,
+ result.epochNanoseconds,
+ "subtract negative hours"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/branding.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/branding.js
new file mode 100644
index 0000000000..dd1f2efc54
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/branding.js
@@ -0,0 +1,27 @@
+// |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.instant.prototype.subtract
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const subtract = Temporal.Instant.prototype.subtract;
+
+assert.sameValue(typeof subtract, "function");
+
+const args = [new Temporal.Duration(0, 0, 0, 0, 5)];
+
+assert.throws(TypeError, () => subtract.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => subtract.apply(null, args), "null");
+assert.throws(TypeError, () => subtract.apply(true, args), "true");
+assert.throws(TypeError, () => subtract.apply("", args), "empty string");
+assert.throws(TypeError, () => subtract.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => subtract.apply(1, args), "1");
+assert.throws(TypeError, () => subtract.apply({}, args), "plain object");
+assert.throws(TypeError, () => subtract.apply(Temporal.Instant, args), "Temporal.Instant");
+assert.throws(TypeError, () => subtract.apply(Temporal.Instant.prototype, args), "Temporal.Instant.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/browser.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/builtin.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/builtin.js
new file mode 100644
index 0000000000..538f05754c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/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.instant.prototype.subtract
+description: >
+ Tests that Temporal.Instant.prototype.subtract
+ 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.Instant.prototype.subtract),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Instant.prototype.subtract),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Instant.prototype.subtract),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Instant.prototype.subtract.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/disallowed-duration-units.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/disallowed-duration-units.js
new file mode 100644
index 0000000000..a7ceddfa95
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/disallowed-duration-units.js
@@ -0,0 +1,34 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.subtract
+description: |
+ Temporal.Instant.prototype.subtract() throws RangeError when the duration has
+ non-zero years, months, weeks or days.
+info: |
+ 1. Let instant be the this value.
+ 3. Let duration be ? ToLimitedTemporalDuration(temporalDurationLike, « "years", "months", "weeks", "days" »).
+features: [Temporal]
+---*/
+
+let i1 = new Temporal.Instant(500000n);
+assert.throws(RangeError, () => i1.subtract(new Temporal.Duration(1)),
+ "should throw RangeError when the duration has non-zero years (positive)");
+assert.throws(RangeError, () => i1.subtract(new Temporal.Duration(0, 2)),
+ "should throw RangeError when the duration has non-zero months (positive)");
+assert.throws(RangeError, () => i1.subtract(new Temporal.Duration(0, 0, 3)),
+ "should throw RangeError when the duration has non-zero weeks (positive)");
+assert.throws(RangeError, () => i1.subtract(new Temporal.Duration(0, 0, 0, 4)),
+ "should throw RangeError when the duration has non-zero days (positive)");
+assert.throws(RangeError, () => i1.subtract(new Temporal.Duration(-1)),
+ "should throw RangeError when the duration has non-zero years (negative)");
+assert.throws(RangeError, () => i1.subtract(new Temporal.Duration(0, -2)),
+ "should throw RangeError when the duration has non-zero months (negative)");
+assert.throws(RangeError, () => i1.subtract(new Temporal.Duration(0, 0, -3)),
+ "should throw RangeError when the duration has non-zero weeks (negative)");
+assert.throws(RangeError, () => i1.subtract(new Temporal.Duration(0, 0, 0, -4)),
+ "should throw RangeError when the duration has non-zero days (negative)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..d18b80a18f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/infinity-throws-rangeerror.js
@@ -0,0 +1,33 @@
+// |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.
+
+/*---
+description: Temporal.Instant.prototype.subtract throws a RangeError if any value in a property bag is Infinity
+esid: sec-temporal.instant.prototype.subtract
+features: [Temporal]
+---*/
+
+const fields = ["hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"];
+
+const instance = Temporal.Instant.fromEpochSeconds(10);
+
+fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.subtract({ [field]: Infinity }));
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return Infinity;
+ }
+};
+
+fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => instance.subtract({ [field]: obj }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/length.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/length.js
new file mode 100644
index 0000000000..a31774766c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/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.instant.prototype.subtract
+description: Temporal.Instant.prototype.subtract.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.Instant.prototype.subtract, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/minimum-maximum-instant.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/minimum-maximum-instant.js
new file mode 100644
index 0000000000..a1b056050b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/minimum-maximum-instant.js
@@ -0,0 +1,55 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.subtract
+description: >
+ Instant is minimum/maximum instant.
+features: [Temporal]
+---*/
+
+let min = new Temporal.Instant(-86_40000_00000_00000_00000n);
+let max = new Temporal.Instant(86_40000_00000_00000_00000n);
+
+let zero = Temporal.Duration.from({nanoseconds: 0});
+let one = Temporal.Duration.from({nanoseconds: 1});
+let minusOne = Temporal.Duration.from({nanoseconds: -1});
+
+// Adding zero to the minimum instant.
+assert.sameValue(min.subtract(zero).epochNanoseconds, min.epochNanoseconds);
+
+// Adding zero to the maximum instant.
+assert.sameValue(max.subtract(zero).epochNanoseconds, max.epochNanoseconds);
+
+// Subtracting one from the minimum instant.
+assert.throws(RangeError, () => min.subtract(one));
+
+// Adding one to the maximum instant.
+assert.throws(RangeError, () => max.subtract(minusOne));
+
+// Adding one to the minimum instant.
+assert.sameValue(min.subtract(minusOne).epochNanoseconds, min.epochNanoseconds + 1n);
+
+// Subtracting one from the maximum instant.
+assert.sameValue(max.subtract(one).epochNanoseconds, max.epochNanoseconds - 1n);
+
+// From minimum to maximum instant.
+assert.sameValue(min.subtract({nanoseconds: -86_40000_00000_00000_00000 * 2}).epochNanoseconds, max.epochNanoseconds);
+
+assert.sameValue(min.subtract({microseconds: -8640_00000_00000_00000 * 2}).epochNanoseconds, max.epochNanoseconds);
+
+assert.sameValue(min.subtract({milliseconds: -8_64000_00000_00000 * 2}).epochNanoseconds, max.epochNanoseconds);
+
+assert.sameValue(min.subtract({seconds: -864_00000_00000 * 2}).epochNanoseconds, max.epochNanoseconds);
+
+// From maximum to minimum instant.
+assert.sameValue(max.subtract({nanoseconds: 86_40000_00000_00000_00000 * 2}).epochNanoseconds, min.epochNanoseconds);
+
+assert.sameValue(max.subtract({microseconds: 8640_00000_00000_00000 * 2}).epochNanoseconds, min.epochNanoseconds);
+
+assert.sameValue(max.subtract({milliseconds: 8_64000_00000_00000 * 2}).epochNanoseconds, min.epochNanoseconds);
+
+assert.sameValue(max.subtract({seconds: 864_00000_00000 * 2}).epochNanoseconds, min.epochNanoseconds);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/name.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/name.js
new file mode 100644
index 0000000000..9be7a0a42e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/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.instant.prototype.subtract
+description: Temporal.Instant.prototype.subtract.name is "subtract".
+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.Instant.prototype.subtract, "name", {
+ value: "subtract",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/negative-infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/negative-infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..cb581446ce
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/negative-infinity-throws-rangeerror.js
@@ -0,0 +1,33 @@
+// |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.
+
+/*---
+description: Temporal.Instant.prototype.subtract throws a RangeError if any value in a property bag is -Infinity
+esid: sec-temporal.instant.prototype.subtract
+features: [Temporal]
+---*/
+
+const fields = ["hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"];
+
+const instance = Temporal.Instant.fromEpochSeconds(10);
+
+fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.subtract({ [field]: -Infinity }));
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return -Infinity;
+ }
+};
+
+fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => instance.subtract({ [field]: obj }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/non-integer-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/non-integer-throws-rangeerror.js
new file mode 100644
index 0000000000..4126d55d71
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/non-integer-throws-rangeerror.js
@@ -0,0 +1,29 @@
+// |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.instant.prototype.subtract
+description: A non-integer value for any recognized property in the property bag, throws a RangeError
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(1_000_000_000_000_000_000n);
+const fields = [
+ "years",
+ "months",
+ "weeks",
+ "days",
+ "hours",
+ "minutes",
+ "seconds",
+ "milliseconds",
+ "microseconds",
+ "nanoseconds",
+];
+fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.subtract({ [field]: 1.5 }));
+ assert.throws(RangeError, () => instance.subtract({ [field]: -1.5 }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/not-a-constructor.js
new file mode 100644
index 0000000000..e19b7ab15b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/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.instant.prototype.subtract
+description: >
+ Temporal.Instant.prototype.subtract 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.Instant.prototype.subtract();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Instant.prototype.subtract), false,
+ "isConstructor(Temporal.Instant.prototype.subtract)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/order-of-operations.js
new file mode 100644
index 0000000000..21fe755862
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/order-of-operations.js
@@ -0,0 +1,50 @@
+// |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.instant.prototype.subtract
+description: Properties on an object passed to subtract() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(10n);
+const expected = [
+ "get fields.days",
+ "get fields.hours",
+ "get fields.hours.valueOf",
+ "call fields.hours.valueOf",
+ "get fields.microseconds",
+ "get fields.microseconds.valueOf",
+ "call fields.microseconds.valueOf",
+ "get fields.milliseconds",
+ "get fields.milliseconds.valueOf",
+ "call fields.milliseconds.valueOf",
+ "get fields.minutes",
+ "get fields.minutes.valueOf",
+ "call fields.minutes.valueOf",
+ "get fields.months",
+ "get fields.nanoseconds",
+ "get fields.nanoseconds.valueOf",
+ "call fields.nanoseconds.valueOf",
+ "get fields.seconds",
+ "get fields.seconds.valueOf",
+ "call fields.seconds.valueOf",
+ "get fields.weeks",
+ "get fields.years",
+];
+const actual = [];
+const fields = TemporalHelpers.propertyBagObserver(actual, {
+ hours: 1,
+ minutes: 1,
+ seconds: 1,
+ milliseconds: 1,
+ microseconds: 1,
+ nanoseconds: 1,
+}, "fields");
+const result = instance.subtract(fields);
+assert.sameValue(result.epochNanoseconds, -3661001000991n, "epochNanoseconds result");
+assert.compareArray(actual, expected, "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/prop-desc.js
new file mode 100644
index 0000000000..83a5af247e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/prop-desc.js
@@ -0,0 +1,24 @@
+// |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.instant.prototype.subtract
+description: The "subtract" property of Temporal.Instant.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Instant.prototype.subtract,
+ "function",
+ "`typeof Instant.prototype.subtract` is `function`"
+);
+
+verifyProperty(Temporal.Instant.prototype, "subtract", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/result-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/result-out-of-range.js
new file mode 100644
index 0000000000..8eec34e330
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/result-out-of-range.js
@@ -0,0 +1,33 @@
+// |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.instant.prototype.subtract
+description: RangeError thrown if result is outside representable range
+features: [Temporal]
+---*/
+
+const fields = ["hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"];
+
+const earliest = Temporal.Instant.fromEpochNanoseconds(-8640000_000_000_000_000_000n);
+
+fields.forEach((field) => {
+ assert.throws(
+ RangeError,
+ () => earliest.subtract({ [field]: 1 }),
+ `subtracting ${field} with result out of range (negative)`
+ );
+});
+
+const latest = Temporal.Instant.fromEpochNanoseconds(8640000_000_000_000_000_000n);
+
+fields.forEach((field) => {
+ assert.throws(
+ RangeError,
+ () => latest.subtract({ [field]: -1 }),
+ `subtracting ${field} with result out of range (positive)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/shell.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/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/Instant/prototype/subtract/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/subclassing-ignored.js
new file mode 100644
index 0000000000..e91ed13876
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/subtract/subclassing-ignored.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.instant.prototype.subtract
+description: Objects of a subclass are never created as return values for subtract()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.Instant,
+ [10n],
+ "subtract",
+ [{ nanoseconds: 5 }],
+ (result) => {
+ assert.sameValue(result.epochNanoseconds, 5n, "epochNanoseconds result");
+ },
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toJSON/basic.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toJSON/basic.js
new file mode 100644
index 0000000000..789017f160
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toJSON/basic.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.instant.prototype.tojson
+description: Basic behavior for toJSON
+features: [BigInt, Temporal]
+---*/
+
+const tests = [
+ [new Temporal.Instant(192_258_181_000_000_000n), "1976-02-04T05:03:01Z"],
+ [new Temporal.Instant(0n), "1970-01-01T00:00:00Z"],
+ [new Temporal.Instant(30_000_000_000n), "1970-01-01T00:00:30Z"],
+ [new Temporal.Instant(30_123_400_000n), "1970-01-01T00:00:30.1234Z"],
+];
+
+const options = new Proxy({}, {
+ get() { throw new Test262Error("should not get properties off argument") }
+});
+for (const [instant, expected] of tests) {
+ assert.sameValue(instant.toJSON(), expected, "toJSON without argument");
+ assert.sameValue(instant.toJSON(options), expected, "toJSON with argument");
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toJSON/branding.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toJSON/branding.js
new file mode 100644
index 0000000000..dcef3a0e34
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toJSON/branding.js
@@ -0,0 +1,25 @@
+// |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.instant.prototype.tojson
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toJSON = Temporal.Instant.prototype.toJSON;
+
+assert.sameValue(typeof toJSON, "function");
+
+assert.throws(TypeError, () => toJSON.call(undefined), "undefined");
+assert.throws(TypeError, () => toJSON.call(null), "null");
+assert.throws(TypeError, () => toJSON.call(true), "true");
+assert.throws(TypeError, () => toJSON.call(""), "empty string");
+assert.throws(TypeError, () => toJSON.call(Symbol()), "symbol");
+assert.throws(TypeError, () => toJSON.call(1), "1");
+assert.throws(TypeError, () => toJSON.call({}), "plain object");
+assert.throws(TypeError, () => toJSON.call(Temporal.Instant), "Temporal.Instant");
+assert.throws(TypeError, () => toJSON.call(Temporal.Instant.prototype), "Temporal.Instant.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toJSON/browser.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toJSON/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toJSON/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toJSON/builtin.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toJSON/builtin.js
new file mode 100644
index 0000000000..10fa906345
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toJSON/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.instant.prototype.tojson
+description: >
+ Tests that Temporal.Instant.prototype.toJSON
+ 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.Instant.prototype.toJSON),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Instant.prototype.toJSON),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Instant.prototype.toJSON),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Instant.prototype.toJSON.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toJSON/length.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toJSON/length.js
new file mode 100644
index 0000000000..eee03491e3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toJSON/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.instant.prototype.tojson
+description: Temporal.Instant.prototype.toJSON.length is 0
+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.Instant.prototype.toJSON, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toJSON/name.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toJSON/name.js
new file mode 100644
index 0000000000..42f6bab57b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toJSON/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.instant.prototype.tojson
+description: Temporal.Instant.prototype.toJSON.name is "toJSON".
+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.Instant.prototype.toJSON, "name", {
+ value: "toJSON",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toJSON/negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toJSON/negative-epochnanoseconds.js
new file mode 100644
index 0000000000..ca8d51b2ec
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toJSON/negative-epochnanoseconds.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.instant.prototype.tojson
+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]
+---*/
+
+const datetime = new Temporal.Instant(-13849764_999_999_999n);
+
+// 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 result = datetime.toJSON();
+assert.sameValue(result, "1969-07-24T16:50:35.000000001Z");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toJSON/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toJSON/not-a-constructor.js
new file mode 100644
index 0000000000..d72bd8cb72
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toJSON/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.instant.prototype.tojson
+description: >
+ Temporal.Instant.prototype.toJSON 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.Instant.prototype.toJSON();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Instant.prototype.toJSON), false,
+ "isConstructor(Temporal.Instant.prototype.toJSON)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toJSON/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toJSON/prop-desc.js
new file mode 100644
index 0000000000..059e5facd7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toJSON/prop-desc.js
@@ -0,0 +1,24 @@
+// |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.instant.prototype.tojson
+description: The "toJSON" property of Temporal.Instant.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Instant.prototype.toJSON,
+ "function",
+ "`typeof Instant.prototype.toJSON` is `function`"
+);
+
+verifyProperty(Temporal.Instant.prototype, "toJSON", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toJSON/shell.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toJSON/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toJSON/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/Instant/prototype/toJSON/year-format.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toJSON/year-format.js
new file mode 100644
index 0000000000..6a3661e338
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toJSON/year-format.js
@@ -0,0 +1,56 @@
+// |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.instant.prototype.tojson
+description: Verify that the year is appropriately formatted as 4 or 6 digits
+features: [Temporal]
+---*/
+
+function epochNsInYear(year) {
+ // Return an epoch nanoseconds value near the middle of the given year
+ const avgNsPerYear = 31_556_952_000_000_000n;
+ return (year - 1970n) * avgNsPerYear + (avgNsPerYear / 2n);
+}
+
+let instance = new Temporal.Instant(epochNsInYear(-100000n));
+assert.sameValue(instance.toJSON(), "-100000-07-01T21:30:36Z", "large negative year formatted as 6-digit");
+
+instance = new Temporal.Instant(epochNsInYear(-10000n));
+assert.sameValue(instance.toJSON(), "-010000-07-01T21:30:36Z", "smallest 5-digit negative year formatted as 6-digit");
+
+instance = new Temporal.Instant(epochNsInYear(-9999n));
+assert.sameValue(instance.toJSON(), "-009999-07-02T03:19:48Z", "largest 4-digit negative year formatted as 6-digit");
+
+instance = new Temporal.Instant(epochNsInYear(-1000n));
+assert.sameValue(instance.toJSON(), "-001000-07-02T09:30:36Z", "smallest 4-digit negative year formatted as 6-digit");
+
+instance = new Temporal.Instant(epochNsInYear(-999n));
+assert.sameValue(instance.toJSON(), "-000999-07-02T15:19:48Z", "largest 3-digit negative year formatted as 6-digit");
+
+instance = new Temporal.Instant(epochNsInYear(-1n));
+assert.sameValue(instance.toJSON(), "-000001-07-02T15:41:24Z", "year -1 formatted as 6-digit");
+
+instance = new Temporal.Instant(epochNsInYear(0n));
+assert.sameValue(instance.toJSON(), "0000-07-01T21:30:36Z", "year 0 formatted as 4-digit");
+
+instance = new Temporal.Instant(epochNsInYear(1n));
+assert.sameValue(instance.toJSON(), "0001-07-02T03:19:48Z", "year 1 formatted as 4-digit");
+
+instance = new Temporal.Instant(epochNsInYear(999n));
+assert.sameValue(instance.toJSON(), "0999-07-02T03:41:24Z", "largest 3-digit positive year formatted as 4-digit");
+
+instance = new Temporal.Instant(epochNsInYear(1000n));
+assert.sameValue(instance.toJSON(), "1000-07-02T09:30:36Z", "smallest 4-digit positive year formatted as 4-digit");
+
+instance = new Temporal.Instant(epochNsInYear(9999n));
+assert.sameValue(instance.toJSON(), "9999-07-02T15:41:24Z", "largest 4-digit positive year formatted as 4-digit");
+
+instance = new Temporal.Instant(epochNsInYear(10000n));
+assert.sameValue(instance.toJSON(), "+010000-07-01T21:30:36Z", "smallest 5-digit positive year formatted as 6-digit");
+
+instance = new Temporal.Instant(epochNsInYear(100000n));
+assert.sameValue(instance.toJSON(), "+100000-07-01T21:30:36Z", "large positive year formatted as 6-digit");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toLocaleString/branding.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toLocaleString/branding.js
new file mode 100644
index 0000000000..9fc9c8780a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toLocaleString/branding.js
@@ -0,0 +1,25 @@
+// |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.instant.prototype.tolocalestring
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toLocaleString = Temporal.Instant.prototype.toLocaleString;
+
+assert.sameValue(typeof toLocaleString, "function");
+
+assert.throws(TypeError, () => toLocaleString.call(undefined), "undefined");
+assert.throws(TypeError, () => toLocaleString.call(null), "null");
+assert.throws(TypeError, () => toLocaleString.call(true), "true");
+assert.throws(TypeError, () => toLocaleString.call(""), "empty string");
+assert.throws(TypeError, () => toLocaleString.call(Symbol()), "symbol");
+assert.throws(TypeError, () => toLocaleString.call(1), "1");
+assert.throws(TypeError, () => toLocaleString.call({}), "plain object");
+assert.throws(TypeError, () => toLocaleString.call(Temporal.Instant), "Temporal.Instant");
+assert.throws(TypeError, () => toLocaleString.call(Temporal.Instant.prototype), "Temporal.Instant.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toLocaleString/browser.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toLocaleString/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toLocaleString/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toLocaleString/builtin.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toLocaleString/builtin.js
new file mode 100644
index 0000000000..e9b4fa01ba
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toLocaleString/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.instant.prototype.tolocalestring
+description: >
+ Tests that Temporal.Instant.prototype.toLocaleString
+ 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.Instant.prototype.toLocaleString),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Instant.prototype.toLocaleString),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Instant.prototype.toLocaleString),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Instant.prototype.toLocaleString.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toLocaleString/length.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toLocaleString/length.js
new file mode 100644
index 0000000000..84660ce630
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toLocaleString/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.instant.prototype.tolocalestring
+description: Temporal.Instant.prototype.toLocaleString.length is 0
+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.Instant.prototype.toLocaleString, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toLocaleString/name.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toLocaleString/name.js
new file mode 100644
index 0000000000..36040a5e08
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toLocaleString/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.instant.prototype.tolocalestring
+description: Temporal.Instant.prototype.toLocaleString.name is "toLocaleString".
+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.Instant.prototype.toLocaleString, "name", {
+ value: "toLocaleString",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toLocaleString/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toLocaleString/not-a-constructor.js
new file mode 100644
index 0000000000..23a2bf2f64
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toLocaleString/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.instant.prototype.tolocalestring
+description: >
+ Temporal.Instant.prototype.toLocaleString 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.Instant.prototype.toLocaleString();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Instant.prototype.toLocaleString), false,
+ "isConstructor(Temporal.Instant.prototype.toLocaleString)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toLocaleString/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toLocaleString/prop-desc.js
new file mode 100644
index 0000000000..4846994d1c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toLocaleString/prop-desc.js
@@ -0,0 +1,24 @@
+// |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.instant.prototype.tolocalestring
+description: The "toLocaleString" property of Temporal.Instant.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Instant.prototype.toLocaleString,
+ "function",
+ "`typeof Instant.prototype.toLocaleString` is `function`"
+);
+
+verifyProperty(Temporal.Instant.prototype, "toLocaleString", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toLocaleString/return-string.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toLocaleString/return-string.js
new file mode 100644
index 0000000000..838c910df9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toLocaleString/return-string.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Kate Miháliková. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.tolocalestring
+description: >
+ toLocaleString return a string.
+features: [Temporal]
+---*/
+
+const instant = new Temporal.Instant(957270896_987_650_000n);
+
+assert.sameValue(typeof instant.toLocaleString("en", { dateStyle: "short" }), "string");
+assert.sameValue(typeof instant.toLocaleString("en", { timeStyle: "short" }), "string");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toLocaleString/shell.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toLocaleString/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toLocaleString/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/Instant/prototype/toString/basic.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/basic.js
new file mode 100644
index 0000000000..568c719c59
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/basic.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.instant.prototype.tostring
+description: Basic tests for toString().
+features: [BigInt, Temporal]
+---*/
+
+const afterEpoch = new Temporal.Instant(217175010_123_456_789n);
+assert.sameValue(afterEpoch.toString(), "1976-11-18T14:23:30.123456789Z", "basic toString() after epoch");
+
+const beforeEpoch = new Temporal.Instant(-217175010_876_543_211n);
+assert.sameValue(beforeEpoch.toString(), "1963-02-13T09:36:29.123456789Z", "basic toString() before epoch");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/branding.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/branding.js
new file mode 100644
index 0000000000..fa93d423be
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/branding.js
@@ -0,0 +1,25 @@
+// |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.instant.prototype.tostring
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toString = Temporal.Instant.prototype.toString;
+
+assert.sameValue(typeof toString, "function");
+
+assert.throws(TypeError, () => toString.call(undefined), "undefined");
+assert.throws(TypeError, () => toString.call(null), "null");
+assert.throws(TypeError, () => toString.call(true), "true");
+assert.throws(TypeError, () => toString.call(""), "empty string");
+assert.throws(TypeError, () => toString.call(Symbol()), "symbol");
+assert.throws(TypeError, () => toString.call(1), "1");
+assert.throws(TypeError, () => toString.call({}), "plain object");
+assert.throws(TypeError, () => toString.call(Temporal.Instant), "Temporal.Instant");
+assert.throws(TypeError, () => toString.call(Temporal.Instant.prototype), "Temporal.Instant.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/browser.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/builtin.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/builtin.js
new file mode 100644
index 0000000000..1b47257cdc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/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.instant.prototype.tostring
+description: >
+ Tests that Temporal.Instant.prototype.toString
+ 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.Instant.prototype.toString),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Instant.prototype.toString),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Instant.prototype.toString),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Instant.prototype.toString.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/fractionalseconddigits-auto.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/fractionalseconddigits-auto.js
new file mode 100644
index 0000000000..261c8917f8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/fractionalseconddigits-auto.js
@@ -0,0 +1,23 @@
+// |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.instant.prototype.tostring
+description: auto value for fractionalSecondDigits option
+features: [BigInt, Temporal]
+---*/
+
+const tests = [
+ [new Temporal.Instant(192_258_181_000_000_000n), "1976-02-04T05:03:01Z"],
+ [new Temporal.Instant(0n), "1970-01-01T00:00:00Z"],
+ [new Temporal.Instant(30_000_000_000n), "1970-01-01T00:00:30Z"],
+ [new Temporal.Instant(30_123_400_000n), "1970-01-01T00:00:30.1234Z"],
+];
+
+for (const [instant, expected] of tests) {
+ assert.sameValue(instant.toString(), expected, "default is to emit seconds and drop trailing zeroes");
+ assert.sameValue(instant.toString({ fractionalSecondDigits: "auto" }), expected, "auto is the default");
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/fractionalseconddigits-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/fractionalseconddigits-invalid-string.js
new file mode 100644
index 0000000000..86ffd1b51f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/fractionalseconddigits-invalid-string.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.instant.prototype.tostring
+description: RangeError thrown when fractionalSecondDigits option not one of the allowed string values
+info: |
+ sec-getstringornumberoption step 4:
+ 4. If _stringValues_ is not *undefined* and _stringValues_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-tosecondsstringprecision step 9:
+ 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
+ sec-temporal.instant.prototype.tostring step 6:
+ 6. Let _precision_ be ? ToSecondsStringPrecision(_options_).
+features: [Temporal]
+---*/
+
+const instant = new Temporal.Instant(1_000_000_000_987_650_000n);
+
+for (const fractionalSecondDigits of ["other string", "AUTO", "not-auto", "autos", "auto\0"]) {
+ assert.throws(RangeError, () => instant.toString({ fractionalSecondDigits }),
+ `"${fractionalSecondDigits}" is not a valid value for fractionalSecondDigits`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/fractionalseconddigits-nan.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/fractionalseconddigits-nan.js
new file mode 100644
index 0000000000..2c9071c6c1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/fractionalseconddigits-nan.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.instant.prototype.tostring
+description: RangeError thrown when fractionalSecondDigits option is NaN
+info: |
+ sec-getoption step 8.b:
+ b. If _value_ is *NaN*, throw a *RangeError* exception.
+ sec-getstringornumberoption step 2:
+ 2. Let _value_ be ? GetOption(_options_, _property_, « Number, String », *undefined*, _fallback_).
+ sec-temporal-tosecondsstringprecision step 9:
+ 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
+ sec-temporal.instant.prototype.tostring step 6:
+ 6. Let _precision_ be ? ToSecondsStringPrecision(_options_).
+features: [Temporal]
+---*/
+
+const instant = new Temporal.Instant(1_000_000_000_987_650_000n);
+assert.throws(RangeError, () => instant.toString({ fractionalSecondDigits: NaN }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/fractionalseconddigits-non-integer.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/fractionalseconddigits-non-integer.js
new file mode 100644
index 0000000000..52165df77f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/fractionalseconddigits-non-integer.js
@@ -0,0 +1,32 @@
+// |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.instant.prototype.tostring
+description: Rounding for fractionalSecondDigits option
+info: |
+ sec-getstringornumberoption step 3.b:
+ b. Return floor(ℝ(_value_)).
+ sec-temporal-tosecondsstringprecision step 9:
+ 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
+ sec-temporal.instant.prototype.tostring step 6:
+ 6. Let _precision_ be ? ToSecondsStringPrecision(_options_).
+features: [Temporal]
+---*/
+
+const instant = new Temporal.Instant(1_000_000_000_987_650_000n);
+
+let string = instant.toString({ fractionalSecondDigits: 2.5 });
+assert.sameValue(string, "2001-09-09T01:46:40.98Z", "fractionalSecondDigits 2.5 floors to 2");
+
+string = instant.toString({ fractionalSecondDigits: 9.7 });
+assert.sameValue(string, "2001-09-09T01:46:40.987650000Z", "fractionalSecondDigits 9.7 floors to 9 and is not out of range");
+
+assert.throws(
+ RangeError,
+ () => instant.toString({ fractionalSecondDigits: -0.6 }),
+ "fractionalSecondDigits -0.6 floors to -1 and is out of range"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/fractionalseconddigits-number.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/fractionalseconddigits-number.js
new file mode 100644
index 0000000000..f4ae2b444f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/fractionalseconddigits-number.js
@@ -0,0 +1,39 @@
+// |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.instant.prototype.tostring
+description: Number for fractionalSecondDigits option
+features: [BigInt, Temporal]
+---*/
+
+const fewSeconds = new Temporal.Instant(192_258_181_000_000_000n);
+const zeroSeconds = new Temporal.Instant(0n);
+const wholeSeconds = new Temporal.Instant(30_000_000_000n);
+const subSeconds = new Temporal.Instant(30_123_400_000n);
+
+assert.sameValue(fewSeconds.toString({ fractionalSecondDigits: 0 }), "1976-02-04T05:03:01Z",
+ "pads parts with 0");
+assert.sameValue(subSeconds.toString({ fractionalSecondDigits: 0 }), "1970-01-01T00:00:30Z",
+ "truncates 4 decimal places to 0");
+assert.sameValue(zeroSeconds.toString({ fractionalSecondDigits: 2 }), "1970-01-01T00:00:00.00Z",
+ "pads zero seconds to 2 decimal places");
+assert.sameValue(wholeSeconds.toString({ fractionalSecondDigits: 2 }), "1970-01-01T00:00:30.00Z",
+ "pads whole seconds to 2 decimal places");
+assert.sameValue(subSeconds.toString({ fractionalSecondDigits: 2 }), "1970-01-01T00:00:30.12Z",
+ "truncates 4 decimal places to 2");
+assert.sameValue(subSeconds.toString({ fractionalSecondDigits: 3 }), "1970-01-01T00:00:30.123Z",
+ "truncates 4 decimal places to 3");
+assert.sameValue(subSeconds.toString({ fractionalSecondDigits: 6 }), "1970-01-01T00:00:30.123400Z",
+ "pads 4 decimal places to 6");
+assert.sameValue(zeroSeconds.toString({ fractionalSecondDigits: 7 }), "1970-01-01T00:00:00.0000000Z",
+ "pads zero seconds to 7 decimal places");
+assert.sameValue(wholeSeconds.toString({ fractionalSecondDigits: 7 }), "1970-01-01T00:00:30.0000000Z",
+ "pads whole seconds to 7 decimal places");
+assert.sameValue(subSeconds.toString({ fractionalSecondDigits: 7 }), "1970-01-01T00:00:30.1234000Z",
+ "pads 4 decimal places to 7");
+assert.sameValue(subSeconds.toString({ fractionalSecondDigits: 9 }), "1970-01-01T00:00:30.123400000Z",
+ "pads 4 decimal places to 9");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/fractionalseconddigits-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/fractionalseconddigits-out-of-range.js
new file mode 100644
index 0000000000..729b5415ce
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/fractionalseconddigits-out-of-range.js
@@ -0,0 +1,29 @@
+// |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.instant.prototype.tostring
+description: RangeError thrown when fractionalSecondDigits option out of range
+info: |
+ sec-getstringornumberoption step 3.a:
+ a. If _value_ < _minimum_ or _value_ > _maximum_, throw a *RangeError* exception.
+ sec-temporal-tosecondsstringprecision step 9:
+ 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
+ sec-temporal.instant.prototype.tostring step 6:
+ 6. Let _precision_ be ? ToSecondsStringPrecision(_options_).
+features: [Temporal]
+---*/
+
+const instant = new Temporal.Instant(1_000_000_000_987_650_000n);
+
+assert.throws(RangeError, () => instant.toString({ fractionalSecondDigits: -Infinity }),
+ "−∞ is out of range for fractionalSecondDigits");
+assert.throws(RangeError, () => instant.toString({ fractionalSecondDigits: -1 }),
+ "−1 is out of range for fractionalSecondDigits");
+assert.throws(RangeError, () => instant.toString({ fractionalSecondDigits: 10 }),
+ "10 is out of range for fractionalSecondDigits");
+assert.throws(RangeError, () => instant.toString({ fractionalSecondDigits: Infinity }),
+ "∞ is out of range for fractionalSecondDigits");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/fractionalseconddigits-undefined.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/fractionalseconddigits-undefined.js
new file mode 100644
index 0000000000..0e8e27aff5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/fractionalseconddigits-undefined.js
@@ -0,0 +1,38 @@
+// |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.instant.prototype.tostring
+description: Fallback value for fractionalSecondDigits option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-getstringornumberoption step 2:
+ 2. Let _value_ be ? GetOption(_options_, _property_, « Number, String », *undefined*, _fallback_).
+ sec-temporal-tosecondsstringprecision step 9:
+ 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
+ sec-temporal.instant.prototype.tostring step 6:
+ 6. Let _precision_ be ? ToSecondsStringPrecision(_options_).
+features: [Temporal]
+---*/
+
+const tests = [
+ [new Temporal.Instant(192_258_181_000_000_000n), "1976-02-04T05:03:01Z"],
+ [new Temporal.Instant(0n), "1970-01-01T00:00:00Z"],
+ [new Temporal.Instant(30_000_000_000n), "1970-01-01T00:00:30Z"],
+ [new Temporal.Instant(30_123_400_000n), "1970-01-01T00:00:30.1234Z"],
+];
+
+for (const [instant, expected] of tests) {
+ const explicit = instant.toString({ fractionalSecondDigits: undefined });
+ assert.sameValue(explicit, expected, "default fractionalSecondDigits is auto (property present but undefined)");
+
+ const implicit = instant.toString({});
+ assert.sameValue(implicit, expected, "default fractionalSecondDigits is auto (property not present)");
+
+ const lambda = instant.toString(() => {});
+ assert.sameValue(lambda, expected, "default fractionalSecondDigits is auto (property not present, function object)");
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/fractionalseconddigits-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/fractionalseconddigits-wrong-type.js
new file mode 100644
index 0000000000..58e24b1df1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/fractionalseconddigits-wrong-type.js
@@ -0,0 +1,50 @@
+// |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.instant.prototype.tostring
+description: Type conversions for fractionalSecondDigits option
+info: |
+ sec-getoption steps 8–9:
+ 8. Else if _type_ is Number, then
+ a. Set _value_ to ? ToNumber(value).
+ b. ...
+ 9. Else,
+ a. Set _value_ to ? ToString(value).
+ sec-getstringornumberoption step 2:
+ 2. Let _value_ be ? GetOption(_options_, _property_, « Number, String », *undefined*, _fallback_).
+ sec-temporal-tosecondsstringprecision step 9:
+ 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
+ sec-temporal.instant.prototype.tostring step 6:
+ 6. Let _precision_ be ? ToSecondsStringPrecision(_options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instant = new Temporal.Instant(1_000_000_000_987_650_000n);
+
+assert.throws(RangeError, () => instant.toString({ fractionalSecondDigits: null }),
+ "null is not a number and converts to the string 'null' which is not valid for fractionalSecondDigits");
+assert.throws(RangeError, () => instant.toString({ fractionalSecondDigits: true }),
+ "true is not a number and converts to the string 'true' which is not valid for fractionalSecondDigits");
+assert.throws(RangeError, () => instant.toString({ fractionalSecondDigits: false }),
+ "false is not a number and converts to the string 'false' which is not valid for fractionalSecondDigits");
+assert.throws(TypeError, () => instant.toString({ fractionalSecondDigits: Symbol() }),
+ "symbols are not numbers and cannot convert to strings");
+assert.throws(RangeError, () => instant.toString({ fractionalSecondDigits: 2n }),
+ "bigints are not numbers and convert to strings which are not valid for fractionalSecondDigits");
+assert.throws(RangeError, () => instant.toString({ fractionalSecondDigits: {} }),
+ "plain objects are not numbers and convert to strings which are not valid for fractionalSecondDigits");
+
+const expected = [
+ "get fractionalSecondDigits.toString",
+ "call fractionalSecondDigits.toString",
+];
+const actual = [];
+const observer = TemporalHelpers.toPrimitiveObserver(actual, "auto", "fractionalSecondDigits");
+const result = instant.toString({ fractionalSecondDigits: observer });
+assert.sameValue(result, "2001-09-09T01:46:40.98765Z", "object with toString uses toString return value");
+assert.compareArray(actual, expected, "object with toString calls toString and not valueOf");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/length.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/length.js
new file mode 100644
index 0000000000..f5c53a1c0f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/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.instant.prototype.tostring
+description: Temporal.Instant.prototype.toString.length is 0
+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.Instant.prototype.toString, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/name.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/name.js
new file mode 100644
index 0000000000..7b57bf6856
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/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.instant.prototype.tostring
+description: Temporal.Instant.prototype.toString.name is "toString".
+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.Instant.prototype.toString, "name", {
+ value: "toString",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/negative-epochnanoseconds.js
new file mode 100644
index 0000000000..90f108ac90
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/negative-epochnanoseconds.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.instant.prototype.tostring
+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]
+---*/
+
+const datetime = new Temporal.Instant(-13849764_999_999_999n);
+
+// 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 result = datetime.toString();
+assert.sameValue(result, "1969-07-24T16:50:35.000000001Z");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/not-a-constructor.js
new file mode 100644
index 0000000000..288a59b02b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/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.instant.prototype.tostring
+description: >
+ Temporal.Instant.prototype.toString 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.Instant.prototype.toString();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Instant.prototype.toString), false,
+ "isConstructor(Temporal.Instant.prototype.toString)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/options-object.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/options-object.js
new file mode 100644
index 0000000000..a61c93d76c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/options-object.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.instant.prototype.tostring
+description: Empty or a function object may be used as options
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(0n);
+
+const result1 = instance.toString({});
+assert.sameValue(
+ result1, "1970-01-01T00:00:00Z",
+ "options may be an empty plain object"
+);
+
+const result2 = instance.toString(() => {});
+assert.sameValue(
+ result2, "1970-01-01T00:00:00Z",
+ "options may be a function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/options-undefined.js
new file mode 100644
index 0000000000..67a7fc5553
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/options-undefined.js
@@ -0,0 +1,41 @@
+// |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.instant.prototype.tostring
+includes: [compareArray.js]
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const actual = [];
+const expected = [];
+
+const instant = Temporal.Instant.from("1975-02-02T14:25:36.12345Z");
+
+Object.defineProperty(Temporal.TimeZone, "from", {
+ get() {
+ actual.push("get Temporal.TimeZone.from");
+ return function(identifier) {
+ actual.push("call Temporal.TimeZone.from");
+ assert.sameValue(identifier, "UTC");
+ };
+ },
+});
+
+assert.sameValue(
+ instant.toString(),
+ "1975-02-02T14:25:36.12345Z",
+ "default time zone is none, precision is auto, and rounding is trunc"
+);
+assert.compareArray(actual, expected);
+
+assert.sameValue(
+ instant.toString(undefined),
+ "1975-02-02T14:25:36.12345Z",
+ "default time zone is none, precision is auto, and rounding is trunc"
+);
+assert.compareArray(actual, expected);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/options-wrong-type.js
new file mode 100644
index 0000000000..3ebc6ff874
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/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.instant.prototype.tostring
+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.Instant(0n);
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.toString(value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/order-of-operations.js
new file mode 100644
index 0000000000..3888c62410
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/order-of-operations.js
@@ -0,0 +1,65 @@
+// |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.instant.prototype.tostring
+description: Properties on objects passed to toString() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "get options.fractionalSecondDigits",
+ "get options.fractionalSecondDigits.toString",
+ "call options.fractionalSecondDigits.toString",
+ "get options.roundingMode",
+ "get options.roundingMode.toString",
+ "call options.roundingMode.toString",
+ "get options.smallestUnit",
+ "get options.smallestUnit.toString",
+ "call options.smallestUnit.toString",
+ "get options.timeZone",
+ "has options.timeZone.getOffsetNanosecondsFor",
+ "has options.timeZone.getPossibleInstantsFor",
+ "has options.timeZone.id",
+ "get options.timeZone.getOffsetNanosecondsFor",
+ "call options.timeZone.getOffsetNanosecondsFor",
+];
+const actual = [];
+
+const instance = new Temporal.Instant(0n);
+
+instance.toString(
+ TemporalHelpers.propertyBagObserver(actual, {
+ fractionalSecondDigits: "auto",
+ roundingMode: "halfExpand",
+ smallestUnit: "millisecond",
+ timeZone: TemporalHelpers.timeZoneObserver(actual, "options.timeZone"),
+ }, "options"),
+);
+assert.compareArray(actual, expected, "order of operations");
+actual.splice(0); // clear
+
+const expectedForFractionalSecondDigits = [
+ "get options.fractionalSecondDigits",
+ "get options.fractionalSecondDigits.toString",
+ "call options.fractionalSecondDigits.toString",
+ "get options.roundingMode",
+ "get options.roundingMode.toString",
+ "call options.roundingMode.toString",
+ "get options.smallestUnit",
+ "get options.timeZone",
+];
+
+instance.toString(
+ TemporalHelpers.propertyBagObserver(actual, {
+ fractionalSecondDigits: "auto",
+ roundingMode: "halfExpand",
+ smallestUnit: undefined,
+ timeZone: undefined,
+ }, "options"),
+);
+assert.compareArray(actual, expectedForFractionalSecondDigits, "order of operations with smallestUnit undefined");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/precision.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/precision.js
new file mode 100644
index 0000000000..a0a6ae9aa1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/precision.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.instant.prototype.tostring
+description: toString() produces a fractional part of the correct length
+features: [Temporal]
+---*/
+
+const { Instant } = Temporal;
+
+const isoString = '2020-01-01T23:58:57.012034Z';
+const instant = Instant.from(isoString);
+const instantIsoStrMicros = instant.toString({
+ smallestUnit: 'microseconds'
+});
+
+assert.sameValue(instantIsoStrMicros, isoString);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/prop-desc.js
new file mode 100644
index 0000000000..feba7e1cc5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/prop-desc.js
@@ -0,0 +1,24 @@
+// |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.instant.prototype.tostring
+description: The "toString" property of Temporal.Instant.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Instant.prototype.toString,
+ "function",
+ "`typeof Instant.prototype.toString` is `function`"
+);
+
+verifyProperty(Temporal.Instant.prototype, "toString", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/rounding-cross-midnight.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/rounding-cross-midnight.js
new file mode 100644
index 0000000000..045c94dda6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/rounding-cross-midnight.js
@@ -0,0 +1,16 @@
+// |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.instant.prototype.tostring
+description: Rounding can cross midnight
+features: [Temporal]
+---*/
+
+const instant = new Temporal.Instant(946_684_799_999_999_999n); // one nanosecond before 2000-01-01T00:00:00
+for (const roundingMode of ["ceil", "halfExpand"]) {
+ assert.sameValue(instant.toString({ fractionalSecondDigits: 8, roundingMode }), "2000-01-01T00:00:00.00000000Z");
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/rounding-direction.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/rounding-direction.js
new file mode 100644
index 0000000000..dff8acca02
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/rounding-direction.js
@@ -0,0 +1,33 @@
+// |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.instant.prototype.tostring
+description: Rounding down is towards the Big Bang, not the epoch or 1 BCE
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(-65_261_246_399_500_000_000n); // -000099-12-15T12:00:00.5Z
+assert.sameValue(
+ instance.toString({ smallestUnit: "second", roundingMode: "floor" }),
+ "-000099-12-15T12:00:00Z",
+ "Rounding down is towards the Big Bang, not the epoch or 1 BCE"
+);
+assert.sameValue(
+ instance.toString({ smallestUnit: "second", roundingMode: "trunc" }),
+ "-000099-12-15T12:00:00Z",
+ "Rounding down is towards the Big Bang, not the epoch or 1 BCE (roundingMode trunc)"
+);
+assert.sameValue(
+ instance.toString({ smallestUnit: "second", roundingMode: "ceil" }),
+ "-000099-12-15T12:00:01Z",
+ "Rounding up is away from the Big Bang, not the epoch or 1 BCE (roundingMode ceil)"
+);
+assert.sameValue(
+ instance.toString({ smallestUnit: "second", roundingMode: "halfExpand" }),
+ "-000099-12-15T12:00:01Z",
+ "Rounding up is away from the Big Bang, not the epoch or 1 BCE (roundingMode halfExpand)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/roundingmode-ceil.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/roundingmode-ceil.js
new file mode 100644
index 0000000000..ddfd1d018a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/roundingmode-ceil.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.instant.prototype.tostring
+description: ceil value for roundingMode option
+features: [Temporal]
+---*/
+
+const instant = new Temporal.Instant(1_000_000_000_123_987_500n);
+
+const result1 = instant.toString({ smallestUnit: "microsecond", roundingMode: "ceil" });
+assert.sameValue(result1, "2001-09-09T01:46:40.123988Z",
+ "roundingMode is ceil (with 6 digits from smallestUnit)");
+
+const result2 = instant.toString({ fractionalSecondDigits: 6, roundingMode: "ceil" });
+assert.sameValue(result2, "2001-09-09T01:46:40.123988Z",
+ "roundingMode is ceil (with 6 digits from fractionalSecondDigits)");
+
+const result3 = instant.toString({ smallestUnit: "millisecond", roundingMode: "ceil" });
+assert.sameValue(result3, "2001-09-09T01:46:40.124Z",
+ "roundingMode is ceil (with 3 digits from smallestUnit)");
+
+const result4 = instant.toString({ fractionalSecondDigits: 3, roundingMode: "ceil" });
+assert.sameValue(result4, "2001-09-09T01:46:40.124Z",
+ "roundingMode is ceil (with 3 digits from fractionalSecondDigits)");
+
+const result5 = instant.toString({ smallestUnit: "second", roundingMode: "ceil" });
+assert.sameValue(result5, "2001-09-09T01:46:41Z",
+ "roundingMode is ceil (with 0 digits from smallestUnit)");
+
+const result6 = instant.toString({ fractionalSecondDigits: 0, roundingMode: "ceil" });
+assert.sameValue(result6, "2001-09-09T01:46:41Z",
+ "roundingMode is ceil (with 0 digits from fractionalSecondDigits)");
+
+const result7 = instant.toString({ smallestUnit: "minute", roundingMode: "ceil" });
+assert.sameValue(result7, "2001-09-09T01:47Z", "roundingMode is ceil (round to minute)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/roundingmode-expand.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/roundingmode-expand.js
new file mode 100644
index 0000000000..dfc7f6335a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/roundingmode-expand.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.instant.prototype.tostring
+description: expand value for roundingMode option
+features: [Temporal]
+---*/
+
+const instant = new Temporal.Instant(1_000_000_000_123_987_500n);
+
+const result1 = instant.toString({ smallestUnit: "microsecond", roundingMode: "expand" });
+assert.sameValue(result1, "2001-09-09T01:46:40.123988Z",
+ "roundingMode is expand (with 6 digits from smallestUnit)");
+
+const result2 = instant.toString({ fractionalSecondDigits: 6, roundingMode: "expand" });
+assert.sameValue(result2, "2001-09-09T01:46:40.123988Z",
+ "roundingMode is expand (with 6 digits from fractionalSecondDigits)");
+
+const result3 = instant.toString({ smallestUnit: "millisecond", roundingMode: "expand" });
+assert.sameValue(result3, "2001-09-09T01:46:40.124Z",
+ "roundingMode is expand (with 3 digits from smallestUnit)");
+
+const result4 = instant.toString({ fractionalSecondDigits: 3, roundingMode: "expand" });
+assert.sameValue(result4, "2001-09-09T01:46:40.124Z",
+ "roundingMode is expand (with 3 digits from fractionalSecondDigits)");
+
+const result5 = instant.toString({ smallestUnit: "second", roundingMode: "expand" });
+assert.sameValue(result5, "2001-09-09T01:46:41Z",
+ "roundingMode is expand (with 0 digits from smallestUnit)");
+
+const result6 = instant.toString({ fractionalSecondDigits: 0, roundingMode: "expand" });
+assert.sameValue(result6, "2001-09-09T01:46:41Z",
+ "roundingMode is expand (with 0 digits from fractionalSecondDigits)");
+
+const result7 = instant.toString({ smallestUnit: "minute", roundingMode: "expand" });
+assert.sameValue(result7, "2001-09-09T01:47Z", "roundingMode is expand (round to minute)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/roundingmode-floor.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/roundingmode-floor.js
new file mode 100644
index 0000000000..277d8f7dec
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/roundingmode-floor.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.instant.prototype.tostring
+description: floor value for roundingMode option
+features: [Temporal]
+---*/
+
+const instant = new Temporal.Instant(1_000_000_000_123_987_500n);
+
+const result1 = instant.toString({ smallestUnit: "microsecond", roundingMode: "floor" });
+assert.sameValue(result1, "2001-09-09T01:46:40.123987Z",
+ "roundingMode is floor (with 6 digits from smallestUnit)");
+
+const result2 = instant.toString({ fractionalSecondDigits: 6, roundingMode: "floor" });
+assert.sameValue(result2, "2001-09-09T01:46:40.123987Z",
+ "roundingMode is floor (with 6 digits from fractionalSecondDigits)");
+
+const result3 = instant.toString({ smallestUnit: "millisecond", roundingMode: "floor" });
+assert.sameValue(result3, "2001-09-09T01:46:40.123Z",
+ "roundingMode is floor (with 3 digits from smallestUnit)");
+
+const result4 = instant.toString({ fractionalSecondDigits: 3, roundingMode: "floor" });
+assert.sameValue(result4, "2001-09-09T01:46:40.123Z",
+ "roundingMode is floor (with 3 digits from fractionalSecondDigits)");
+
+const result5 = instant.toString({ smallestUnit: "second", roundingMode: "floor" });
+assert.sameValue(result5, "2001-09-09T01:46:40Z",
+ "roundingMode is floor (with 0 digits from smallestUnit)");
+
+const result6 = instant.toString({ fractionalSecondDigits: 0, roundingMode: "floor" });
+assert.sameValue(result6, "2001-09-09T01:46:40Z",
+ "roundingMode is floor (with 0 digits from fractionalSecondDigits)");
+
+const result7 = instant.toString({ smallestUnit: "minute", roundingMode: "floor" });
+assert.sameValue(result7, "2001-09-09T01:46Z", "roundingMode is floor (round to minute)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/roundingmode-halfCeil.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/roundingmode-halfCeil.js
new file mode 100644
index 0000000000..9e88af2ed8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/roundingmode-halfCeil.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.instant.prototype.tostring
+description: halfCeil value for roundingMode option
+features: [Temporal]
+---*/
+
+const instant = new Temporal.Instant(1_000_000_000_123_987_500n);
+
+const result1 = instant.toString({ smallestUnit: "microsecond", roundingMode: "halfCeil" });
+assert.sameValue(result1, "2001-09-09T01:46:40.123988Z",
+ "roundingMode is halfCeil (with 6 digits from smallestUnit)");
+
+const result2 = instant.toString({ fractionalSecondDigits: 6, roundingMode: "halfCeil" });
+assert.sameValue(result2, "2001-09-09T01:46:40.123988Z",
+ "roundingMode is halfCeil (with 6 digits from fractionalSecondDigits)");
+
+const result3 = instant.toString({ smallestUnit: "millisecond", roundingMode: "halfCeil" });
+assert.sameValue(result3, "2001-09-09T01:46:40.124Z",
+ "roundingMode is halfCeil (with 3 digits from smallestUnit)");
+
+const result4 = instant.toString({ fractionalSecondDigits: 3, roundingMode: "halfCeil" });
+assert.sameValue(result4, "2001-09-09T01:46:40.124Z",
+ "roundingMode is halfCeil (with 3 digits from fractionalSecondDigits)");
+
+const result5 = instant.toString({ smallestUnit: "second", roundingMode: "halfCeil" });
+assert.sameValue(result5, "2001-09-09T01:46:40Z",
+ "roundingMode is halfCeil (with 0 digits from smallestUnit)");
+
+const result6 = instant.toString({ fractionalSecondDigits: 0, roundingMode: "halfCeil" });
+assert.sameValue(result6, "2001-09-09T01:46:40Z",
+ "roundingMode is halfCeil (with 0 digits from fractionalSecondDigits)");
+
+const result7 = instant.toString({ smallestUnit: "minute", roundingMode: "halfCeil" });
+assert.sameValue(result7, "2001-09-09T01:47Z", "roundingMode is halfCeil (round to minute)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/roundingmode-halfEven.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/roundingmode-halfEven.js
new file mode 100644
index 0000000000..26f0653ace
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/roundingmode-halfEven.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.instant.prototype.tostring
+description: halfEven value for roundingMode option
+features: [Temporal]
+---*/
+
+const instant = new Temporal.Instant(1_000_000_000_123_987_500n);
+
+const result1 = instant.toString({ smallestUnit: "microsecond", roundingMode: "halfEven" });
+assert.sameValue(result1, "2001-09-09T01:46:40.123988Z",
+ "roundingMode is halfEven (with 6 digits from smallestUnit)");
+
+const result2 = instant.toString({ fractionalSecondDigits: 6, roundingMode: "halfEven" });
+assert.sameValue(result2, "2001-09-09T01:46:40.123988Z",
+ "roundingMode is halfEven (with 6 digits from fractionalSecondDigits)");
+
+const result3 = instant.toString({ smallestUnit: "millisecond", roundingMode: "halfEven" });
+assert.sameValue(result3, "2001-09-09T01:46:40.124Z",
+ "roundingMode is halfEven (with 3 digits from smallestUnit)");
+
+const result4 = instant.toString({ fractionalSecondDigits: 3, roundingMode: "halfEven" });
+assert.sameValue(result4, "2001-09-09T01:46:40.124Z",
+ "roundingMode is halfEven (with 3 digits from fractionalSecondDigits)");
+
+const result5 = instant.toString({ smallestUnit: "second", roundingMode: "halfEven" });
+assert.sameValue(result5, "2001-09-09T01:46:40Z",
+ "roundingMode is halfEven (with 0 digits from smallestUnit)");
+
+const result6 = instant.toString({ fractionalSecondDigits: 0, roundingMode: "halfEven" });
+assert.sameValue(result6, "2001-09-09T01:46:40Z",
+ "roundingMode is halfEven (with 0 digits from fractionalSecondDigits)");
+
+const result7 = instant.toString({ smallestUnit: "minute", roundingMode: "halfEven" });
+assert.sameValue(result7, "2001-09-09T01:47Z", "roundingMode is halfEven (round to minute)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/roundingmode-halfExpand.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/roundingmode-halfExpand.js
new file mode 100644
index 0000000000..0f58fc152d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/roundingmode-halfExpand.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.instant.prototype.tostring
+description: halfExpand value for roundingMode option
+features: [Temporal]
+---*/
+
+const instant = new Temporal.Instant(1_000_000_000_123_987_500n);
+
+const result1 = instant.toString({ smallestUnit: "microsecond", roundingMode: "halfExpand" });
+assert.sameValue(result1, "2001-09-09T01:46:40.123988Z",
+ "roundingMode is halfExpand (with 6 digits from smallestUnit)");
+
+const result2 = instant.toString({ fractionalSecondDigits: 6, roundingMode: "halfExpand" });
+assert.sameValue(result2, "2001-09-09T01:46:40.123988Z",
+ "roundingMode is halfExpand (with 6 digits from fractionalSecondDigits)");
+
+const result3 = instant.toString({ smallestUnit: "millisecond", roundingMode: "halfExpand" });
+assert.sameValue(result3, "2001-09-09T01:46:40.124Z",
+ "roundingMode is halfExpand (with 3 digits from smallestUnit)");
+
+const result4 = instant.toString({ fractionalSecondDigits: 3, roundingMode: "halfExpand" });
+assert.sameValue(result4, "2001-09-09T01:46:40.124Z",
+ "roundingMode is halfExpand (with 3 digits from fractionalSecondDigits)");
+
+const result5 = instant.toString({ smallestUnit: "second", roundingMode: "halfExpand" });
+assert.sameValue(result5, "2001-09-09T01:46:40Z",
+ "roundingMode is halfExpand (with 0 digits from smallestUnit)");
+
+const result6 = instant.toString({ fractionalSecondDigits: 0, roundingMode: "halfExpand" });
+assert.sameValue(result6, "2001-09-09T01:46:40Z",
+ "roundingMode is halfExpand (with 0 digits from fractionalSecondDigits)");
+
+const result7 = instant.toString({ smallestUnit: "minute", roundingMode: "halfExpand" });
+assert.sameValue(result7, "2001-09-09T01:47Z", "roundingMode is halfExpand (round to minute)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/roundingmode-halfFloor.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/roundingmode-halfFloor.js
new file mode 100644
index 0000000000..8152795b17
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/roundingmode-halfFloor.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.instant.prototype.tostring
+description: halfFloor value for roundingMode option
+features: [Temporal]
+---*/
+
+const instant = new Temporal.Instant(1_000_000_000_123_987_500n);
+
+const result1 = instant.toString({ smallestUnit: "microsecond", roundingMode: "halfFloor" });
+assert.sameValue(result1, "2001-09-09T01:46:40.123987Z",
+ "roundingMode is halfFloor (with 6 digits from smallestUnit)");
+
+const result2 = instant.toString({ fractionalSecondDigits: 6, roundingMode: "halfFloor" });
+assert.sameValue(result2, "2001-09-09T01:46:40.123987Z",
+ "roundingMode is halfFloor (with 6 digits from fractionalSecondDigits)");
+
+const result3 = instant.toString({ smallestUnit: "millisecond", roundingMode: "halfFloor" });
+assert.sameValue(result3, "2001-09-09T01:46:40.124Z",
+ "roundingMode is halfFloor (with 3 digits from smallestUnit)");
+
+const result4 = instant.toString({ fractionalSecondDigits: 3, roundingMode: "halfFloor" });
+assert.sameValue(result4, "2001-09-09T01:46:40.124Z",
+ "roundingMode is halfFloor (with 3 digits from fractionalSecondDigits)");
+
+const result5 = instant.toString({ smallestUnit: "second", roundingMode: "halfFloor" });
+assert.sameValue(result5, "2001-09-09T01:46:40Z",
+ "roundingMode is halfFloor (with 0 digits from smallestUnit)");
+
+const result6 = instant.toString({ fractionalSecondDigits: 0, roundingMode: "halfFloor" });
+assert.sameValue(result6, "2001-09-09T01:46:40Z",
+ "roundingMode is halfFloor (with 0 digits from fractionalSecondDigits)");
+
+const result7 = instant.toString({ smallestUnit: "minute", roundingMode: "halfFloor" });
+assert.sameValue(result7, "2001-09-09T01:47Z", "roundingMode is halfFloor (round to minute)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/roundingmode-halfTrunc.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/roundingmode-halfTrunc.js
new file mode 100644
index 0000000000..e4b49cacfd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/roundingmode-halfTrunc.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.instant.prototype.tostring
+description: halfTrunc value for roundingMode option
+features: [Temporal]
+---*/
+
+const instant = new Temporal.Instant(1_000_000_000_123_987_500n);
+
+const result1 = instant.toString({ smallestUnit: "microsecond", roundingMode: "halfTrunc" });
+assert.sameValue(result1, "2001-09-09T01:46:40.123987Z",
+ "roundingMode is halfTrunc (with 6 digits from smallestUnit)");
+
+const result2 = instant.toString({ fractionalSecondDigits: 6, roundingMode: "halfTrunc" });
+assert.sameValue(result2, "2001-09-09T01:46:40.123987Z",
+ "roundingMode is halfTrunc (with 6 digits from fractionalSecondDigits)");
+
+const result3 = instant.toString({ smallestUnit: "millisecond", roundingMode: "halfTrunc" });
+assert.sameValue(result3, "2001-09-09T01:46:40.124Z",
+ "roundingMode is halfTrunc (with 3 digits from smallestUnit)");
+
+const result4 = instant.toString({ fractionalSecondDigits: 3, roundingMode: "halfTrunc" });
+assert.sameValue(result4, "2001-09-09T01:46:40.124Z",
+ "roundingMode is halfTrunc (with 3 digits from fractionalSecondDigits)");
+
+const result5 = instant.toString({ smallestUnit: "second", roundingMode: "halfTrunc" });
+assert.sameValue(result5, "2001-09-09T01:46:40Z",
+ "roundingMode is halfTrunc (with 0 digits from smallestUnit)");
+
+const result6 = instant.toString({ fractionalSecondDigits: 0, roundingMode: "halfTrunc" });
+assert.sameValue(result6, "2001-09-09T01:46:40Z",
+ "roundingMode is halfTrunc (with 0 digits from fractionalSecondDigits)");
+
+const result7 = instant.toString({ smallestUnit: "minute", roundingMode: "halfTrunc" });
+assert.sameValue(result7, "2001-09-09T01:47Z", "roundingMode is halfTrunc (round to minute)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/roundingmode-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/roundingmode-invalid-string.js
new file mode 100644
index 0000000000..37e3e4ce39
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/roundingmode-invalid-string.js
@@ -0,0 +1,16 @@
+// |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.instant.prototype.tostring
+description: RangeError thrown when roundingMode option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const instant = new Temporal.Instant(1_000_000_000_123_987_500n);
+for (const roundingMode of ["other string", "cile", "CEIL", "ce\u0131l", "auto", "halfexpand", "floor\0"]) {
+ assert.throws(RangeError, () => instant.toString({ smallestUnit: "microsecond", roundingMode }));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/roundingmode-trunc.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/roundingmode-trunc.js
new file mode 100644
index 0000000000..f5bc0b8489
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/roundingmode-trunc.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.instant.prototype.tostring
+description: trunc value for roundingMode option
+features: [Temporal]
+---*/
+
+const instant = new Temporal.Instant(1_000_000_000_123_987_500n);
+
+const result1 = instant.toString({ smallestUnit: "microsecond", roundingMode: "trunc" });
+assert.sameValue(result1, "2001-09-09T01:46:40.123987Z",
+ "roundingMode is trunc (with 6 digits from smallestUnit)");
+
+const result2 = instant.toString({ fractionalSecondDigits: 6, roundingMode: "trunc" });
+assert.sameValue(result2, "2001-09-09T01:46:40.123987Z",
+ "roundingMode is trunc (with 6 digits from fractionalSecondDigits)");
+
+const result3 = instant.toString({ smallestUnit: "millisecond", roundingMode: "trunc" });
+assert.sameValue(result3, "2001-09-09T01:46:40.123Z",
+ "roundingMode is trunc (with 3 digits from smallestUnit)");
+
+const result4 = instant.toString({ fractionalSecondDigits: 3, roundingMode: "trunc" });
+assert.sameValue(result4, "2001-09-09T01:46:40.123Z",
+ "roundingMode is trunc (with 3 digits from fractionalSecondDigits)");
+
+const result5 = instant.toString({ smallestUnit: "second", roundingMode: "trunc" });
+assert.sameValue(result5, "2001-09-09T01:46:40Z",
+ "roundingMode is trunc (with 0 digits from smallestUnit)");
+
+const result6 = instant.toString({ fractionalSecondDigits: 0, roundingMode: "trunc" });
+assert.sameValue(result6, "2001-09-09T01:46:40Z",
+ "roundingMode is trunc (with 0 digits from fractionalSecondDigits)");
+
+const result7 = instant.toString({ smallestUnit: "minute", roundingMode: "trunc" });
+assert.sameValue(result7, "2001-09-09T01:46Z", "roundingMode is trunc (round to minute)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/roundingmode-undefined.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/roundingmode-undefined.js
new file mode 100644
index 0000000000..f2a1fb3fd1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/roundingmode-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.instant.prototype.tostring
+description: Fallback value for roundingMode option
+features: [Temporal]
+---*/
+
+const instant = new Temporal.Instant(1_000_000_000_123_987_500n);
+
+const explicit1 = instant.toString({ smallestUnit: "microsecond", roundingMode: undefined });
+assert.sameValue(explicit1, "2001-09-09T01:46:40.123987Z", "default roundingMode is trunc");
+const implicit1 = instant.toString({ smallestUnit: "microsecond" });
+assert.sameValue(implicit1, "2001-09-09T01:46:40.123987Z", "default roundingMode is trunc");
+
+const explicit2 = instant.toString({ smallestUnit: "millisecond", roundingMode: undefined });
+assert.sameValue(explicit2, "2001-09-09T01:46:40.123Z", "default roundingMode is trunc");
+const implicit2 = instant.toString({ smallestUnit: "millisecond" });
+assert.sameValue(implicit2, "2001-09-09T01:46:40.123Z", "default roundingMode is trunc");
+
+const explicit3 = instant.toString({ smallestUnit: "second", roundingMode: undefined });
+assert.sameValue(explicit3, "2001-09-09T01:46:40Z", "default roundingMode is trunc");
+const implicit3 = instant.toString({ smallestUnit: "second" });
+assert.sameValue(implicit3, "2001-09-09T01:46:40Z", "default roundingMode is trunc");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/roundingmode-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/roundingmode-wrong-type.js
new file mode 100644
index 0000000000..4a0fa94cd6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/roundingmode-wrong-type.js
@@ -0,0 +1,18 @@
+// |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.instant.prototype.tostring
+description: Type conversions for roundingMode option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instant = new Temporal.Instant(1_000_000_000_123_987_500n);
+TemporalHelpers.checkStringOptionWrongType("roundingMode", "trunc",
+ (roundingMode) => instant.toString({ smallestUnit: "microsecond", roundingMode }),
+ (result, descr) => assert.sameValue(result, "2001-09-09T01:46:40.123987Z", descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/shell.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/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/Instant/prototype/toString/smallestunit-fractionalseconddigits.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/smallestunit-fractionalseconddigits.js
new file mode 100644
index 0000000000..601c7ec0bf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/smallestunit-fractionalseconddigits.js
@@ -0,0 +1,33 @@
+// |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.instant.prototype.tostring
+description: fractionalSecondDigits option is not used with smallestUnit present
+features: [Temporal]
+---*/
+
+const instant = new Temporal.Instant(56_789_999_999n);
+const tests = [
+ ["minute", "1970-01-01T00:00Z"],
+ ["second", "1970-01-01T00:00:56Z"],
+ ["millisecond", "1970-01-01T00:00:56.789Z"],
+ ["microsecond", "1970-01-01T00:00:56.789999Z"],
+ ["nanosecond", "1970-01-01T00:00:56.789999999Z"],
+];
+
+for (const [smallestUnit, expected] of tests) {
+ const string = instant.toString({
+ smallestUnit,
+ fractionalSecondDigits: 5,
+ });
+ assert.sameValue(string, expected, `smallestUnit: "${smallestUnit}" overrides fractionalSecondDigits`);
+}
+
+assert.throws(RangeError, () => instant.toString({
+ smallestUnit: "hour",
+ fractionalSecondDigits: 5,
+}), "hour is an invalid smallestUnit but still overrides fractionalSecondDigits");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/smallestunit-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/smallestunit-invalid-string.js
new file mode 100644
index 0000000000..e3be1b082b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/smallestunit-invalid-string.js
@@ -0,0 +1,40 @@
+// |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.instant.prototype.tostring
+description: RangeError thrown when smallestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const instant = new Temporal.Instant(1_000_000_000_123_987_500n);
+const badValues = [
+ "era",
+ "eraYear",
+ "year",
+ "month",
+ "week",
+ "day",
+ "hour",
+ "millisecond\0",
+ "mill\u0131second",
+ "SECOND",
+ "eras",
+ "eraYears",
+ "years",
+ "months",
+ "weeks",
+ "days",
+ "hours",
+ "milliseconds\0",
+ "mill\u0131seconds",
+ "SECONDS",
+ "other string",
+];
+for (const smallestUnit of badValues) {
+ assert.throws(RangeError, () => instant.toString({ smallestUnit }),
+ `"${smallestUnit}" is not a valid value for smallest unit`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/smallestunit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/smallestunit-plurals-accepted.js
new file mode 100644
index 0000000000..de13a8980a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/smallestunit-plurals-accepted.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.instant.prototype.tostring
+description: Plural units are accepted as well for the smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instant = new Temporal.Instant(1_000_000_000_123_456_789n);
+const validUnits = [
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+];
+TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => instant.toString({ smallestUnit }), validUnits);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/smallestunit-undefined.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/smallestunit-undefined.js
new file mode 100644
index 0000000000..2f43a0b2c6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/smallestunit-undefined.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.instant.prototype.tostring
+description: Fallback value for smallestUnit option
+features: [Temporal]
+---*/
+
+const instant = new Temporal.Instant(1_000_000_000_123_987_500n);
+
+const explicit1 = instant.toString({ smallestUnit: undefined, fractionalSecondDigits: 6 });
+assert.sameValue(explicit1, "2001-09-09T01:46:40.123987Z", "default smallestUnit defers to fractionalSecondDigits");
+const implicit1 = instant.toString({ fractionalSecondDigits: 6 });
+assert.sameValue(implicit1, "2001-09-09T01:46:40.123987Z", "default smallestUnit defers to fractionalSecondDigits");
+
+const explicit2 = instant.toString({ smallestUnit: undefined, fractionalSecondDigits: 3 });
+assert.sameValue(explicit2, "2001-09-09T01:46:40.123Z", "default smallestUnit defers to fractionalSecondDigits");
+const implicit2 = instant.toString({ fractionalSecondDigits: 3 });
+assert.sameValue(implicit2, "2001-09-09T01:46:40.123Z", "default smallestUnit defers to fractionalSecondDigits");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/smallestunit-valid-units.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/smallestunit-valid-units.js
new file mode 100644
index 0000000000..c6b43e8d31
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/smallestunit-valid-units.js
@@ -0,0 +1,58 @@
+// |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.instant.prototype.tostring
+description: Valid units for the smallestUnit option
+features: [Temporal]
+---*/
+
+const instant = new Temporal.Instant(1_000_000_000_123_456_789n);
+
+function test(instance, expectations, description) {
+ for (const [smallestUnit, expectedResult] of expectations) {
+ assert.sameValue(instance.toString({ smallestUnit }), expectedResult,
+ `${description} with smallestUnit "${smallestUnit}"`);
+ }
+}
+
+test(
+ instant,
+ [
+ ["minute", "2001-09-09T01:46Z"],
+ ["second", "2001-09-09T01:46:40Z"],
+ ["millisecond", "2001-09-09T01:46:40.123Z"],
+ ["microsecond", "2001-09-09T01:46:40.123456Z"],
+ ["nanosecond", "2001-09-09T01:46:40.123456789Z"],
+ ],
+ "subseconds toString"
+);
+
+test(
+ new Temporal.Instant(999_999_960_000_000_000n),
+ [
+ ["minute", "2001-09-09T01:46Z"],
+ ["second", "2001-09-09T01:46:00Z"],
+ ["millisecond", "2001-09-09T01:46:00.000Z"],
+ ["microsecond", "2001-09-09T01:46:00.000000Z"],
+ ["nanosecond", "2001-09-09T01:46:00.000000000Z"],
+ ],
+ "whole minutes toString"
+);
+
+const notValid = [
+ "era",
+ "year",
+ "month",
+ "week",
+ "day",
+ "hour",
+];
+
+notValid.forEach((smallestUnit) => {
+ assert.throws(RangeError, () => instant.toString({ smallestUnit }),
+ `"${smallestUnit}" is not a valid unit for the smallestUnit option`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/smallestunit-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/smallestunit-wrong-type.js
new file mode 100644
index 0000000000..3ae694b85d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/smallestunit-wrong-type.js
@@ -0,0 +1,18 @@
+// |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.instant.prototype.tostring
+description: Type conversions for smallestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instant = new Temporal.Instant(1_000_000_000_123_987_500n);
+TemporalHelpers.checkStringOptionWrongType("smallestUnit", "microsecond",
+ (smallestUnit) => instant.toString({ smallestUnit }),
+ (result, descr) => assert.sameValue(result, "2001-09-09T01:46:40.123987Z", descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..485c0dd690
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,18 @@
+// |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.instant.prototype.tostring
+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 instant = new Temporal.Instant(1_000_000_000_987_654_321n);
+ assert.throws(RangeError, () => instant.toString({ timeZone }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..fae78b3b51
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/timezone-getoffsetnanosecondsfor-not-callable.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.instant.prototype.tostring
+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 instant = new Temporal.Instant(1_000_000_000_987_654_321n);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => instant.toString({ timeZone }),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..86daf236ea
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,18 @@
+// |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.instant.prototype.tostring
+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 instant = new Temporal.Instant(1_000_000_000_987_654_321n);
+ assert.throws(RangeError, () => instant.toString({ timeZone }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..e787956a2c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/timezone-getoffsetnanosecondsfor-wrong-type.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.instant.prototype.tostring
+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 instant = new Temporal.Instant(1_000_000_000_987_654_321n);
+ assert.throws(TypeError, () => instant.toString({ timeZone }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/timezone-offset.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/timezone-offset.js
new file mode 100644
index 0000000000..347d4baa22
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/timezone-offset.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.instant.prototype.tostring
+description: The time zone offset part of the string serialization
+features: [BigInt, Temporal]
+---*/
+
+const instant = new Temporal.Instant(0n);
+
+function test(timeZoneIdentifier, expected, description) {
+ const timeZone = new Temporal.TimeZone(timeZoneIdentifier);
+ assert.sameValue(instant.toString({ timeZone }), expected, description);
+}
+
+test("UTC", "1970-01-01T00:00:00+00:00", "offset of UTC is +00:00");
+test("+01:00", "1970-01-01T01:00:00+01:00", "positive offset");
+test("-05:00", "1969-12-31T19:00:00-05:00", "negative offset");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/timezone-string-datetime.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/timezone-string-datetime.js
new file mode 100644
index 0000000000..4debe3d604
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/timezone-string-datetime.js
@@ -0,0 +1,65 @@
+// |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.instant.prototype.tostring
+description: Conversion of ISO date-time strings to Temporal.TimeZone instances
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(0n);
+
+let timeZone = "2021-08-19T17:30";
+assert.throws(RangeError, () => instance.toString({ timeZone }), "bare date-time string is not a time zone");
+
+[
+ "2021-08-19T17:30-07:00:01",
+ "2021-08-19T17:30-07:00:00",
+ "2021-08-19T17:30-07:00:00.1",
+ "2021-08-19T17:30-07:00:00.0",
+ "2021-08-19T17:30-07:00:00.01",
+ "2021-08-19T17:30-07:00:00.00",
+ "2021-08-19T17:30-07:00:00.001",
+ "2021-08-19T17:30-07:00:00.000",
+ "2021-08-19T17:30-07:00:00.0001",
+ "2021-08-19T17:30-07:00:00.0000",
+ "2021-08-19T17:30-07:00:00.00001",
+ "2021-08-19T17:30-07:00:00.00000",
+ "2021-08-19T17:30-07:00:00.000001",
+ "2021-08-19T17:30-07:00:00.000000",
+ "2021-08-19T17:30-07:00:00.0000001",
+ "2021-08-19T17:30-07:00:00.0000000",
+ "2021-08-19T17:30-07:00:00.00000001",
+ "2021-08-19T17:30-07:00:00.00000000",
+ "2021-08-19T17:30-07:00:00.000000001",
+ "2021-08-19T17:30-07:00:00.000000000",
+].forEach((timeZone) => {
+ assert.throws(
+ RangeError,
+ () => instance.toString({ timeZone }),
+ `ISO string ${timeZone} with a sub-minute offset is not a valid time zone`
+ );
+});
+
+timeZone = "2021-08-19T17:30Z";
+const result1 = instance.toString({ timeZone });
+assert.sameValue(result1.substr(-6), "+00:00", "date-time + Z is UTC time zone");
+
+timeZone = "2021-08-19T17:30-07:00";
+const result2 = instance.toString({ timeZone });
+assert.sameValue(result2.substr(-6), "-07:00", "date-time + offset is the offset time zone");
+
+timeZone = "2021-08-19T17:30[UTC]";
+const result3 = instance.toString({ timeZone });
+assert.sameValue(result3.substr(-6), "+00:00", "date-time + IANA annotation is the offset time zone");
+
+timeZone = "2021-08-19T17:30Z[UTC]";
+const result4 = instance.toString({ timeZone });
+assert.sameValue(result4.substr(-6), "+00:00", "date-time + Z + IANA annotation is the offset time zone");
+
+timeZone = "2021-08-19T17:30-07:00[UTC]";
+const result5 = instance.toString({ timeZone });
+assert.sameValue(result5.substr(-6), "+00:00", "date-time + offset + IANA annotation is the offset time zone");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/timezone-string-leap-second.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/timezone-string-leap-second.js
new file mode 100644
index 0000000000..38a39f5ee9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/timezone-string-leap-second.js
@@ -0,0 +1,20 @@
+// |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.instant.prototype.tostring
+description: Leap second is a valid ISO string for TimeZone
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(0n);
+let timeZone = "2016-12-31T23:59:60+00:00[UTC]";
+
+const result = instance.toString({ timeZone });
+assert.sameValue(result.substr(-6), "+00:00", "leap second is a valid ISO string for TimeZone");
+
+timeZone = "2021-08-19T17:30:45.123456789+23:59[+23:59:60]";
+assert.throws(RangeError, () => instance.toString({ timeZone }), "leap second in time zone name not valid");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/timezone-string-multiple-offsets.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/timezone-string-multiple-offsets.js
new file mode 100644
index 0000000000..3257baa8d4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/timezone-string-multiple-offsets.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.instant.prototype.tostring
+description: Time zone strings with UTC offset are not confused with time
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(0n);
+const timeZone = "2021-08-19T17:30:45.123456789-12:12[+01:46]";
+
+const result = instance.toString({ timeZone });
+assert.sameValue(result.substr(-6), "+01:46", "Time zone string determined from bracket name");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/timezone-string-year-zero.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/timezone-string-year-zero.js
new file mode 100644
index 0000000000..bcf5cee1db
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/timezone-string-year-zero.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.instant.prototype.tostring
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+const instance = new Temporal.Instant(0n);
+invalidStrings.forEach((timeZone) => {
+ assert.throws(
+ RangeError,
+ () => instance.toString({ timeZone }),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/timezone-string.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/timezone-string.js
new file mode 100644
index 0000000000..34d1cf44c1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/timezone-string.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.instant.prototype.tostring
+description: Time zone IDs are valid input for a time zone
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getPossibleInstantsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getPossibleInstantsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be looked up");
+ },
+});
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+
+const instance = new Temporal.Instant(0n);
+
+const result1 = instance.toString({ timeZone: "UTC" });
+assert.sameValue(result1.substr(-6), "+00:00", "Time zone created from string 'UTC'");
+
+const result2 = instance.toString({ timeZone: "-01:30" });
+assert.sameValue(result2.substr(-6), "-01:30", "Time zone created from string '-01:30'");
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", getPossibleInstantsForOriginal);
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/timezone-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/timezone-wrong-type.js
new file mode 100644
index 0000000000..e562aa793d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/timezone-wrong-type.js
@@ -0,0 +1,42 @@
+// |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.instant.prototype.tostring
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or object for TimeZone
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.Instant(0n);
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [19761118, "number that would convert to a valid ISO string in other contexts"],
+ [1n, "bigint"],
+];
+
+for (const [timeZone, description] of primitiveTests) {
+ assert.throws(
+ typeof timeZone === 'string' ? RangeError : TypeError,
+ () => instance.toString({ timeZone }),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "object not implementing time zone protocol"],
+ [new Temporal.Calendar("iso8601"), "calendar instance"],
+];
+
+for (const [timeZone, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.toString({ timeZone }), `${description} is not a valid object and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/timezone.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/timezone.js
new file mode 100644
index 0000000000..5be691a9a7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/timezone.js
@@ -0,0 +1,41 @@
+// |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.instant.prototype.tostring
+description: >
+ Passing a TimeZone to options calls getOffsetNanosecondsFor, but not toString
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const expected = [
+ "has timeZone.getOffsetNanosecondsFor",
+ "has timeZone.getPossibleInstantsFor",
+ "has timeZone.id",
+ "get timeZone.getOffsetNanosecondsFor",
+ "call timeZone.getOffsetNanosecondsFor",
+];
+
+const instant = Temporal.Instant.from("1975-02-02T14:25:36.123456Z");
+const timeZone = TemporalHelpers.timeZoneObserver(actual, "timeZone", {
+ toString: TemporalHelpers.toPrimitiveObserver(actual, "Custom/TimeZone", "name"),
+ getOffsetNanosecondsFor(instantArg) {
+ assert.sameValue(instantArg.epochNanoseconds, instant.epochNanoseconds);
+ return -8735135801679;
+ },
+});
+
+Object.defineProperty(Temporal.TimeZone, "from", {
+ get() {
+ actual.push("get Temporal.TimeZone.from");
+ return undefined;
+ },
+});
+
+assert.sameValue(instant.toString({ timeZone }), "1975-02-02T12:00:00.987654321-02:26");
+assert.compareArray(actual, expected);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/year-format.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/year-format.js
new file mode 100644
index 0000000000..579e086006
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toString/year-format.js
@@ -0,0 +1,56 @@
+// |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.instant.prototype.tostring
+description: Verify that the year is appropriately formatted as 4 or 6 digits
+features: [Temporal]
+---*/
+
+function epochNsInYear(year) {
+ // Return an epoch nanoseconds value near the middle of the given year
+ const avgNsPerYear = 31_556_952_000_000_000n;
+ return (year - 1970n) * avgNsPerYear + (avgNsPerYear / 2n);
+}
+
+let instance = new Temporal.Instant(epochNsInYear(-100000n));
+assert.sameValue(instance.toString(), "-100000-07-01T21:30:36Z", "large negative year formatted as 6-digit");
+
+instance = new Temporal.Instant(epochNsInYear(-10000n));
+assert.sameValue(instance.toString(), "-010000-07-01T21:30:36Z", "smallest 5-digit negative year formatted as 6-digit");
+
+instance = new Temporal.Instant(epochNsInYear(-9999n));
+assert.sameValue(instance.toString(), "-009999-07-02T03:19:48Z", "largest 4-digit negative year formatted as 6-digit");
+
+instance = new Temporal.Instant(epochNsInYear(-1000n));
+assert.sameValue(instance.toString(), "-001000-07-02T09:30:36Z", "smallest 4-digit negative year formatted as 6-digit");
+
+instance = new Temporal.Instant(epochNsInYear(-999n));
+assert.sameValue(instance.toString(), "-000999-07-02T15:19:48Z", "largest 3-digit negative year formatted as 6-digit");
+
+instance = new Temporal.Instant(epochNsInYear(-1n));
+assert.sameValue(instance.toString(), "-000001-07-02T15:41:24Z", "year -1 formatted as 6-digit");
+
+instance = new Temporal.Instant(epochNsInYear(0n));
+assert.sameValue(instance.toString(), "0000-07-01T21:30:36Z", "year 0 formatted as 4-digit");
+
+instance = new Temporal.Instant(epochNsInYear(1n));
+assert.sameValue(instance.toString(), "0001-07-02T03:19:48Z", "year 1 formatted as 4-digit");
+
+instance = new Temporal.Instant(epochNsInYear(999n));
+assert.sameValue(instance.toString(), "0999-07-02T03:41:24Z", "largest 3-digit positive year formatted as 4-digit");
+
+instance = new Temporal.Instant(epochNsInYear(1000n));
+assert.sameValue(instance.toString(), "1000-07-02T09:30:36Z", "smallest 4-digit positive year formatted as 4-digit");
+
+instance = new Temporal.Instant(epochNsInYear(9999n));
+assert.sameValue(instance.toString(), "9999-07-02T15:41:24Z", "largest 4-digit positive year formatted as 4-digit");
+
+instance = new Temporal.Instant(epochNsInYear(10000n));
+assert.sameValue(instance.toString(), "+010000-07-01T21:30:36Z", "smallest 5-digit positive year formatted as 6-digit");
+
+instance = new Temporal.Instant(epochNsInYear(100000n));
+assert.sameValue(instance.toString(), "+100000-07-01T21:30:36Z", "large positive year formatted as 6-digit");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toStringTag/browser.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toStringTag/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toStringTag/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toStringTag/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toStringTag/prop-desc.js
new file mode 100644
index 0000000000..2de7806345
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toStringTag/prop-desc.js
@@ -0,0 +1,19 @@
+// |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.instant.prototype-@@tostringtag
+description: The @@toStringTag property of Temporal.Instant
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Instant.prototype, Symbol.toStringTag, {
+ value: "Temporal.Instant",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toStringTag/shell.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toStringTag/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toStringTag/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/branding.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/branding.js
new file mode 100644
index 0000000000..3e57449375
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/branding.js
@@ -0,0 +1,27 @@
+// |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.instant.prototype.tozoneddatetime
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toZonedDateTime = Temporal.Instant.prototype.toZonedDateTime;
+
+assert.sameValue(typeof toZonedDateTime, "function");
+
+const args = [{ calendar: new Temporal.Calendar("iso8601"), timeZone: new Temporal.TimeZone("UTC") }];
+
+assert.throws(TypeError, () => toZonedDateTime.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => toZonedDateTime.apply(null, args), "null");
+assert.throws(TypeError, () => toZonedDateTime.apply(true, args), "true");
+assert.throws(TypeError, () => toZonedDateTime.apply("", args), "empty string");
+assert.throws(TypeError, () => toZonedDateTime.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => toZonedDateTime.apply(1, args), "1");
+assert.throws(TypeError, () => toZonedDateTime.apply({}, args), "plain object");
+assert.throws(TypeError, () => toZonedDateTime.apply(Temporal.Instant, args), "Temporal.Instant");
+assert.throws(TypeError, () => toZonedDateTime.apply(Temporal.Instant.prototype, args), "Temporal.Instant.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/browser.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/builtin.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/builtin.js
new file mode 100644
index 0000000000..d26c5cb1cb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/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.instant.prototype.tozoneddatetime
+description: >
+ Tests that Temporal.Instant.prototype.toZonedDateTime
+ 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.Instant.prototype.toZonedDateTime),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Instant.prototype.toZonedDateTime),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Instant.prototype.toZonedDateTime),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Instant.prototype.toZonedDateTime.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/calendar-case-insensitive.js
new file mode 100644
index 0000000000..f393f6cb24
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/calendar-case-insensitive.js
@@ -0,0 +1,17 @@
+// |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.instant.prototype.tozoneddatetime
+description: Calendar names are case-insensitive
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(1_000_000_000_000_000_000n);
+
+const arg = "iSo8601";
+const result = instance.toZonedDateTime({ calendar: arg, timeZone: "UTC" });
+assert.sameValue(result.calendarId, "iso8601", "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/calendar-number.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/calendar-number.js
new file mode 100644
index 0000000000..eabffeed4d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/calendar-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.instant.prototype.tozoneddatetime
+description: A number is not allowed to be a calendar
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(1_000_000_000_000_000_000n);
+
+const numbers = [
+ 1,
+ -19761118,
+ 19761118,
+ 1234567890,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => instance.toZonedDateTime({ calendar: arg, timeZone: "UTC" }),
+ "A number is not a valid ISO string for Calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/calendar-string-leap-second.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/calendar-string-leap-second.js
new file mode 100644
index 0000000000..b54fb01e97
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/calendar-string-leap-second.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.instant.prototype.tozoneddatetime
+description: Leap second is a valid ISO string for Calendar
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(1_000_000_000_000_000_000n);
+
+const arg = "2016-12-31T23:59:60";
+const result = instance.toZonedDateTime({ calendar: arg, timeZone: "UTC" });
+assert.sameValue(
+ result.calendarId,
+ "iso8601",
+ "leap second is a valid ISO string for Calendar"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/calendar-string.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/calendar-string.js
new file mode 100644
index 0000000000..a8b84623db
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/calendar-string.js
@@ -0,0 +1,18 @@
+// |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.instant.prototype.tozoneddatetime
+description: A calendar ID is valid input for Calendar
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(1_000_000_000_000_000_000n);
+
+const arg = "iso8601";
+
+const result = instance.toZonedDateTime({ calendar: arg, timeZone: "UTC" });
+assert.sameValue(result.getISOFields().calendar, "iso8601", `Calendar created from string "${arg}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/calendar-temporal-object.js
new file mode 100644
index 0000000000..6b9c5a98ba
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/calendar-temporal-object.js
@@ -0,0 +1,42 @@
+// |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.instant.prototype.tozoneddatetime
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal-totemporalcalendar step 1.b:
+ b. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const plainDate = new Temporal.PlainDate(2000, 5, 2);
+const plainDateTime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const plainMonthDay = new Temporal.PlainMonthDay(5, 2);
+const plainYearMonth = new Temporal.PlainYearMonth(2000, 5);
+const zonedDateTime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+
+[plainDate, plainDateTime, plainMonthDay, plainYearMonth, zonedDateTime].forEach((arg) => {
+ const actual = [];
+ const expected = [];
+
+ const calendar = arg.getISOFields().calendar;
+
+ Object.defineProperty(arg, "calendar", {
+ get() {
+ actual.push("get calendar");
+ return calendar;
+ },
+ });
+
+ const instance = new Temporal.Instant(1_000_000_000_000_000_000n);
+ const result = instance.toZonedDateTime({ calendar: arg, timeZone: "UTC" });
+ assert.sameValue(result.getISOFields().calendar, calendar, "Temporal object coerced to calendar");
+
+ assert.compareArray(actual, expected, "calendar getter not called");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/calendar-wrong-type.js
new file mode 100644
index 0000000000..d8c217e83c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/calendar-wrong-type.js
@@ -0,0 +1,42 @@
+// |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.instant.prototype.tozoneddatetime
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or object for Calendar
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.Instant(1_000_000_000_000_000_000n);
+
+const primitiveTests = [
+ [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.toZonedDateTime({ calendar: arg, timeZone: "UTC" }),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.toZonedDateTime({ calendar: arg, timeZone: "UTC" }), `${description} is not a valid object and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/length.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/length.js
new file mode 100644
index 0000000000..7f01cb14d6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/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.instant.prototype.tozoneddatetime
+description: Temporal.Instant.prototype.toZonedDateTime.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.Instant.prototype.toZonedDateTime, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/name.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/name.js
new file mode 100644
index 0000000000..6c96d1aae9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/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.instant.prototype.tozoneddatetime
+description: Temporal.Instant.prototype.toZonedDateTime.name is "toZonedDateTime".
+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.Instant.prototype.toZonedDateTime, "name", {
+ value: "toZonedDateTime",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/not-a-constructor.js
new file mode 100644
index 0000000000..0918db0a8b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/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.instant.prototype.tozoneddatetime
+description: >
+ Temporal.Instant.prototype.toZonedDateTime 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.Instant.prototype.toZonedDateTime();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Instant.prototype.toZonedDateTime), false,
+ "isConstructor(Temporal.Instant.prototype.toZonedDateTime)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/plain-custom-timezone.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/plain-custom-timezone.js
new file mode 100644
index 0000000000..df2a605d4c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/plain-custom-timezone.js
@@ -0,0 +1,30 @@
+// |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.instant.prototype.tozoneddatetime
+description: TimeZone.getPlainDateTimeFor is not called
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const expected = [
+ "has timeZone.getOffsetNanosecondsFor",
+ "has timeZone.getPossibleInstantsFor",
+ "has timeZone.id",
+];
+
+const instant = Temporal.Instant.from("1975-02-02T14:25:36.123456789Z");
+const calendar = Temporal.Calendar.from("iso8601");
+const timeZone = TemporalHelpers.timeZoneObserver(actual, "timeZone", {
+ getPlainDateTimeFor: Temporal.PlainDateTime.from("1963-07-02T12:00:00.987654321"),
+});
+
+const result = instant.toZonedDateTime({ timeZone, calendar });
+assert.sameValue(result.epochNanoseconds, instant.epochNanoseconds);
+
+assert.compareArray(actual, expected);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/prop-desc.js
new file mode 100644
index 0000000000..89182f210b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/prop-desc.js
@@ -0,0 +1,24 @@
+// |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.instant.prototype.tozoneddatetime
+description: The "toZonedDateTime" property of Temporal.Instant.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Instant.prototype.toZonedDateTime,
+ "function",
+ "`typeof Instant.prototype.toZonedDateTime` is `function`"
+);
+
+verifyProperty(Temporal.Instant.prototype, "toZonedDateTime", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/shell.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/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/Instant/prototype/toZonedDateTime/timezone-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/timezone-case-insensitive.js
new file mode 100644
index 0000000000..bcbda633be
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/timezone-case-insensitive.js
@@ -0,0 +1,17 @@
+// |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.instant.prototype.tozoneddatetime
+description: Time zone names are case insensitive
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(0n);
+
+const timeZone = 'uTc';
+const result = instance.toZonedDateTime({ timeZone, calendar: "iso8601" });
+assert.sameValue(result.timeZoneId, 'UTC', `Time zone created from string "${timeZone}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/timezone-string-datetime.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/timezone-string-datetime.js
new file mode 100644
index 0000000000..d31a21dfb4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/timezone-string-datetime.js
@@ -0,0 +1,65 @@
+// |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.instant.prototype.tozoneddatetime
+description: Conversion of ISO date-time strings to Temporal.TimeZone instances
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(0n);
+
+let timeZone = "2021-08-19T17:30";
+assert.throws(RangeError, () => instance.toZonedDateTime({ timeZone, calendar: "iso8601" }), "bare date-time string is not a time zone");
+
+[
+ "2021-08-19T17:30-07:00:01",
+ "2021-08-19T17:30-07:00:00",
+ "2021-08-19T17:30-07:00:00.1",
+ "2021-08-19T17:30-07:00:00.0",
+ "2021-08-19T17:30-07:00:00.01",
+ "2021-08-19T17:30-07:00:00.00",
+ "2021-08-19T17:30-07:00:00.001",
+ "2021-08-19T17:30-07:00:00.000",
+ "2021-08-19T17:30-07:00:00.0001",
+ "2021-08-19T17:30-07:00:00.0000",
+ "2021-08-19T17:30-07:00:00.00001",
+ "2021-08-19T17:30-07:00:00.00000",
+ "2021-08-19T17:30-07:00:00.000001",
+ "2021-08-19T17:30-07:00:00.000000",
+ "2021-08-19T17:30-07:00:00.0000001",
+ "2021-08-19T17:30-07:00:00.0000000",
+ "2021-08-19T17:30-07:00:00.00000001",
+ "2021-08-19T17:30-07:00:00.00000000",
+ "2021-08-19T17:30-07:00:00.000000001",
+ "2021-08-19T17:30-07:00:00.000000000",
+].forEach((timeZone) => {
+ assert.throws(
+ RangeError,
+ () => instance.toZonedDateTime({ timeZone, calendar: "iso8601" }),
+ `ISO string ${timeZone} with a sub-minute offset is not a valid time zone`
+ );
+});
+
+timeZone = "2021-08-19T17:30Z";
+const result1 = instance.toZonedDateTime({ timeZone, calendar: "iso8601" });
+assert.sameValue(result1.timeZoneId, "UTC", "date-time + Z is UTC time zone");
+
+timeZone = "2021-08-19T17:30-07:00";
+const result2 = instance.toZonedDateTime({ timeZone, calendar: "iso8601" });
+assert.sameValue(result2.timeZoneId, "-07:00", "date-time + offset is the offset time zone");
+
+timeZone = "2021-08-19T17:30[UTC]";
+const result3 = instance.toZonedDateTime({ timeZone, calendar: "iso8601" });
+assert.sameValue(result3.timeZoneId, "UTC", "date-time + IANA annotation is the IANA time zone");
+
+timeZone = "2021-08-19T17:30Z[UTC]";
+const result4 = instance.toZonedDateTime({ timeZone, calendar: "iso8601" });
+assert.sameValue(result4.timeZoneId, "UTC", "date-time + Z + IANA annotation is the IANA time zone");
+
+timeZone = "2021-08-19T17:30-07:00[UTC]";
+const result5 = instance.toZonedDateTime({ timeZone, calendar: "iso8601" });
+assert.sameValue(result5.timeZoneId, "UTC", "date-time + offset + IANA annotation is the IANA time zone");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/timezone-string-leap-second.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/timezone-string-leap-second.js
new file mode 100644
index 0000000000..c9a81057b0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/timezone-string-leap-second.js
@@ -0,0 +1,20 @@
+// |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.instant.prototype.tozoneddatetime
+description: Leap second is a valid ISO string for TimeZone
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(0n);
+let timeZone = "2016-12-31T23:59:60+00:00[UTC]";
+
+const result = instance.toZonedDateTime({ timeZone, calendar: "iso8601" });
+assert.sameValue(result.timeZoneId, "UTC", "leap second is a valid ISO string for TimeZone");
+
+timeZone = "2021-08-19T17:30:45.123456789+23:59[+23:59:60]";
+assert.throws(RangeError, () => instance.toZonedDateTime({ timeZone, calendar: "iso8601" }), "leap second in time zone name not valid");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/timezone-string-multiple-offsets.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/timezone-string-multiple-offsets.js
new file mode 100644
index 0000000000..feb6069399
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/timezone-string-multiple-offsets.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.instant.prototype.tozoneddatetime
+description: Time zone parsing from ISO strings uses the bracketed offset, not the ISO string offset
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(0n);
+const timeZone = "2021-08-19T17:30:45.123456789-12:12[+01:46]";
+
+const result = instance.toZonedDateTime({ timeZone, calendar: "iso8601" });
+assert.sameValue(result.timeZoneId, "+01:46", "Time zone string determined from bracket name");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/timezone-string-year-zero.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/timezone-string-year-zero.js
new file mode 100644
index 0000000000..d0b8bed5fb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/timezone-string-year-zero.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.instant.prototype.tozoneddatetime
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+const instance = new Temporal.Instant(0n);
+invalidStrings.forEach((timeZone) => {
+ assert.throws(
+ RangeError,
+ () => instance.toZonedDateTime({ timeZone, calendar: "iso8601" }),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/timezone-string.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/timezone-string.js
new file mode 100644
index 0000000000..bbb0f9191c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/timezone-string.js
@@ -0,0 +1,39 @@
+// |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.instant.prototype.tozoneddatetime
+description: Time zone IDs are valid input for a time zone
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getPossibleInstantsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getPossibleInstantsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be looked up");
+ },
+});
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+
+const instance = new Temporal.Instant(0n);
+
+["UTC", "+01:30"].forEach((timeZone) => {
+ const result = instance.toZonedDateTime({ timeZone, calendar: "iso8601" });
+ assert.sameValue(result.getISOFields().timeZone, timeZone, `time zone slot should store string "${timeZone}"`);
+});
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", getPossibleInstantsForOriginal);
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/timezone-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/timezone-wrong-type.js
new file mode 100644
index 0000000000..fb06d5d9dd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTime/timezone-wrong-type.js
@@ -0,0 +1,42 @@
+// |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.instant.prototype.tozoneddatetime
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or object for TimeZone
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.Instant(0n);
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [19761118, "number that would convert to a valid ISO string in other contexts"],
+ [1n, "bigint"],
+];
+
+for (const [timeZone, description] of primitiveTests) {
+ assert.throws(
+ typeof timeZone === 'string' ? RangeError : TypeError,
+ () => instance.toZonedDateTime({ timeZone, calendar: "iso8601" }),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "object not implementing time zone protocol"],
+ [new Temporal.Calendar("iso8601"), "calendar instance"],
+];
+
+for (const [timeZone, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.toZonedDateTime({ timeZone, calendar: "iso8601" }), `${description} is not a valid object and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/branding.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/branding.js
new file mode 100644
index 0000000000..0c2c626166
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/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.instant.prototype.tozoneddatetimeiso
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toZonedDateTimeISO = Temporal.Instant.prototype.toZonedDateTimeISO;
+
+assert.sameValue(typeof toZonedDateTimeISO, "function");
+
+const args = [{ timeZone: new Temporal.TimeZone("UTC") }];
+
+assert.throws(TypeError, () => toZonedDateTimeISO.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => toZonedDateTimeISO.apply(null, args), "null");
+assert.throws(TypeError, () => toZonedDateTimeISO.apply(true, args), "true");
+assert.throws(TypeError, () => toZonedDateTimeISO.apply("", args), "empty string");
+assert.throws(TypeError, () => toZonedDateTimeISO.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => toZonedDateTimeISO.apply(1, args), "1");
+assert.throws(TypeError, () => toZonedDateTimeISO.apply({}, args), "plain object");
+assert.throws(TypeError, () => toZonedDateTimeISO.apply(Temporal.Instant, args), "Temporal.Instant");
+assert.throws(TypeError, () => toZonedDateTimeISO.apply(Temporal.Instant.prototype, args), "Temporal.Instant.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/browser.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/builtin.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/builtin.js
new file mode 100644
index 0000000000..49049f27ab
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/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.instant.prototype.tozoneddatetimeiso
+description: >
+ Tests that Temporal.Instant.prototype.toZonedDateTimeISO
+ 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.Instant.prototype.toZonedDateTimeISO),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Instant.prototype.toZonedDateTimeISO),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Instant.prototype.toZonedDateTimeISO),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Instant.prototype.toZonedDateTimeISO.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/calendar-is-builtin.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/calendar-is-builtin.js
new file mode 100644
index 0000000000..1d766b5524
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/calendar-is-builtin.js
@@ -0,0 +1,16 @@
+// |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.instant.prototype.tozoneddatetimeiso
+description: >
+ toZonedDateTimeISO() results in a ZonedDateTime with builtin ISO calendar
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(0n);
+const result = instance.toZonedDateTimeISO("UTC");
+assert.sameValue(result.getISOFields().calendar, "iso8601", "calendar slot stores a string");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/length.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/length.js
new file mode 100644
index 0000000000..a7f892b29e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/length.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.instant.prototype.tozoneddatetimeiso
+description: Temporal.Instant.prototype.toZonedDateTimeISO.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.Instant.prototype.toZonedDateTimeISO, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/name.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/name.js
new file mode 100644
index 0000000000..5c6950d02e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/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.instant.prototype.tozoneddatetimeiso
+description: Temporal.Instant.prototype.toZonedDateTimeISO.name is "toZonedDateTimeISO".
+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.Instant.prototype.toZonedDateTimeISO, "name", {
+ value: "toZonedDateTimeISO",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/not-a-constructor.js
new file mode 100644
index 0000000000..ccb58cf341
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/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.instant.prototype.tozoneddatetimeiso
+description: >
+ Temporal.Instant.prototype.toZonedDateTimeISO 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.Instant.prototype.toZonedDateTimeISO();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Instant.prototype.toZonedDateTimeISO), false,
+ "isConstructor(Temporal.Instant.prototype.toZonedDateTimeISO)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/prop-desc.js
new file mode 100644
index 0000000000..8f1dc2f923
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/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.instant.prototype.tozoneddatetimeiso
+description: The "toZonedDateTimeISO" property of Temporal.Instant.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Instant.prototype.toZonedDateTimeISO,
+ "function",
+ "`typeof Instant.prototype.toZonedDateTimeISO` is `function`"
+);
+
+verifyProperty(Temporal.Instant.prototype, "toZonedDateTimeISO", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/shell.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/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/Instant/prototype/toZonedDateTimeISO/timezone-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/timezone-case-insensitive.js
new file mode 100644
index 0000000000..ffeb1f1fad
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/timezone-case-insensitive.js
@@ -0,0 +1,17 @@
+// |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.instant.prototype.tozoneddatetimeiso
+description: Time zone names are case insensitive
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(0n);
+
+const timeZone = 'uTc';
+const result = instance.toZonedDateTimeISO(timeZone);
+assert.sameValue(result.timeZoneId, 'UTC', `Time zone created from string "${timeZone}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/timezone-string-datetime.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/timezone-string-datetime.js
new file mode 100644
index 0000000000..9713be6c7e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/timezone-string-datetime.js
@@ -0,0 +1,65 @@
+// |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.instant.prototype.tozoneddatetimeiso
+description: Conversion of ISO date-time strings to Temporal.TimeZone instances
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(0n);
+
+let timeZone = "2021-08-19T17:30";
+assert.throws(RangeError, () => instance.toZonedDateTimeISO(timeZone), "bare date-time string is not a time zone");
+
+[
+ "2021-08-19T17:30-07:00:01",
+ "2021-08-19T17:30-07:00:00",
+ "2021-08-19T17:30-07:00:00.1",
+ "2021-08-19T17:30-07:00:00.0",
+ "2021-08-19T17:30-07:00:00.01",
+ "2021-08-19T17:30-07:00:00.00",
+ "2021-08-19T17:30-07:00:00.001",
+ "2021-08-19T17:30-07:00:00.000",
+ "2021-08-19T17:30-07:00:00.0001",
+ "2021-08-19T17:30-07:00:00.0000",
+ "2021-08-19T17:30-07:00:00.00001",
+ "2021-08-19T17:30-07:00:00.00000",
+ "2021-08-19T17:30-07:00:00.000001",
+ "2021-08-19T17:30-07:00:00.000000",
+ "2021-08-19T17:30-07:00:00.0000001",
+ "2021-08-19T17:30-07:00:00.0000000",
+ "2021-08-19T17:30-07:00:00.00000001",
+ "2021-08-19T17:30-07:00:00.00000000",
+ "2021-08-19T17:30-07:00:00.000000001",
+ "2021-08-19T17:30-07:00:00.000000000",
+].forEach((timeZone) => {
+ assert.throws(
+ RangeError,
+ () => instance.toZonedDateTimeISO(timeZone),
+ `ISO string ${timeZone} with a sub-minute offset is not a valid time zone`
+ );
+});
+
+timeZone = "2021-08-19T17:30Z";
+const result1 = instance.toZonedDateTimeISO(timeZone);
+assert.sameValue(result1.timeZoneId, "UTC", "date-time + Z is UTC time zone");
+
+timeZone = "2021-08-19T17:30-07:00";
+const result2 = instance.toZonedDateTimeISO(timeZone);
+assert.sameValue(result2.timeZoneId, "-07:00", "date-time + offset is the offset time zone");
+
+timeZone = "2021-08-19T17:30[UTC]";
+const result3 = instance.toZonedDateTimeISO(timeZone);
+assert.sameValue(result3.timeZoneId, "UTC", "date-time + IANA annotation is the IANA time zone");
+
+timeZone = "2021-08-19T17:30Z[UTC]";
+const result4 = instance.toZonedDateTimeISO(timeZone);
+assert.sameValue(result4.timeZoneId, "UTC", "date-time + Z + IANA annotation is the IANA time zone");
+
+timeZone = "2021-08-19T17:30-07:00[UTC]";
+const result5 = instance.toZonedDateTimeISO(timeZone);
+assert.sameValue(result5.timeZoneId, "UTC", "date-time + offset + IANA annotation is the IANA time zone");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/timezone-string-leap-second.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/timezone-string-leap-second.js
new file mode 100644
index 0000000000..b7a00369e8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/timezone-string-leap-second.js
@@ -0,0 +1,20 @@
+// |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.instant.prototype.tozoneddatetimeiso
+description: Leap second is a valid ISO string for TimeZone
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(0n);
+let timeZone = "2016-12-31T23:59:60+00:00[UTC]";
+
+const result = instance.toZonedDateTimeISO(timeZone);
+assert.sameValue(result.timeZoneId, "UTC", "leap second is a valid ISO string for TimeZone");
+
+timeZone = "2021-08-19T17:30:45.123456789+23:59[+23:59:60]";
+assert.throws(RangeError, () => instance.toZonedDateTimeISO(timeZone), "leap second in time zone name not valid");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/timezone-string-multiple-offsets.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/timezone-string-multiple-offsets.js
new file mode 100644
index 0000000000..17f233faa7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/timezone-string-multiple-offsets.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.instant.prototype.tozoneddatetimeiso
+description: Time zone parsing from ISO strings uses the bracketed offset, not the ISO string offset
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(0n);
+const timeZone = "2021-08-19T17:30:45.123456789-12:12[+01:46]";
+
+const result = instance.toZonedDateTimeISO(timeZone);
+assert.sameValue(result.timeZoneId, "+01:46", "Time zone string determined from bracket name");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/timezone-string-year-zero.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/timezone-string-year-zero.js
new file mode 100644
index 0000000000..f6a8c14cf3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/timezone-string-year-zero.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.instant.prototype.tozoneddatetimeiso
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+const instance = new Temporal.Instant(0n);
+invalidStrings.forEach((timeZone) => {
+ assert.throws(
+ RangeError,
+ () => instance.toZonedDateTimeISO(timeZone),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/timezone-string.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/timezone-string.js
new file mode 100644
index 0000000000..c3dfdc8cd9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/timezone-string.js
@@ -0,0 +1,39 @@
+// |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.instant.prototype.tozoneddatetimeiso
+description: Time zone IDs are valid input for a time zone
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getPossibleInstantsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getPossibleInstantsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be looked up");
+ },
+});
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+
+const instance = new Temporal.Instant(0n);
+
+["UTC", "+01:30"].forEach((timeZone) => {
+ const result = instance.toZonedDateTimeISO(timeZone);
+ assert.sameValue(result.getISOFields().timeZone, timeZone, `time zone slot should store string "${timeZone}"`);
+});
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", getPossibleInstantsForOriginal);
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/timezone-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/timezone-wrong-type.js
new file mode 100644
index 0000000000..07e6f59a67
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/timezone-wrong-type.js
@@ -0,0 +1,42 @@
+// |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.instant.prototype.tozoneddatetimeiso
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or object for TimeZone
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.Instant(0n);
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [19761118, "number that would convert to a valid ISO string in other contexts"],
+ [1n, "bigint"],
+];
+
+for (const [timeZone, description] of primitiveTests) {
+ assert.throws(
+ typeof timeZone === 'string' ? RangeError : TypeError,
+ () => instance.toZonedDateTimeISO(timeZone),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "object not implementing time zone protocol"],
+ [new Temporal.Calendar("iso8601"), "calendar instance"],
+];
+
+for (const [timeZone, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.toZonedDateTimeISO(timeZone), `${description} is not a valid object and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/argument-object-tostring.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/argument-object-tostring.js
new file mode 100644
index 0000000000..3b2fe85f93
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/argument-object-tostring.js
@@ -0,0 +1,23 @@
+// |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.instant.prototype.until
+description: Object is converted to a string, then to Temporal.Instant
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(0n);
+
+const arg = {};
+assert.throws(RangeError, () => instance.until(arg), "[object Object] is not a valid ISO string");
+
+arg.toString = function() {
+ return "1970-01-01T00:00Z";
+};
+const result = instance.until(arg);
+TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "result of toString is interpreted as ISO string");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..1881714dcf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/argument-string-calendar-annotation.js
@@ -0,0 +1,34 @@
+// |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.instant.prototype.until
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["1970-01-01T00:00Z[u-ca=iso8601]", "without time zone"],
+ ["1970-01-01T00:00Z[UTC][u-ca=gregory]", "with time zone"],
+ ["1970-01-01T00:00Z[!u-ca=hebrew]", "with ! and no time zone"],
+ ["1970-01-01T00:00Z[UTC][!u-ca=chinese]", "with ! and time zone"],
+ ["1970-01-01T00:00Z[u-ca=discord]", "annotation is ignored"],
+ ["1970-01-01T00:00Z[!u-ca=discord]", "annotation with ! is ignored"],
+ ["1970-01-01T00:00Z[u-ca=iso8601][u-ca=discord]", "two annotations are ignored"],
+];
+
+const instance = new Temporal.Instant(0n);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.until(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/Instant/prototype/until/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..c4a207d849
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/argument-string-critical-unknown-annotation.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.instant.prototype.until
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01T00:00Z[!foo=bar]",
+ "1970-01-01T00:00Z[UTC][!foo=bar]",
+ "1970-01-01T00:00Z[u-ca=iso8601][!foo=bar]",
+ "1970-01-01T00:00Z[UTC][!foo=bar][u-ca=iso8601]",
+ "1970-01-01T00:00Z[foo=bar][!_foo-bar0=Dont-Ignore-This-99999999999]",
+];
+const instance = new Temporal.Instant(0n);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.until(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..969eddb2f1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/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.instant.prototype.until
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const instance = new Temporal.Instant(0n);
+
+const validStrings = [
+ "1970-01-01T00Z",
+ "1970-01-01T00Z[UTC]",
+ "1970-01-01T00Z[!UTC]",
+ "1970-01-01T00Z[Europe/Vienna]",
+ "1970-01-01T00+00:00",
+ "1970-01-01T00+00:00[UTC]",
+ "1970-01-01T00+00:00[!UTC]",
+ "1969-12-31T16-08:00[America/Vancouver]",
+];
+
+for (const arg of validStrings) {
+ const result = instance.until(arg);
+
+ TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ `"${arg}" is a valid UTC offset with time for Instant`
+ );
+}
+
+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.until(arg),
+ `"${arg}" UTC offset without time is not valid for Instant`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/argument-string-invalid.js
new file mode 100644
index 0000000000..57fc985cf4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/argument-string-invalid.js
@@ -0,0 +1,69 @@
+// |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.instant.prototype.until
+description: >
+ RangeError thrown if an invalid ISO string (or syntactically valid ISO string
+ that is not supported) is used as an Instant
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ // invalid ISO strings:
+ "",
+ "invalid iso8601",
+ "2020-01-00T00:00Z",
+ "2020-01-32T00:00Z",
+ "2020-02-30T00:00Z",
+ "2021-02-29T00:00Z",
+ "2020-00-01T00:00Z",
+ "2020-13-01T00:00Z",
+ "2020-01-01TZ",
+ "2020-01-01T25:00:00Z",
+ "2020-01-01T01:60:00Z",
+ "2020-01-01T01:60:61Z",
+ "2020-01-01T00:00Zjunk",
+ "2020-01-01T00:00:00Zjunk",
+ "2020-01-01T00:00:00.000000000Zjunk",
+ "2020-01-01T00:00:00+00:00junk",
+ "2020-01-01T00:00:00+00:00[UTC]junk",
+ "2020-01-01T00:00:00+00:00[UTC][u-ca=iso8601]junk",
+ "02020-01-01T00:00Z",
+ "2020-001-01T00:00Z",
+ "2020-01-001T00:00Z",
+ "2020-01-01T001Z",
+ "2020-01-01T01:001Z",
+ "2020-01-01T01:01:001Z",
+ // valid, but forms not supported in Temporal:
+ "2020-W01-1T00:00Z",
+ "2020-001T00:00Z",
+ "+0002020-01-01T00:00Z",
+ // may be valid in other contexts, but insufficient information for Instant:
+ "2020-01",
+ "+002020-01",
+ "01-01",
+ "2020-W01",
+ "P1Y",
+ "-P12Y",
+ "2020-01-01",
+ "2020-01-01T00",
+ "2020-01-01T00:00",
+ "2020-01-01T00:00:00",
+ "2020-01-01T00:00:00.000000000",
+ // valid, but outside the supported range:
+ "-999999-01-01T00:00Z",
+ "+999999-01-01T00:00Z",
+];
+
+const instance = new Temporal.Instant(0n);
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => instance.until(arg),
+ `"${arg}" should not be a valid ISO string for an Instant`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..5cd59d6043
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/argument-string-multiple-calendar.js
@@ -0,0 +1,28 @@
+// |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.instant.prototype.until
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01T00:00Z[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00Z[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01T00:00Z[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00Z[u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+];
+const instance = new Temporal.Instant(0n);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.until(arg),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..dea3e08b9b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/argument-string-multiple-time-zone.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.instant.prototype.until
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01T00:00Z[UTC][UTC]",
+ "1970-01-01T00:00Z[!UTC][UTC]",
+ "1970-01-01T00:00Z[UTC][!UTC]",
+ "1970-01-01T00:00Z[UTC][u-ca=iso8601][UTC]",
+ "1970-01-01T00:00Z[UTC][foo=bar][UTC]",
+];
+const instance = new Temporal.Instant(0n);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.until(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/argument-string-time-separators.js
new file mode 100644
index 0000000000..26cc469ccb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/argument-string-time-separators.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.instant.prototype.until
+description: Time separator in string argument can vary
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["1970-01-01T00:00Z", "uppercase T"],
+ ["1970-01-01t00:00Z", "lowercase T"],
+ ["1970-01-01 00:00Z", "space between date and time"],
+];
+
+const instance = new Temporal.Instant(0n);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.until(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/Instant/prototype/until/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..0e0210a86e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/argument-string-time-zone-annotation.js
@@ -0,0 +1,35 @@
+// |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.instant.prototype.until
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["1970-01-01T00:00Z[Asia/Kolkata]", "named, with Z"],
+ ["1970-01-01T00:00Z[!Europe/Vienna]", "named, with Z and !"],
+ ["1970-01-01T00:00Z[+00:00]", "numeric, with Z"],
+ ["1970-01-01T00:00Z[!-02:30]", "numeric, with Z and !"],
+ ["1970-01-01T00:00+00:00[UTC]", "named, with offset"],
+ ["1970-01-01T00:00+00:00[!Africa/Abidjan]", "named, with offset and !"],
+ ["1970-01-01T00:00+00:00[-08:00]", "numeric, with offset"],
+ ["1970-01-01T00:00+00:00[!+01:00]", "numeric, with offset and !"],
+];
+
+const instance = new Temporal.Instant(0n);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.until(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/Instant/prototype/until/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..b746009089
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/argument-string-unknown-annotation.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.instant.prototype.until
+description: Various forms of unknown annotation
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["1970-01-01T00:00Z[foo=bar]", "alone"],
+ ["1970-01-01T00:00Z[UTC][foo=bar]", "with time zone"],
+ ["1970-01-01T00:00Z[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["1970-01-01T00:00Z[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["1970-01-01T00:00Z[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+const instance = new Temporal.Instant(0n);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.until(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/Instant/prototype/until/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/argument-wrong-type.js
new file mode 100644
index 0000000000..d788b9437a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/argument-wrong-type.js
@@ -0,0 +1,46 @@
+// |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.instant.prototype.until
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ for Instant
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.Instant(0n);
+
+const primitiveTests = [
+ [undefined, "undefined"],
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [19761118, "number that would convert to a valid ISO string in other contexts"],
+ [1n, "bigint"],
+ [{}, "plain object"],
+ [Temporal.Instant, "Temporal.Instant, object"],
+];
+
+for (const [arg, description] of primitiveTests) {
+ assert.throws(
+ typeof arg === "string" || (typeof arg === "object" && arg !== null) || typeof arg === "function"
+ ? RangeError
+ : TypeError,
+ () => instance.until(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [Temporal.Instant.prototype, "Temporal.Instant.prototype, object"], // fails brand check in toString()
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.until(arg), `${description} does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/argument-zoneddatetime.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/argument-zoneddatetime.js
new file mode 100644
index 0000000000..1477d841f7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/argument-zoneddatetime.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.instant.prototype.until
+description: Fast path for converting Temporal.ZonedDateTime to Temporal.Instant
+info: |
+ sec-temporal.instant.prototype.until step 3:
+ 3. Set _other_ to ? ToTemporalInstant(_other_).
+ sec-temporal-totemporalinstant step 1.b:
+ b. If _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return ! CreateTemporalInstant(_item_.[[Nanoseconds]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalInstantFastPath((datetime) => {
+ const instant = new Temporal.Instant(1_000_000_000_000_000_000n);
+ const result = instant.until(datetime);
+ assert.sameValue(result.total({ unit: "nanoseconds" }), 987654321, "nanoseconds result");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/branding.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/branding.js
new file mode 100644
index 0000000000..84c6d4d5f8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/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.instant.prototype.until
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const until = Temporal.Instant.prototype.until;
+
+assert.sameValue(typeof until, "function");
+
+const args = [new Temporal.Instant(123456n)];
+
+assert.throws(TypeError, () => until.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => until.apply(null, args), "null");
+assert.throws(TypeError, () => until.apply(true, args), "true");
+assert.throws(TypeError, () => until.apply("", args), "empty string");
+assert.throws(TypeError, () => until.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => until.apply(1, args), "1");
+assert.throws(TypeError, () => until.apply({}, args), "plain object");
+assert.throws(TypeError, () => until.apply(Temporal.Instant, args), "Temporal.Instant");
+assert.throws(TypeError, () => until.apply(Temporal.Instant.prototype, args), "Temporal.Instant.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/browser.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/builtin.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/builtin.js
new file mode 100644
index 0000000000..6692d64a58
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/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.instant.prototype.until
+description: >
+ Tests that Temporal.Instant.prototype.until
+ 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.Instant.prototype.until),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Instant.prototype.until),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Instant.prototype.until),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Instant.prototype.until.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/instant-string-limits.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/instant-string-limits.js
new file mode 100644
index 0000000000..42389da3f8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/instant-string-limits.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.instant.prototype.until
+description: String arguments at the limit of the representable range
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const minInstant = new Temporal.Instant(-86400_00000000_000_000_000n);
+const maxInstant = new Temporal.Instant(86400_00000000_000_000_000n);
+
+const minInstantStrings = [
+ "-271821-04-20T00:00Z",
+ "-271821-04-19T23:00-01:00",
+ "-271821-04-19T00:00:00.000000001-23:59:59.999999999",
+];
+for (const str of minInstantStrings) {
+ TemporalHelpers.assertDuration(minInstant.until(str), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, `instant string ${str} should be valid`);
+}
+
+const maxInstantStrings = [
+ "+275760-09-13T00:00Z",
+ "+275760-09-13T01:00+01:00",
+ "+275760-09-13T23:59:59.999999999+23:59:59.999999999",
+];
+
+for (const str of maxInstantStrings) {
+ TemporalHelpers.assertDuration(maxInstant.until(str), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, `instant string ${str} should be valid`);
+}
+
+const outOfRangeInstantStrings = [
+ "-271821-04-19T23:59:59.999999999Z",
+ "-271821-04-19T23:00-00:59:59.999999999",
+ "-271821-04-19T00:00:00-23:59:59.999999999",
+ "-271821-04-19T00:00:00-24:00",
+ "+275760-09-13T00:00:00.000000001Z",
+ "+275760-09-13T01:00+00:59:59.999999999",
+ "+275760-09-14T00:00+23:59:59.999999999",
+ "+275760-09-14T00:00+24:00",
+];
+
+for (const str of outOfRangeInstantStrings) {
+ assert.throws(RangeError, () => minInstant.until(str), `instant string ${str} should not be valid`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/instant-string-multiple-offsets.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/instant-string-multiple-offsets.js
new file mode 100644
index 0000000000..bad4c969a5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/instant-string-multiple-offsets.js
@@ -0,0 +1,18 @@
+// |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.instant.prototype.until
+description: Instant strings with UTC offset fractional part are not confused with time fractional part
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(0n);
+const str = "1970-01-01T00:02:00.000000000+00:02[+01:30]";
+
+const result = instance.until(str);
+TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "UTC offset determined from offset part of string");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/instant-string-sub-minute-offset.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/instant-string-sub-minute-offset.js
new file mode 100644
index 0000000000..7cbfc63dac
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/instant-string-sub-minute-offset.js
@@ -0,0 +1,68 @@
+// |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.instant.prototype.until
+description: Temporal.Instant string with sub-minute offset
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(0n);
+
+const str = "1970-01-01T00:19:32.37+00:19:32.37";
+const result = instance.until(str);
+TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "if present, sub-minute offset is accepted exactly");
+
+[
+ "2021-08-19T17:30-07:00:01[-07:00:01]",
+ "2021-08-19T17:30-07:00:00[-07:00:00]",
+ "2021-08-19T17:30-07:00:00.1[-07:00:00.1]",
+ "2021-08-19T17:30-07:00:00.0[-07:00:00.0]",
+ "2021-08-19T17:30-07:00:00.01[-07:00:00.01]",
+ "2021-08-19T17:30-07:00:00.00[-07:00:00.00]",
+ "2021-08-19T17:30-07:00:00.001[-07:00:00.001]",
+ "2021-08-19T17:30-07:00:00.000[-07:00:00.000]",
+ "2021-08-19T17:30-07:00:00.0001[-07:00:00.0001]",
+ "2021-08-19T17:30-07:00:00.0000[-07:00:00.0000]",
+ "2021-08-19T17:30-07:00:00.00001[-07:00:00.00001]",
+ "2021-08-19T17:30-07:00:00.00000[-07:00:00.00000]",
+ "2021-08-19T17:30-07:00:00.000001[-07:00:00.000001]",
+ "2021-08-19T17:30-07:00:00.000000[-07:00:00.000000]",
+ "2021-08-19T17:30-07:00:00.0000001[-07:00:00.0000001]",
+ "2021-08-19T17:30-07:00:00.0000000[-07:00:00.0000000]",
+ "2021-08-19T17:30-07:00:00.00000001[-07:00:00.00000001]",
+ "2021-08-19T17:30-07:00:00.00000000[-07:00:00.00000000]",
+ "2021-08-19T17:30-07:00:00.000000001[-07:00:00.000000001]",
+ "2021-08-19T17:30-07:00:00.000000000[-07:00:00.000000000]",
+
+ "2021-08-19T17:30-07:00:01[-070001]",
+ "2021-08-19T17:30-07:00:00[-070000]",
+ "2021-08-19T17:30-07:00:00.1[-070000.1]",
+ "2021-08-19T17:30-07:00:00.0[-070000.0]",
+ "2021-08-19T17:30-07:00:00.01[-070000.01]",
+ "2021-08-19T17:30-07:00:00.00[-070000.00]",
+ "2021-08-19T17:30-07:00:00.001[-070000.001]",
+ "2021-08-19T17:30-07:00:00.000[-070000.000]",
+ "2021-08-19T17:30-07:00:00.0001[-070000.0001]",
+ "2021-08-19T17:30-07:00:00.0000[-070000.0000]",
+ "2021-08-19T17:30-07:00:00.00001[-070000.00001]",
+ "2021-08-19T17:30-07:00:00.00000[-070000.00000]",
+ "2021-08-19T17:30-07:00:00.000001[-070000.000001]",
+ "2021-08-19T17:30-07:00:00.000000[-070000.000000]",
+ "2021-08-19T17:30-07:00:00.0000001[-070000.0000001]",
+ "2021-08-19T17:30-07:00:00.0000000[-070000.0000000]",
+ "2021-08-19T17:30-07:00:00.00000001[-070000.00000001]",
+ "2021-08-19T17:30-07:00:00.00000000[-070000.00000000]",
+ "2021-08-19T17:30-07:00:00.000000001[-070000.000000001]",
+ "2021-08-19T17:30-07:00:00.000000000[-070000.000000000]"
+].forEach((str) => {
+ assert.throws(
+ RangeError,
+ () => instance.until(str),
+ `ISO strings cannot have sub-minute offsets in time zone annotations: ${str}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/instant-string.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/instant-string.js
new file mode 100644
index 0000000000..43559a84e2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/instant-string.js
@@ -0,0 +1,47 @@
+// |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.instant.prototype.until
+description: Conversion of ISO date-time strings to Temporal.Instant instances
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(0n);
+
+let str = "1970-01-01T00:00";
+assert.throws(RangeError, () => instance.until(str), "bare date-time string is not an instant");
+str = "1970-01-01T00:00[UTC]";
+assert.throws(RangeError, () => instance.until(str), "date-time + IANA annotation is not an instant");
+
+str = "1970-01-01T00:00Z";
+const result1 = instance.until(str);
+TemporalHelpers.assertDuration(result1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "date-time + Z preserves exact time");
+
+str = "1970-01-01T00:00+01:00";
+const result2 = instance.until(str);
+TemporalHelpers.assertDuration(result2, 0, 0, 0, 0, 0, 0, -3600, 0, 0, 0, "date-time + offset preserves exact time with offset");
+
+str = "1970-01-01T00:00Z[Etc/Ignored]";
+const result3 = instance.until(str);
+TemporalHelpers.assertDuration(result3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "date-time + Z + IANA annotation ignores the IANA annotation");
+
+str = "1970-01-01T00:00+01:00[Etc/Ignored]";
+const result4 = instance.until(str);
+TemporalHelpers.assertDuration(result4, 0, 0, 0, 0, 0, 0, -3600, 0, 0, 0, "date-time + offset + IANA annotation ignores the IANA annotation");
+
+str = "1970-01-01T00:00Z[u-ca=hebrew]";
+const result6 = instance.until(str);
+TemporalHelpers.assertDuration(result6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "date-time + Z + Calendar ignores the Calendar");
+
+str = "1970-01-01T00:00+01:00[u-ca=hebrew]";
+const result5 = instance.until(str);
+TemporalHelpers.assertDuration(result5, 0, 0, 0, 0, 0, 0, -3600, 0, 0, 0, "date-time + offset + Calendar ignores the Calendar");
+
+str = "1970-01-01T00:00+01:00[Etc/Ignored][u-ca=hebrew]";
+const result7 = instance.until(str);
+TemporalHelpers.assertDuration(result7, 0, 0, 0, 0, 0, 0, -3600, 0, 0, 0, "date-time + offset + IANA annotation + Calendar ignores the Calendar and IANA annotation");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/largestunit-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/largestunit-invalid-string.js
new file mode 100644
index 0000000000..7987fb44c2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/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.instant.prototype.until
+description: RangeError thrown when largestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(1_000_000_000_000_000_000n);
+const later = new Temporal.Instant(1_000_090_061_987_654_321n);
+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, () => earlier.until(later, { largestUnit }),
+ `"${largestUnit}" is not a valid value for largestUnit`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/largestunit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/largestunit-plurals-accepted.js
new file mode 100644
index 0000000000..21539cc6a8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/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.instant.prototype.until
+description: Plural units are accepted as well for the largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(1_000_000_000_987_654_321n);
+const later = new Temporal.Instant(1_086_403_661_988_655_322n);
+const validUnits = [
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+];
+TemporalHelpers.checkPluralUnitsAccepted((largestUnit) => earlier.until(later, { largestUnit }), validUnits);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/largestunit-smallestunit-mismatch.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/largestunit-smallestunit-mismatch.js
new file mode 100644
index 0000000000..944796c86c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/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.instant.prototype.until
+description: RangeError thrown when smallestUnit is larger than largestUnit
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(1_000_000_000_000_000_000n);
+const later = new Temporal.Instant(1_000_090_061_987_654_321n);
+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, () => earlier.until(later, { largestUnit, smallestUnit }));
+ }
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/largestunit-undefined.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/largestunit-undefined.js
new file mode 100644
index 0000000000..fb42751bcb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/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.instant.prototype.until
+description: Fallback value for largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(1_000_000_000_000_000_000n);
+const later = new Temporal.Instant(1_000_090_061_987_654_321n);
+
+const explicit = earlier.until(later, { largestUnit: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 0, 0, 0, 90061, 987, 654, 321, "default largestUnit is second");
+const implicit = earlier.until(later, {});
+TemporalHelpers.assertDuration(implicit, 0, 0, 0, 0, 0, 0, 90061, 987, 654, 321, "default largestUnit is second");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/largestunit-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/largestunit-wrong-type.js
new file mode 100644
index 0000000000..1227effb18
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/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.instant.prototype.until
+description: Type conversions for largestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(1_000_000_000_000_000_000n);
+const later = new Temporal.Instant(1_000_090_061_987_654_321n);
+TemporalHelpers.checkStringOptionWrongType("largestUnit", "hour",
+ (largestUnit) => earlier.until(later, { largestUnit }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 25, 1, 1, 987, 654, 321, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/leap-second.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/leap-second.js
new file mode 100644
index 0000000000..683989a391
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/leap-second.js
@@ -0,0 +1,22 @@
+// |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.instant.prototype.until
+description: Leap second is a valid ISO string for Instant
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(1_483_228_799_000_000_000n);
+
+const arg = "2016-12-31T23:59:60Z";
+const result = instance.until(arg);
+TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "leap second is a valid ISO string for Instant"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/length.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/length.js
new file mode 100644
index 0000000000..fe7cf76710
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/length.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.instant.prototype.until
+description: Temporal.Instant.prototype.until.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.Instant.prototype.until, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/name.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/name.js
new file mode 100644
index 0000000000..abee981a48
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/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.instant.prototype.until
+description: Temporal.Instant.prototype.until.name is "until".
+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.Instant.prototype.until, "name", {
+ value: "until",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/not-a-constructor.js
new file mode 100644
index 0000000000..65f4d094af
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/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.instant.prototype.until
+description: >
+ Temporal.Instant.prototype.until 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.Instant.prototype.until();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Instant.prototype.until), false,
+ "isConstructor(Temporal.Instant.prototype.until)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/options-object.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/options-object.js
new file mode 100644
index 0000000000..8689633bce
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/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.instant.prototype.until
+description: Empty or a function object may be used as options
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(0n);
+
+const result1 = instance.until(new Temporal.Instant(3600_000_000_000n), {});
+TemporalHelpers.assertDuration(
+ result1, 0, 0, 0, 0, 0, 0, 3600, 0, 0, 0,
+ "options may be an empty plain object"
+);
+
+const result2 = instance.until(new Temporal.Instant(3600_000_000_000n), () => {});
+TemporalHelpers.assertDuration(
+ result2, 0, 0, 0, 0, 0, 0, 3600, 0, 0, 0,
+ "options may be a function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/options-undefined.js
new file mode 100644
index 0000000000..de5e7cfa18
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/options-undefined.js
@@ -0,0 +1,34 @@
+// |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.instant.prototype.until
+description: Verify that undefined options are handled correctly.
+features: [BigInt, Temporal]
+---*/
+
+const earlier = new Temporal.Instant(957270896_987_654_321n);
+const later = new Temporal.Instant(959949296_987_654_322n);
+
+const explicit = earlier.until(later, undefined);
+assert.sameValue(explicit.years, 0, "default largest unit is seconds");
+assert.sameValue(explicit.months, 0, "default largest unit is seconds");
+assert.sameValue(explicit.weeks, 0, "default largest unit is seconds");
+assert.sameValue(explicit.days, 0, "default largest unit is seconds");
+assert.sameValue(explicit.hours, 0, "default largest unit is seconds");
+assert.sameValue(explicit.minutes, 0, "default largest unit is seconds");
+assert.sameValue(explicit.seconds, 2678400, "default largest unit is seconds");
+assert.sameValue(explicit.nanoseconds, 1, "default smallest unit is nanoseconds and no rounding");
+
+const implicit = earlier.until(later);
+assert.sameValue(implicit.years, 0, "default largest unit is seconds");
+assert.sameValue(implicit.months, 0, "default largest unit is seconds");
+assert.sameValue(implicit.weeks, 0, "default largest unit is seconds");
+assert.sameValue(implicit.days, 0, "default largest unit is seconds");
+assert.sameValue(implicit.hours, 0, "default largest unit is seconds");
+assert.sameValue(implicit.minutes, 0, "default largest unit is seconds");
+assert.sameValue(implicit.seconds, 2678400, "default largest unit is seconds");
+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/Instant/prototype/until/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/options-wrong-type.js
new file mode 100644
index 0000000000..8eead35af0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/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.instant.prototype.until
+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.Instant(0n);
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.until(new Temporal.Instant(3600_000_000_000n), value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/order-of-operations.js
new file mode 100644
index 0000000000..c4d202646a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/order-of-operations.js
@@ -0,0 +1,57 @@
+// |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.instant.prototype.until
+description: Properties on objects passed to until() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "get other.toString",
+ "call other.toString",
+ "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",
+ "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.Instant(1_000_000_000_000_000_000n);
+const options = TemporalHelpers.propertyBagObserver(actual, {
+ roundingIncrement: 1,
+ roundingMode: "halfExpand",
+ largestUnit: "hours",
+ smallestUnit: "minutes",
+ additional: true,
+}, "options");
+
+instance.until(TemporalHelpers.toPrimitiveObserver(actual, "1970-01-01T00:00Z", "other"), options);
+assert.compareArray(actual, expected, "order of operations");
+
+actual.splice(0); // clear
+
+// short-circuit does not skip reading options
+instance.until(TemporalHelpers.toPrimitiveObserver(actual, "2001-09-09T01:46:40Z", "other"), options);
+assert.compareArray(actual, expected, "order of operations with identical instants");
+
+actual.splice(0); // clear
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/prop-desc.js
new file mode 100644
index 0000000000..755a2ff1ab
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/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.instant.prototype.until
+description: The "until" property of Temporal.Instant.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Instant.prototype.until,
+ "function",
+ "`typeof Instant.prototype.until` is `function`"
+);
+
+verifyProperty(Temporal.Instant.prototype, "until", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/round-cross-unit-boundary.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/round-cross-unit-boundary.js
new file mode 100644
index 0000000000..101827aea1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/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.instant.prototype.until
+description: Rounding can cross unit boundaries up to largestUnit
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(0n);
+const later = new Temporal.Instant(7199_000_000_000n);
+const duration = earlier.until(later, { largestUnit: "hours", smallestUnit: "minutes", roundingMode: "expand" });
+TemporalHelpers.assertDuration(duration, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, "1:59 balances to 2 hours");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/roundingincrement-nan.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/roundingincrement-nan.js
new file mode 100644
index 0000000000..87ac223661
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/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.instant.prototype.until
+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.instant.prototype.until step 11:
+ 11. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*).
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(1_000_000_000_987_654_321n);
+const later = new Temporal.Instant(1_000_090_061_988_655_322n);
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: NaN }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/roundingincrement-non-integer.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/roundingincrement-non-integer.js
new file mode 100644
index 0000000000..70464af60b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/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.instant.prototype.until
+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.Instant(1_000_000_000_000_000_000n);
+const later = new Temporal.Instant(1_000_000_000_000_000_005n);
+const result = earlier.until(later, { roundingIncrement: 2.5, roundingMode: "trunc" });
+TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, "roundingIncrement 2.5 truncates to 2");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/roundingincrement-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/roundingincrement-out-of-range.js
new file mode 100644
index 0000000000..807cb7a3e6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/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.instant.prototype.until
+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.Instant(1_000_000_000_000_000_000n);
+const later = new Temporal.Instant(1_000_000_000_000_000_005n);
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: -Infinity }));
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: -1 }));
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: 0 }));
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: 0.9 }));
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: 1e9 + 1 }));
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: Infinity }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/roundingincrement-undefined.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/roundingincrement-undefined.js
new file mode 100644
index 0000000000..592f0c6064
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/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.instant.prototype.until
+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.instant.prototype.until step 11:
+ 11. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(1_000_000_000_987_654_321n);
+const later = new Temporal.Instant(1_000_090_061_988_655_322n);
+
+const explicit = earlier.until(later, { roundingIncrement: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 0, 0, 0, 90061, 1, 1, 1, "default roundingIncrement is 1");
+
+const implicit = earlier.until(later, {});
+TemporalHelpers.assertDuration(implicit, 0, 0, 0, 0, 0, 0, 90061, 1, 1, 1, "default roundingIncrement is 1");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/roundingincrement-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/roundingincrement-wrong-type.js
new file mode 100644
index 0000000000..46ea3d964d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/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.instant.prototype.until
+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.instant.prototype.until step 11:
+ 11. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*).
+includes: [temporalHelpers.js, compareArray.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(1_000_000_000_987_654_321n);
+const later = new Temporal.Instant(1_000_090_061_988_655_322n);
+
+TemporalHelpers.checkRoundingIncrementOptionWrongType(
+ (roundingIncrement) => earlier.until(later, { roundingIncrement }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 90061, 1, 1, 1, descr),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 90061, 1, 1, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/roundingmode-ceil.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/roundingmode-ceil.js
new file mode 100644
index 0000000000..ef964703f7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/roundingmode-ceil.js
@@ -0,0 +1,42 @@
+// |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.instant.prototype.until
+description: Tests calculations with roundingMode "ceil".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(217178610_123_456_789n /* 1976-11-18T15:23:30.123456789Z */);
+const later = new Temporal.Instant(1572345998_271_986_289n /* 2019-10-29T10:46:38.271986289Z */);
+
+const expected = [
+ ["hours", [0, 0, 0, 0, 376436], [0, 0, 0, 0, -376435]],
+ ["minutes", [0, 0, 0, 0, 376435, 24], [0, 0, 0, 0, -376435, -23]],
+ ["seconds", [0, 0, 0, 0, 376435, 23, 9], [0, 0, 0, 0, -376435, -23, -8]],
+ ["milliseconds", [0, 0, 0, 0, 376435, 23, 8, 149], [0, 0, 0, 0, -376435, -23, -8, -148]],
+ ["microseconds", [0, 0, 0, 0, 376435, 23, 8, 148, 530], [0, 0, 0, 0, -376435, -23, -8, -148, -529]],
+ ["nanoseconds", [0, 0, 0, 0, 376435, 23, 8, 148, 529, 500], [0, 0, 0, 0, -376435, -23, -8, -148, -529, -500]],
+];
+
+const roundingMode = "ceil";
+const largestUnit = "hours";
+
+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(
+ earlier.until(later, { largestUnit, smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { largestUnit, 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/Instant/prototype/until/roundingmode-expand.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/roundingmode-expand.js
new file mode 100644
index 0000000000..5d905f9a87
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/roundingmode-expand.js
@@ -0,0 +1,42 @@
+// |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.instant.prototype.until
+description: Tests calculations with roundingMode "expand".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(217178610_123_456_789n /* 1976-11-18T15:23:30.123456789Z */);
+const later = new Temporal.Instant(1572345998_271_986_289n /* 2019-10-29T10:46:38.271986289Z */);
+
+const expected = [
+ ["hours", [0, 0, 0, 0, 376436], [0, 0, 0, 0, -376436]],
+ ["minutes", [0, 0, 0, 0, 376435, 24], [0, 0, 0, 0, -376435, -24]],
+ ["seconds", [0, 0, 0, 0, 376435, 23, 9], [0, 0, 0, 0, -376435, -23, -9]],
+ ["milliseconds", [0, 0, 0, 0, 376435, 23, 8, 149], [0, 0, 0, 0, -376435, -23, -8, -149]],
+ ["microseconds", [0, 0, 0, 0, 376435, 23, 8, 148, 530], [0, 0, 0, 0, -376435, -23, -8, -148, -530]],
+ ["nanoseconds", [0, 0, 0, 0, 376435, 23, 8, 148, 529, 500], [0, 0, 0, 0, -376435, -23, -8, -148, -529, -500]],
+];
+
+const roundingMode = "expand";
+const largestUnit = "hours";
+
+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(
+ earlier.until(later, { largestUnit, smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { largestUnit, 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/Instant/prototype/until/roundingmode-floor.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/roundingmode-floor.js
new file mode 100644
index 0000000000..730df9ba66
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/roundingmode-floor.js
@@ -0,0 +1,42 @@
+// |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.instant.prototype.until
+description: Tests calculations with roundingMode "floor".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(217178610_123_456_789n /* 1976-11-18T15:23:30.123456789Z */);
+const later = new Temporal.Instant(1572345998_271_986_289n /* 2019-10-29T10:46:38.271986289Z */);
+
+const expected = [
+ ["hours", [0, 0, 0, 0, 376435], [0, 0, 0, 0, -376436]],
+ ["minutes", [0, 0, 0, 0, 376435, 23], [0, 0, 0, 0, -376435, -24]],
+ ["seconds", [0, 0, 0, 0, 376435, 23, 8], [0, 0, 0, 0, -376435, -23, -9]],
+ ["milliseconds", [0, 0, 0, 0, 376435, 23, 8, 148], [0, 0, 0, 0, -376435, -23, -8, -149]],
+ ["microseconds", [0, 0, 0, 0, 376435, 23, 8, 148, 529], [0, 0, 0, 0, -376435, -23, -8, -148, -530]],
+ ["nanoseconds", [0, 0, 0, 0, 376435, 23, 8, 148, 529, 500], [0, 0, 0, 0, -376435, -23, -8, -148, -529, -500]],
+];
+
+const roundingMode = "floor";
+const largestUnit = "hours";
+
+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(
+ earlier.until(later, { largestUnit, smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { largestUnit, 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/Instant/prototype/until/roundingmode-halfCeil.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/roundingmode-halfCeil.js
new file mode 100644
index 0000000000..8baafd92a4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/roundingmode-halfCeil.js
@@ -0,0 +1,42 @@
+// |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.instant.prototype.until
+description: Tests calculations with roundingMode "halfCeil".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(217178610_123_456_789n /* 1976-11-18T15:23:30.123456789Z */);
+const later = new Temporal.Instant(1572345998_271_986_289n /* 2019-10-29T10:46:38.271986289Z */);
+
+const expected = [
+ ["hours", [0, 0, 0, 0, 376435], [0, 0, 0, 0, -376435]],
+ ["minutes", [0, 0, 0, 0, 376435, 23], [0, 0, 0, 0, -376435, -23]],
+ ["seconds", [0, 0, 0, 0, 376435, 23, 8], [0, 0, 0, 0, -376435, -23, -8]],
+ ["milliseconds", [0, 0, 0, 0, 376435, 23, 8, 149], [0, 0, 0, 0, -376435, -23, -8, -149]],
+ ["microseconds", [0, 0, 0, 0, 376435, 23, 8, 148, 530], [0, 0, 0, 0, -376435, -23, -8, -148, -529]],
+ ["nanoseconds", [0, 0, 0, 0, 376435, 23, 8, 148, 529, 500], [0, 0, 0, 0, -376435, -23, -8, -148, -529, -500]],
+];
+
+const roundingMode = "halfCeil";
+const largestUnit = "hours";
+
+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(
+ earlier.until(later, { largestUnit, smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { largestUnit, 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/Instant/prototype/until/roundingmode-halfEven.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/roundingmode-halfEven.js
new file mode 100644
index 0000000000..824eed73f8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/roundingmode-halfEven.js
@@ -0,0 +1,42 @@
+// |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.instant.prototype.until
+description: Tests calculations with roundingMode "halfEven".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(217178610_123_456_789n /* 1976-11-18T15:23:30.123456789Z */);
+const later = new Temporal.Instant(1572345998_271_986_289n /* 2019-10-29T10:46:38.271986289Z */);
+
+const expected = [
+ ["hours", [0, 0, 0, 0, 376435], [0, 0, 0, 0, -376435]],
+ ["minutes", [0, 0, 0, 0, 376435, 23], [0, 0, 0, 0, -376435, -23]],
+ ["seconds", [0, 0, 0, 0, 376435, 23, 8], [0, 0, 0, 0, -376435, -23, -8]],
+ ["milliseconds", [0, 0, 0, 0, 376435, 23, 8, 149], [0, 0, 0, 0, -376435, -23, -8, -149]],
+ ["microseconds", [0, 0, 0, 0, 376435, 23, 8, 148, 530], [0, 0, 0, 0, -376435, -23, -8, -148, -530]],
+ ["nanoseconds", [0, 0, 0, 0, 376435, 23, 8, 148, 529, 500], [0, 0, 0, 0, -376435, -23, -8, -148, -529, -500]],
+];
+
+const roundingMode = "halfEven";
+const largestUnit = "hours";
+
+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(
+ earlier.until(later, { largestUnit, smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { largestUnit, 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/Instant/prototype/until/roundingmode-halfExpand.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/roundingmode-halfExpand.js
new file mode 100644
index 0000000000..3f8b034a27
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/roundingmode-halfExpand.js
@@ -0,0 +1,42 @@
+// |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.instant.prototype.until
+description: Tests calculations with roundingMode "halfExpand".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(217178610_123_456_789n /* 1976-11-18T15:23:30.123456789Z */);
+const later = new Temporal.Instant(1572345998_271_986_289n /* 2019-10-29T10:46:38.271986289Z */);
+
+const expected = [
+ ["hours", [0, 0, 0, 0, 376435], [0, 0, 0, 0, -376435]],
+ ["minutes", [0, 0, 0, 0, 376435, 23], [0, 0, 0, 0, -376435, -23]],
+ ["seconds", [0, 0, 0, 0, 376435, 23, 8], [0, 0, 0, 0, -376435, -23, -8]],
+ ["milliseconds", [0, 0, 0, 0, 376435, 23, 8, 149], [0, 0, 0, 0, -376435, -23, -8, -149]],
+ ["microseconds", [0, 0, 0, 0, 376435, 23, 8, 148, 530], [0, 0, 0, 0, -376435, -23, -8, -148, -530]],
+ ["nanoseconds", [0, 0, 0, 0, 376435, 23, 8, 148, 529, 500], [0, 0, 0, 0, -376435, -23, -8, -148, -529, -500]],
+];
+
+const roundingMode = "halfExpand";
+const largestUnit = "hours";
+
+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(
+ earlier.until(later, { largestUnit, smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { largestUnit, 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/Instant/prototype/until/roundingmode-halfFloor.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/roundingmode-halfFloor.js
new file mode 100644
index 0000000000..e9fd014f2d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/roundingmode-halfFloor.js
@@ -0,0 +1,42 @@
+// |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.instant.prototype.until
+description: Tests calculations with roundingMode "halfFloor".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(217178610_123_456_789n /* 1976-11-18T15:23:30.123456789Z */);
+const later = new Temporal.Instant(1572345998_271_986_289n /* 2019-10-29T10:46:38.271986289Z */);
+
+const expected = [
+ ["hours", [0, 0, 0, 0, 376435], [0, 0, 0, 0, -376435]],
+ ["minutes", [0, 0, 0, 0, 376435, 23], [0, 0, 0, 0, -376435, -23]],
+ ["seconds", [0, 0, 0, 0, 376435, 23, 8], [0, 0, 0, 0, -376435, -23, -8]],
+ ["milliseconds", [0, 0, 0, 0, 376435, 23, 8, 149], [0, 0, 0, 0, -376435, -23, -8, -149]],
+ ["microseconds", [0, 0, 0, 0, 376435, 23, 8, 148, 529], [0, 0, 0, 0, -376435, -23, -8, -148, -530]],
+ ["nanoseconds", [0, 0, 0, 0, 376435, 23, 8, 148, 529, 500], [0, 0, 0, 0, -376435, -23, -8, -148, -529, -500]],
+];
+
+const roundingMode = "halfFloor";
+const largestUnit = "hours";
+
+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(
+ earlier.until(later, { largestUnit, smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { largestUnit, 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/Instant/prototype/until/roundingmode-halfTrunc.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/roundingmode-halfTrunc.js
new file mode 100644
index 0000000000..a3576e7162
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/roundingmode-halfTrunc.js
@@ -0,0 +1,42 @@
+// |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.instant.prototype.until
+description: Tests calculations with roundingMode "halfTrunc".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(217178610_123_456_789n /* 1976-11-18T15:23:30.123456789Z */);
+const later = new Temporal.Instant(1572345998_271_986_289n /* 2019-10-29T10:46:38.271986289Z */);
+
+const expected = [
+ ["hours", [0, 0, 0, 0, 376435], [0, 0, 0, 0, -376435]],
+ ["minutes", [0, 0, 0, 0, 376435, 23], [0, 0, 0, 0, -376435, -23]],
+ ["seconds", [0, 0, 0, 0, 376435, 23, 8], [0, 0, 0, 0, -376435, -23, -8]],
+ ["milliseconds", [0, 0, 0, 0, 376435, 23, 8, 149], [0, 0, 0, 0, -376435, -23, -8, -149]],
+ ["microseconds", [0, 0, 0, 0, 376435, 23, 8, 148, 529], [0, 0, 0, 0, -376435, -23, -8, -148, -529]],
+ ["nanoseconds", [0, 0, 0, 0, 376435, 23, 8, 148, 529, 500], [0, 0, 0, 0, -376435, -23, -8, -148, -529, -500]],
+];
+
+const roundingMode = "halfTrunc";
+const largestUnit = "hours";
+
+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(
+ earlier.until(later, { largestUnit, smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { largestUnit, 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/Instant/prototype/until/roundingmode-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/roundingmode-invalid-string.js
new file mode 100644
index 0000000000..3440586b5b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/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.instant.prototype.until
+description: RangeError thrown when roundingMode option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(1_000_000_000_000_000_000n);
+const later = new Temporal.Instant(1_000_090_061_123_987_500n);
+for (const roundingMode of ["other string", "cile", "CEIL", "ce\u0131l", "auto", "halfexpand", "floor\0"]) {
+ assert.throws(RangeError, () => earlier.until(later, { smallestUnit: "microsecond", roundingMode }));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/roundingmode-trunc.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/roundingmode-trunc.js
new file mode 100644
index 0000000000..e070610f13
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/roundingmode-trunc.js
@@ -0,0 +1,42 @@
+// |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.instant.prototype.until
+description: Tests calculations with roundingMode "trunc".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(217178610_123_456_789n /* 1976-11-18T15:23:30.123456789Z */);
+const later = new Temporal.Instant(1572345998_271_986_289n /* 2019-10-29T10:46:38.271986289Z */);
+
+const expected = [
+ ["hours", [0, 0, 0, 0, 376435], [0, 0, 0, 0, -376435]],
+ ["minutes", [0, 0, 0, 0, 376435, 23], [0, 0, 0, 0, -376435, -23]],
+ ["seconds", [0, 0, 0, 0, 376435, 23, 8], [0, 0, 0, 0, -376435, -23, -8]],
+ ["milliseconds", [0, 0, 0, 0, 376435, 23, 8, 148], [0, 0, 0, 0, -376435, -23, -8, -148]],
+ ["microseconds", [0, 0, 0, 0, 376435, 23, 8, 148, 529], [0, 0, 0, 0, -376435, -23, -8, -148, -529]],
+ ["nanoseconds", [0, 0, 0, 0, 376435, 23, 8, 148, 529, 500], [0, 0, 0, 0, -376435, -23, -8, -148, -529, -500]],
+];
+
+const roundingMode = "trunc";
+const largestUnit = "hours";
+
+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(
+ earlier.until(later, { largestUnit, smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { largestUnit, 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/Instant/prototype/until/roundingmode-undefined.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/roundingmode-undefined.js
new file mode 100644
index 0000000000..ab92b11c4a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/roundingmode-undefined.js
@@ -0,0 +1,30 @@
+// |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.instant.prototype.until
+description: Fallback value for roundingMode option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(1_000_000_000_000_000_000n);
+const later = new Temporal.Instant(1_000_090_061_123_987_500n);
+
+const explicit1 = earlier.until(later, { smallestUnit: "microsecond", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit1, 0, 0, 0, 0, 0, 0, 90061, 123, 987, 0, "default roundingMode is trunc");
+const implicit1 = earlier.until(later, { smallestUnit: "microsecond" });
+TemporalHelpers.assertDuration(implicit1, 0, 0, 0, 0, 0, 0, 90061, 123, 987, 0, "default roundingMode is trunc");
+
+const explicit2 = earlier.until(later, { smallestUnit: "millisecond", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit2, 0, 0, 0, 0, 0, 0, 90061, 123, 0, 0, "default roundingMode is trunc");
+const implicit2 = earlier.until(later, { smallestUnit: "millisecond" });
+TemporalHelpers.assertDuration(implicit2, 0, 0, 0, 0, 0, 0, 90061, 123, 0, 0, "default roundingMode is trunc");
+
+const explicit3 = earlier.until(later, { smallestUnit: "second", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit3, 0, 0, 0, 0, 0, 0, 90061, 0, 0, 0, "default roundingMode is trunc");
+const implicit3 = earlier.until(later, { smallestUnit: "second" });
+TemporalHelpers.assertDuration(implicit3, 0, 0, 0, 0, 0, 0, 90061, 0, 0, 0, "default roundingMode is trunc");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/roundingmode-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/roundingmode-wrong-type.js
new file mode 100644
index 0000000000..ced6b5dbd0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/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.instant.prototype.until
+description: Type conversions for roundingMode option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(1_000_000_000_000_000_000n);
+const later = new Temporal.Instant(1_000_090_061_123_987_500n);
+TemporalHelpers.checkStringOptionWrongType("roundingMode", "trunc",
+ (roundingMode) => earlier.until(later, { smallestUnit: "microsecond", roundingMode }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 90061, 123, 987, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/shell.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/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/Instant/prototype/until/smallestunit-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/smallestunit-invalid-string.js
new file mode 100644
index 0000000000..5f37b4f361
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/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.instant.prototype.until
+description: RangeError thrown when smallestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(1_000_000_000_000_000_000n);
+const later = new Temporal.Instant(1_000_090_061_987_654_321n);
+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, () => earlier.until(later, { smallestUnit }),
+ `"${smallestUnit}" is not a valid value for smallest unit`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/smallestunit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/smallestunit-plurals-accepted.js
new file mode 100644
index 0000000000..b3f349f642
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/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.instant.prototype.until
+description: Plural units are accepted as well for the smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(1_000_000_000_987_654_321n);
+const later = new Temporal.Instant(1_086_403_661_988_655_322n);
+const validUnits = [
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+];
+TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => earlier.until(later, { smallestUnit }), validUnits);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/smallestunit-undefined.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/smallestunit-undefined.js
new file mode 100644
index 0000000000..e56a1621c0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/smallestunit-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.instant.prototype.until
+description: Fallback value for smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(1_000_000_000_000_000_000n);
+const later = new Temporal.Instant(1_000_090_061_987_654_321n);
+
+const explicit = earlier.until(later, { smallestUnit: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 0, 0, 0, 90061, 987, 654, 321, "default smallestUnit is nanosecond");
+const implicit = earlier.until(later, {});
+TemporalHelpers.assertDuration(implicit, 0, 0, 0, 0, 0, 0, 90061, 987, 654, 321, "default smallestUnit is nanosecond");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/smallestunit-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/smallestunit-wrong-type.js
new file mode 100644
index 0000000000..96b26d4495
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/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.instant.prototype.until
+description: Type conversions for smallestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(1_000_000_000_000_000_000n);
+const later = new Temporal.Instant(1_000_090_061_987_654_321n);
+TemporalHelpers.checkStringOptionWrongType("smallestUnit", "microsecond",
+ (smallestUnit) => earlier.until(later, { smallestUnit }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 90061, 987, 654, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/year-zero.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/year-zero.js
new file mode 100644
index 0000000000..1b12a067f3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/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.instant.prototype.until
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-03-30T00:45Z",
+ "-000000-03-30T01:45+01:00",
+ "-000000-03-30T01:45:00+00:00[UTC]",
+];
+const instance = new Temporal.Instant(0n);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.until(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/valueOf/basic.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/valueOf/basic.js
new file mode 100644
index 0000000000..9ef9afc56c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/valueOf/basic.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.instant.prototype.valueof
+description: Basic tests for valueOf().
+features: [Temporal]
+---*/
+
+const instant = new Temporal.Instant(100n);
+const instant2 = new Temporal.Instant(987654321n);
+
+assert.throws(TypeError, () => instant.valueOf(), "valueOf");
+assert.throws(TypeError, () => instant < instant, "<");
+assert.throws(TypeError, () => instant <= instant, "<=");
+assert.throws(TypeError, () => instant > instant, ">");
+assert.throws(TypeError, () => instant >= instant, ">=");
+assert.sameValue(instant === instant, true, "===");
+assert.sameValue(instant === instant2, false, "===");
+assert.sameValue(instant !== instant, false, "!==");
+assert.sameValue(instant !== instant2, true, "!==");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/valueOf/branding.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/valueOf/branding.js
new file mode 100644
index 0000000000..95b62869e9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/valueOf/branding.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.instant.prototype.valueof
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const valueOf = Temporal.Instant.prototype.valueOf;
+
+assert.sameValue(typeof valueOf, "function");
+
+assert.throws(TypeError, () => valueOf.call(undefined), "undefined");
+assert.throws(TypeError, () => valueOf.call(null), "null");
+assert.throws(TypeError, () => valueOf.call(true), "true");
+assert.throws(TypeError, () => valueOf.call(""), "empty string");
+assert.throws(TypeError, () => valueOf.call(Symbol()), "symbol");
+assert.throws(TypeError, () => valueOf.call(1), "1");
+assert.throws(TypeError, () => valueOf.call({}), "plain object");
+assert.throws(TypeError, () => valueOf.call(Temporal.Instant), "Temporal.Instant");
+assert.throws(TypeError, () => valueOf.call(Temporal.Instant.prototype), "Temporal.Instant.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/valueOf/browser.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/valueOf/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/valueOf/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/valueOf/builtin.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/valueOf/builtin.js
new file mode 100644
index 0000000000..3323d55d88
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/valueOf/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.instant.prototype.valueof
+description: >
+ Tests that Temporal.Instant.prototype.valueOf
+ 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.Instant.prototype.valueOf),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Instant.prototype.valueOf),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Instant.prototype.valueOf),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Instant.prototype.valueOf.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/valueOf/length.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/valueOf/length.js
new file mode 100644
index 0000000000..e23907f0b2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/valueOf/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.instant.prototype.valueof
+description: Temporal.Instant.prototype.valueOf.length is 0
+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.Instant.prototype.valueOf, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/valueOf/name.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/valueOf/name.js
new file mode 100644
index 0000000000..62b7bb3b01
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/valueOf/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.instant.prototype.valueof
+description: Temporal.Instant.prototype.valueOf.name is "valueOf".
+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.Instant.prototype.valueOf, "name", {
+ value: "valueOf",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/valueOf/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/valueOf/not-a-constructor.js
new file mode 100644
index 0000000000..337f3ef41d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/valueOf/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.instant.prototype.valueof
+description: >
+ Temporal.Instant.prototype.valueOf 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.Instant.prototype.valueOf();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Instant.prototype.valueOf), false,
+ "isConstructor(Temporal.Instant.prototype.valueOf)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/valueOf/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/valueOf/prop-desc.js
new file mode 100644
index 0000000000..2230f174dc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/valueOf/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.instant.prototype.valueof
+description: The "valueOf" property of Temporal.Instant.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Instant.prototype.valueOf,
+ "function",
+ "`typeof Instant.prototype.valueOf` is `function`"
+);
+
+verifyProperty(Temporal.Instant.prototype, "valueOf", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/valueOf/shell.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/valueOf/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/valueOf/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/Instant/shell.js b/js/src/tests/test262/built-ins/Temporal/Instant/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/subclass.js b/js/src/tests/test262/built-ins/Temporal/Instant/subclass.js
new file mode 100644
index 0000000000..1858a7294f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Instant/subclass.js
@@ -0,0 +1,20 @@
+// |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.instant
+description: Test for Temporal.Instant subclassing.
+features: [Temporal]
+---*/
+
+class CustomInstant extends Temporal.Instant {
+}
+
+const instance = new CustomInstant(0n);
+assert.sameValue(instance.epochNanoseconds, 0n);
+assert.sameValue(Object.getPrototypeOf(instance), CustomInstant.prototype, "Instance of CustomInstant");
+assert(instance instanceof CustomInstant, "Instance of CustomInstant");
+assert(instance instanceof Temporal.Instant, "Instance of Temporal.Instant");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/browser.js b/js/src/tests/test262/built-ins/Temporal/Now/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/builtin.js b/js/src/tests/test262/built-ins/Temporal/Now/builtin.js
new file mode 100644
index 0000000000..458ae0f7f8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/builtin.js
@@ -0,0 +1,30 @@
+// |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.now
+description: Tests that Temporal.Now meets the requirements for built-in objects
+info: |
+ 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.Now),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Now),
+ "[object Temporal.Now]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Now),
+ Object.prototype, "prototype");
+
+assert.sameValue(Temporal.Now.prototype,
+ undefined, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/instant/browser.js b/js/src/tests/test262/built-ins/Temporal/Now/instant/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/instant/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/instant/extensible.js b/js/src/tests/test262/built-ins/Temporal/Now/instant/extensible.js
new file mode 100644
index 0000000000..23153dafac
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/instant/extensible.js
@@ -0,0 +1,15 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.now.instant
+description: Temporal.Now.instant is extensible.
+features: [Temporal]
+---*/
+
+assert(
+ Object.isExtensible(Temporal.Now.instant),
+ 'Object.isExtensible(Temporal.Now.instant) must return true'
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/instant/length.js b/js/src/tests/test262/built-ins/Temporal/Now/instant/length.js
new file mode 100644
index 0000000000..a2b47f6cc0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/instant/length.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.now.instant
+description: Temporal.Now.instant.length is 0
+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.Now.instant, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/instant/name.js b/js/src/tests/test262/built-ins/Temporal/Now/instant/name.js
new file mode 100644
index 0000000000..557467e929
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/instant/name.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.now.instant
+description: Temporal.Now.instant.name is "instant".
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ Temporal.Now.instant.name,
+ 'instant',
+ 'The value of Temporal.Now.instant.name is expected to be "instant"'
+);
+
+verifyProperty(Temporal.Now.instant, 'name', {
+ enumerable: false,
+ writable: false,
+ configurable: true
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/instant/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Now/instant/not-a-constructor.js
new file mode 100644
index 0000000000..819fc7f67f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/instant/not-a-constructor.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.now.instant
+description: Temporal.Now.instant does not implement [[Construct]]
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal, arrow-function]
+---*/
+
+assert.sameValue(isConstructor(Temporal.Now.instant), false, 'isConstructor(Temporal.Now.instant) must return false');
+
+assert.throws(TypeError, () => {
+ new Temporal.Now.instant();
+}, 'new Temporal.Now.instant() throws a TypeError exception');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/instant/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Now/instant/prop-desc.js
new file mode 100644
index 0000000000..45507da1de
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/instant/prop-desc.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.now.instant
+description: The "instant" property of Temporal.Now
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(typeof Temporal.Now.instant, "function", "typeof is function");
+
+verifyProperty(Temporal.Now, 'instant', {
+ enumerable: false,
+ writable: true,
+ configurable: true
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/instant/return-value-distinct.js b/js/src/tests/test262/built-ins/Temporal/Now/instant/return-value-distinct.js
new file mode 100644
index 0000000000..a94da622cb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/instant/return-value-distinct.js
@@ -0,0 +1,15 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.now.instant
+description: Each invocation of the function produces a distinct object value
+features: [Temporal]
+---*/
+
+var instant1 = Temporal.Now.instant();
+var instant2 = Temporal.Now.instant();
+
+assert.notSameValue(instant1, instant2, 'The value of instant1 is expected to not equal the value of `instant2`');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/instant/return-value-instance.js b/js/src/tests/test262/built-ins/Temporal/Now/instant/return-value-instance.js
new file mode 100644
index 0000000000..514a6a8659
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/instant/return-value-instance.js
@@ -0,0 +1,14 @@
+// |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.now.instant
+description: Temporal.Now.instant returns an Instant
+features: [Temporal]
+---*/
+
+const instant = Temporal.Now.instant();
+assert(instant instanceof Temporal.Instant);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/instant/return-value-prototype.js b/js/src/tests/test262/built-ins/Temporal/Now/instant/return-value-prototype.js
new file mode 100644
index 0000000000..abf09be905
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/instant/return-value-prototype.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.now.instant
+description: Temporal.Now.instant returns an instance of the Instant constructor
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ Object.getPrototypeOf(Temporal.Now.instant()),
+ Temporal.Instant.prototype,
+ 'Object.getPrototypeOf(Temporal.Now.instant()) returns Temporal.Instant.prototype'
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/instant/return-value-value.js b/js/src/tests/test262/built-ins/Temporal/Now/instant/return-value-value.js
new file mode 100644
index 0000000000..6e9350ae82
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/instant/return-value-value.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.now.instant
+description: >
+ Temporal.Now.instant returns an Instant describing the current moment in time
+ (as corroborated by `Date.now`)
+features: [BigInt, Temporal]
+---*/
+var nowBefore = Date.now();
+var seconds = Number(Temporal.Now.instant().epochNanoseconds / 1000000n);
+var nowAfter = Date.now();
+assert(seconds >= nowBefore, 'The result of evaluating (seconds >= nowBefore) is expected to be true');
+assert(seconds <= nowAfter, 'The result of evaluating (seconds <= nowAfter) is expected to be true');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/instant/shell.js b/js/src/tests/test262/built-ins/Temporal/Now/instant/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/instant/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/Now/plainDate/browser.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDate/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDate/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDate/calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDate/calendar-case-insensitive.js
new file mode 100644
index 0000000000..8fd36601e5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDate/calendar-case-insensitive.js
@@ -0,0 +1,16 @@
+// |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.now.plaindate
+description: Calendar names are case-insensitive
+features: [Temporal]
+---*/
+
+const arg = "iSo8601";
+
+const result = Temporal.Now.plainDate(arg);
+assert.sameValue(result.calendarId, "iso8601", "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDate/calendar-number.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDate/calendar-number.js
new file mode 100644
index 0000000000..0ecdc676c1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDate/calendar-number.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.now.plaindate
+description: A number is not allowed to be a calendar
+features: [Temporal]
+---*/
+
+const numbers = [
+ 1,
+ -19761118,
+ 19761118,
+ 1234567890,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => Temporal.Now.plainDate(arg),
+ "A number is not a valid ISO string for Calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDate/calendar-string-leap-second.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDate/calendar-string-leap-second.js
new file mode 100644
index 0000000000..827c62bf00
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDate/calendar-string-leap-second.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.now.plaindate
+description: Leap second is a valid ISO string for Calendar
+features: [Temporal]
+---*/
+
+const arg = "2016-12-31T23:59:60";
+const result = Temporal.Now.plainDate(arg);
+assert.sameValue(
+ result.calendarId,
+ "iso8601",
+ "leap second is a valid ISO string for Calendar"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDate/calendar-string.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDate/calendar-string.js
new file mode 100644
index 0000000000..e5940c63b2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDate/calendar-string.js
@@ -0,0 +1,16 @@
+// |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.now.plaindate
+description: A calendar ID is valid input for Calendar
+features: [Temporal]
+---*/
+
+const arg = "iso8601";
+
+const result = Temporal.Now.plainDate(arg);
+assert.sameValue(result.getISOFields().calendar, "iso8601", `Calendar created from string "${arg}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDate/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDate/calendar-temporal-object.js
new file mode 100644
index 0000000000..c26f9cbecb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDate/calendar-temporal-object.js
@@ -0,0 +1,41 @@
+// |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.now.plaindate
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal-totemporalcalendar step 1.b:
+ b. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const plainDate = new Temporal.PlainDate(2000, 5, 2);
+const plainDateTime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const plainMonthDay = new Temporal.PlainMonthDay(5, 2);
+const plainYearMonth = new Temporal.PlainYearMonth(2000, 5);
+const zonedDateTime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+
+[plainDate, plainDateTime, plainMonthDay, plainYearMonth, zonedDateTime].forEach((arg) => {
+ const actual = [];
+ const expected = [];
+
+ const calendar = arg.getISOFields().calendar;
+
+ Object.defineProperty(arg, "calendar", {
+ get() {
+ actual.push("get calendar");
+ return calendar;
+ },
+ });
+
+ const result = Temporal.Now.plainDate(arg);
+ assert.sameValue(result.getISOFields().calendar, calendar, "Temporal object coerced to calendar");
+
+ assert.compareArray(actual, expected, "calendar getter not called");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDate/calendar-undefined.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDate/calendar-undefined.js
new file mode 100644
index 0000000000..95adc0e98d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDate/calendar-undefined.js
@@ -0,0 +1,14 @@
+// |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.now.plaindate
+description: Throws when the calendar argument is undefined
+features: [Temporal]
+---*/
+
+assert.throws(TypeError, () => Temporal.Now.plainDate(), "implicit");
+assert.throws(TypeError, () => Temporal.Now.plainDate(undefined), "implicit");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDate/calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDate/calendar-wrong-type.js
new file mode 100644
index 0000000000..920af04b27
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDate/calendar-wrong-type.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.now.plaindate
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or object for Calendar
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const primitiveTests = [
+ [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,
+ () => Temporal.Now.plainDate(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => Temporal.Now.plainDate(arg), `${description} is not a valid object and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDate/length.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDate/length.js
new file mode 100644
index 0000000000..c46b6fcb04
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDate/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.now.plaindate
+description: Temporal.Now.plainDate.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.Now.plainDate, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDate/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDate/prop-desc.js
new file mode 100644
index 0000000000..dc8b51fec3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDate/prop-desc.js
@@ -0,0 +1,20 @@
+// |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.now.plaindate
+description: The "plainDate" property of Temporal.Now
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(typeof Temporal.Now.plainDate, "function", "typeof is function");
+
+verifyProperty(Temporal.Now, "plainDate", {
+ enumerable: false,
+ writable: true,
+ configurable: true
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDate/shell.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDate/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDate/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDate/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDate/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..46389adc98
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDate/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,18 @@
+// |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.now.plaindate
+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].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+
+ assert.throws(RangeError, () => Temporal.Now.plainDate("iso8601", timeZone));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDate/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDate/timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..7e250ec946
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDate/timezone-getoffsetnanosecondsfor-not-callable.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.now.plaindate
+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");
+
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => Temporal.Now.plainDate('iso8601', timeZone),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDate/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDate/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..c122803b98
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDate/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,18 @@
+// |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.now.plaindate
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+
+ assert.throws(RangeError, () => Temporal.Now.plainDate("iso8601", timeZone));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDate/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDate/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..bd83cf00b6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDate/timezone-getoffsetnanosecondsfor-wrong-type.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.now.plaindate
+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);
+
+ assert.throws(TypeError, () => Temporal.Now.plainDate("iso8601", timeZone));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDate/timezone-string-datetime.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDate/timezone-string-datetime.js
new file mode 100644
index 0000000000..a91f521b7e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDate/timezone-string-datetime.js
@@ -0,0 +1,64 @@
+// |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.now.plaindate
+description: Conversion of ISO date-time strings to Temporal.TimeZone instances
+features: [Temporal]
+---*/
+
+let timeZone = "2021-08-19T17:30";
+assert.throws(RangeError, () => Temporal.Now.plainDate("iso8601", timeZone), "bare date-time string is not a time zone");
+
+[
+ "2021-08-19T17:30-07:00:01",
+ "2021-08-19T17:30-07:00:00",
+ "2021-08-19T17:30-07:00:00.1",
+ "2021-08-19T17:30-07:00:00.0",
+ "2021-08-19T17:30-07:00:00.01",
+ "2021-08-19T17:30-07:00:00.00",
+ "2021-08-19T17:30-07:00:00.001",
+ "2021-08-19T17:30-07:00:00.000",
+ "2021-08-19T17:30-07:00:00.0001",
+ "2021-08-19T17:30-07:00:00.0000",
+ "2021-08-19T17:30-07:00:00.00001",
+ "2021-08-19T17:30-07:00:00.00000",
+ "2021-08-19T17:30-07:00:00.000001",
+ "2021-08-19T17:30-07:00:00.000000",
+ "2021-08-19T17:30-07:00:00.0000001",
+ "2021-08-19T17:30-07:00:00.0000000",
+ "2021-08-19T17:30-07:00:00.00000001",
+ "2021-08-19T17:30-07:00:00.00000000",
+ "2021-08-19T17:30-07:00:00.000000001",
+ "2021-08-19T17:30-07:00:00.000000000",
+].forEach((timeZone) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.Now.plainDate("iso8601", timeZone),
+ `ISO string ${timeZone} with a sub-minute offset is not a valid time zone`
+ );
+});
+
+// The following are all valid strings so should not throw:
+
+[
+ "2021-08-19T17:30Z",
+ "2021-08-19T1730Z",
+ "2021-08-19T17:30-07:00",
+ "2021-08-19T1730-07:00",
+ "2021-08-19T17:30-0700",
+ "2021-08-19T1730-0700",
+ "2021-08-19T17:30[UTC]",
+ "2021-08-19T1730[UTC]",
+ "2021-08-19T17:30Z[UTC]",
+ "2021-08-19T1730Z[UTC]",
+ "2021-08-19T17:30-07:00[UTC]",
+ "2021-08-19T1730-07:00[UTC]",
+ "2021-08-19T17:30-0700[UTC]",
+ "2021-08-19T1730-0700[UTC]",
+].forEach((timeZone) => {
+ Temporal.Now.plainDate("iso8601", timeZone);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDate/timezone-string-leap-second.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDate/timezone-string-leap-second.js
new file mode 100644
index 0000000000..8ff197234d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDate/timezone-string-leap-second.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.now.plaindate
+description: Leap second is a valid ISO string for TimeZone
+features: [Temporal]
+---*/
+
+let timeZone = "2016-12-31T23:59:60+00:00[UTC]";
+
+// A string with a leap second is a valid ISO string, so the following
+// operation should not throw
+
+Temporal.Now.plainDate("iso8601", timeZone);
+
+timeZone = "2021-08-19T17:30:45.123456789+23:59[+23:59:60]";
+assert.throws(RangeError, () => Temporal.Now.plainDate("iso8601", timeZone), "leap second in time zone name not valid");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDate/timezone-string-year-zero.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDate/timezone-string-year-zero.js
new file mode 100644
index 0000000000..345d2178d4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDate/timezone-string-year-zero.js
@@ -0,0 +1,23 @@
+// |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.now.plaindate
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+invalidStrings.forEach((timeZone) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.Now.plainDate("iso8601", timeZone),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDate/timezone-string.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDate/timezone-string.js
new file mode 100644
index 0000000000..f3a4c07e4e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDate/timezone-string.js
@@ -0,0 +1,38 @@
+// |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.now.plaindate
+description: Time zone IDs are valid input for a time zone
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getPossibleInstantsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getPossibleInstantsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be looked up");
+ },
+});
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+
+// The following are all valid strings so should not throw:
+
+["UTC", "+01:00"].forEach((timeZone) => {
+ Temporal.Now.plainDate("iso8601", timeZone);
+});
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", getPossibleInstantsForOriginal);
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDate/timezone-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDate/timezone-wrong-type.js
new file mode 100644
index 0000000000..20894fd610
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDate/timezone-wrong-type.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.now.plaindate
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or object for TimeZone
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [19761118, "number that would convert to a valid ISO string in other contexts"],
+ [1n, "bigint"],
+];
+
+for (const [timeZone, description] of primitiveTests) {
+ assert.throws(
+ typeof timeZone === 'string' ? RangeError : TypeError,
+ () => Temporal.Now.plainDate("iso8601", timeZone),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "object not implementing time zone protocol"],
+ [new Temporal.Calendar("iso8601"), "calendar instance"],
+];
+
+for (const [timeZone, description] of typeErrorTests) {
+ assert.throws(TypeError, () => Temporal.Now.plainDate("iso8601", timeZone), `${description} is not a valid object and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDate/toPlainDate-override.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDate/toPlainDate-override.js
new file mode 100644
index 0000000000..1b5ba17030
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDate/toPlainDate-override.js
@@ -0,0 +1,43 @@
+// |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.now.plaindate
+description: PlainDateTime.toPlainDate is not observably called
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const expected = [
+ "has timeZone.getOffsetNanosecondsFor",
+ "has timeZone.getPossibleInstantsFor",
+ "has timeZone.id",
+ "get timeZone.getOffsetNanosecondsFor",
+ "call timeZone.getOffsetNanosecondsFor",
+];
+
+Object.defineProperty(Temporal.PlainDateTime.prototype, "toPlainDate", {
+ get() {
+ actual.push("get Temporal.PlainDateTime.prototype.toPlainDate");
+ return function() {
+ actual.push("call Temporal.PlainDateTime.prototype.toPlainDate");
+ };
+ },
+});
+
+const timeZone = TemporalHelpers.timeZoneObserver(actual, "timeZone", {
+ getOffsetNanosecondsFor(instant) {
+ assert.sameValue(instant instanceof Temporal.Instant, true, "Instant");
+ return 86399_999_999_999;
+ },
+});
+
+const result = Temporal.Now.plainDate("iso8601", timeZone);
+assert.notSameValue(result, undefined);
+assert.sameValue(result instanceof Temporal.PlainDate, true);
+
+assert.compareArray(actual, expected);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateISO/browser.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateISO/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateISO/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateISO/length.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateISO/length.js
new file mode 100644
index 0000000000..1f278d493c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateISO/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.now.plaindateiso
+description: Temporal.Now.plainDateISO.length is 0
+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.Now.plainDateISO, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateISO/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateISO/prop-desc.js
new file mode 100644
index 0000000000..ae966c8253
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateISO/prop-desc.js
@@ -0,0 +1,20 @@
+// |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.now.plaindateiso
+description: The "plainDateISO" property of Temporal.Now
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(typeof Temporal.Now.plainDateISO, "function", "typeof is function");
+
+verifyProperty(Temporal.Now, "plainDateISO", {
+ enumerable: false,
+ writable: true,
+ configurable: true
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateISO/return-value.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateISO/return-value.js
new file mode 100644
index 0000000000..1431597fcd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateISO/return-value.js
@@ -0,0 +1,15 @@
+// |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.now.plaindateiso
+description: Functions when time zone argument is omitted
+features: [Temporal]
+---*/
+
+const d = Temporal.Now.plainDateISO();
+assert(d instanceof Temporal.PlainDate);
+assert.sameValue(d.getISOFields().calendar, "iso8601", "calendar slot should store a string");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateISO/shell.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateISO/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateISO/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateISO/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateISO/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..637dc35eac
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateISO/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,18 @@
+// |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.now.plaindateiso
+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].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+
+ assert.throws(RangeError, () => Temporal.Now.plainDateISO(timeZone));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateISO/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateISO/timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..93adafc9a1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateISO/timezone-getoffsetnanosecondsfor-not-callable.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.now.plaindateiso
+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");
+
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => Temporal.Now.plainDateISO(timeZone),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateISO/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateISO/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..eb0b46e888
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateISO/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,18 @@
+// |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.now.plaindateiso
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+
+ assert.throws(RangeError, () => Temporal.Now.plainDateISO(timeZone));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateISO/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateISO/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..fde130990e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateISO/timezone-getoffsetnanosecondsfor-wrong-type.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.now.plaindateiso
+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);
+
+ assert.throws(TypeError, () => Temporal.Now.plainDateISO(timeZone));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateISO/timezone-string-datetime.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateISO/timezone-string-datetime.js
new file mode 100644
index 0000000000..c3c67142b1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateISO/timezone-string-datetime.js
@@ -0,0 +1,64 @@
+// |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.now.plaindateiso
+description: Conversion of ISO date-time strings to Temporal.TimeZone instances
+features: [Temporal]
+---*/
+
+let timeZone = "2021-08-19T17:30";
+assert.throws(RangeError, () => Temporal.Now.plainDateISO(timeZone), "bare date-time string is not a time zone");
+
+[
+ "2021-08-19T17:30-07:00:01",
+ "2021-08-19T17:30-07:00:00",
+ "2021-08-19T17:30-07:00:00.1",
+ "2021-08-19T17:30-07:00:00.0",
+ "2021-08-19T17:30-07:00:00.01",
+ "2021-08-19T17:30-07:00:00.00",
+ "2021-08-19T17:30-07:00:00.001",
+ "2021-08-19T17:30-07:00:00.000",
+ "2021-08-19T17:30-07:00:00.0001",
+ "2021-08-19T17:30-07:00:00.0000",
+ "2021-08-19T17:30-07:00:00.00001",
+ "2021-08-19T17:30-07:00:00.00000",
+ "2021-08-19T17:30-07:00:00.000001",
+ "2021-08-19T17:30-07:00:00.000000",
+ "2021-08-19T17:30-07:00:00.0000001",
+ "2021-08-19T17:30-07:00:00.0000000",
+ "2021-08-19T17:30-07:00:00.00000001",
+ "2021-08-19T17:30-07:00:00.00000000",
+ "2021-08-19T17:30-07:00:00.000000001",
+ "2021-08-19T17:30-07:00:00.000000000",
+].forEach((timeZone) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.Now.plainDateISO(timeZone),
+ `ISO string ${timeZone} with a sub-minute offset is not a valid time zone`
+ );
+});
+
+// The following are all valid strings so should not throw:
+
+[
+ "2021-08-19T17:30Z",
+ "2021-08-19T1730Z",
+ "2021-08-19T17:30-07:00",
+ "2021-08-19T1730-07:00",
+ "2021-08-19T17:30-0700",
+ "2021-08-19T1730-0700",
+ "2021-08-19T17:30[UTC]",
+ "2021-08-19T1730[UTC]",
+ "2021-08-19T17:30Z[UTC]",
+ "2021-08-19T1730Z[UTC]",
+ "2021-08-19T17:30-07:00[UTC]",
+ "2021-08-19T1730-07:00[UTC]",
+ "2021-08-19T17:30-0700[UTC]",
+ "2021-08-19T1730-0700[UTC]",
+].forEach((timeZone) => {
+ Temporal.Now.plainDateISO(timeZone);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateISO/timezone-string-leap-second.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateISO/timezone-string-leap-second.js
new file mode 100644
index 0000000000..fb2dbbee02
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateISO/timezone-string-leap-second.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.now.plaindateiso
+description: Leap second is a valid ISO string for TimeZone
+features: [Temporal]
+---*/
+
+let timeZone = "2016-12-31T23:59:60+00:00[UTC]";
+
+// A string with a leap second is a valid ISO string, so the following
+// operation should not throw
+
+Temporal.Now.plainDateISO(timeZone);
+
+timeZone = "2021-08-19T17:30:45.123456789+23:59[+23:59:60]";
+assert.throws(RangeError, () => Temporal.Now.plainDateISO(timeZone), "leap second in time zone name not valid");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateISO/timezone-string-year-zero.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateISO/timezone-string-year-zero.js
new file mode 100644
index 0000000000..a3abf91147
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateISO/timezone-string-year-zero.js
@@ -0,0 +1,23 @@
+// |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.now.plaindateiso
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+invalidStrings.forEach((timeZone) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.Now.plainDateISO(timeZone),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateISO/timezone-string.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateISO/timezone-string.js
new file mode 100644
index 0000000000..eb02175a5e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateISO/timezone-string.js
@@ -0,0 +1,38 @@
+// |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.now.plaindateiso
+description: Time zone IDs are valid input for a time zone
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getPossibleInstantsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getPossibleInstantsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be looked up");
+ },
+});
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+
+// The following are all valid strings so should not throw:
+
+["UTC", "+01:00"].forEach((timeZone) => {
+ Temporal.Now.plainDateISO(timeZone);
+});
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", getPossibleInstantsForOriginal);
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateISO/timezone-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateISO/timezone-wrong-type.js
new file mode 100644
index 0000000000..d4e5af5fb7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateISO/timezone-wrong-type.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.now.plaindateiso
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or object for TimeZone
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [19761118, "number that would convert to a valid ISO string in other contexts"],
+ [1n, "bigint"],
+];
+
+for (const [timeZone, description] of primitiveTests) {
+ assert.throws(
+ typeof timeZone === 'string' ? RangeError : TypeError,
+ () => Temporal.Now.plainDateISO(timeZone),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "object not implementing time zone protocol"],
+ [new Temporal.Calendar("iso8601"), "calendar instance"],
+];
+
+for (const [timeZone, description] of typeErrorTests) {
+ assert.throws(TypeError, () => Temporal.Now.plainDateISO(timeZone), `${description} is not a valid object and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/browser.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/calendar-case-insensitive.js
new file mode 100644
index 0000000000..52546d1d8e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/calendar-case-insensitive.js
@@ -0,0 +1,16 @@
+// |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.now.plaindatetime
+description: Calendar names are case-insensitive
+features: [Temporal]
+---*/
+
+const arg = "iSo8601";
+
+const result = Temporal.Now.plainDateTime(arg);
+assert.sameValue(result.calendarId, "iso8601", "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/calendar-function.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/calendar-function.js
new file mode 100644
index 0000000000..9c6379c768
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/calendar-function.js
@@ -0,0 +1,64 @@
+// |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.now.plaindatetime
+description: Behavior when provided calendar value is a function
+includes: [compareArray.js, temporalHelpers.js]
+features: [BigInt, Proxy, Temporal]
+---*/
+const actual = [];
+
+const expected = [
+ 'has timeZone.getOffsetNanosecondsFor',
+ 'has timeZone.getPossibleInstantsFor',
+ 'has timeZone.id',
+ 'get timeZone.getOffsetNanosecondsFor',
+ 'call timeZone.getOffsetNanosecondsFor'
+];
+
+const calendar = function() {};
+calendar.dateAdd = () => {};
+calendar.dateFromFields = () => {};
+calendar.dateUntil = () => {};
+calendar.day = () => {};
+calendar.dayOfWeek = () => {};
+calendar.dayOfYear = () => {};
+calendar.daysInMonth = () => {};
+calendar.daysInWeek = () => {};
+calendar.daysInYear = () => {};
+calendar.fields = () => {};
+calendar.id = "test-calendar";
+calendar.inLeapYear = () => {};
+calendar.mergeFields = () => {};
+calendar.month = () => {};
+calendar.monthCode = () => {};
+calendar.monthDayFromFields = () => {};
+calendar.monthsInYear = () => {};
+calendar.weekOfYear = () => {};
+calendar.year = () => {};
+calendar.yearMonthFromFields = () => {};
+calendar.yearOfWeek = () => {};
+
+const timeZone = TemporalHelpers.timeZoneObserver(actual, "timeZone", {
+ getOffsetNanosecondsFor(instant) {
+ return -Number(instant.epochNanoseconds % 86400000000000n);
+ },
+});
+
+Object.defineProperty(Temporal.Calendar, 'from', {
+ get() {
+ actual.push('get Temporal.Calendar.from');
+ return undefined;
+ }
+});
+
+const result = Temporal.Now.plainDateTime(calendar, timeZone);
+
+for (const property of ['hour', 'minute', 'second', 'millisecond', 'microsecond', 'nanosecond']) {
+ assert.sameValue(result[property], 0, 'The value of result[property] is expected to be 0');
+}
+
+assert.compareArray(actual, expected, 'The value of actual is expected to equal the value of expected');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/calendar-number.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/calendar-number.js
new file mode 100644
index 0000000000..ed78325b06
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/calendar-number.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.now.plaindatetime
+description: A number is not allowed to be a calendar
+features: [Temporal]
+---*/
+
+const numbers = [
+ 1,
+ -19761118,
+ 19761118,
+ 1234567890,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => Temporal.Now.plainDateTime(arg),
+ "A number is not a valid ISO string for Calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/calendar-object.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/calendar-object.js
new file mode 100644
index 0000000000..a14d749281
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/calendar-object.js
@@ -0,0 +1,52 @@
+// |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.now.plaindatetime
+description: Observable interactions with the provided calendar-like object
+includes: [compareArray.js, temporalHelpers.js]
+features: [Proxy, Temporal]
+---*/
+
+const actual = [];
+const expected = [
+ "has calendar.dateAdd",
+ "has calendar.dateFromFields",
+ "has calendar.dateUntil",
+ "has calendar.day",
+ "has calendar.dayOfWeek",
+ "has calendar.dayOfYear",
+ "has calendar.daysInMonth",
+ "has calendar.daysInWeek",
+ "has calendar.daysInYear",
+ "has calendar.fields",
+ "has calendar.id",
+ "has calendar.inLeapYear",
+ "has calendar.mergeFields",
+ "has calendar.month",
+ "has calendar.monthCode",
+ "has calendar.monthDayFromFields",
+ "has calendar.monthsInYear",
+ "has calendar.weekOfYear",
+ "has calendar.year",
+ "has calendar.yearMonthFromFields",
+ "has calendar.yearOfWeek",
+];
+
+const calendar = TemporalHelpers.calendarObserver(actual, "calendar", {
+ toString: "iso8601",
+});
+
+Object.defineProperty(Temporal.Calendar, 'from', {
+ get() {
+ actual.push('get Temporal.Calendar.from');
+ return undefined;
+ },
+});
+
+Temporal.Now.plainDateTime(calendar);
+
+assert.compareArray(actual, expected, 'order of observable operations');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/calendar-string-leap-second.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/calendar-string-leap-second.js
new file mode 100644
index 0000000000..fe748cdba6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/calendar-string-leap-second.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.now.plaindatetime
+description: Leap second is a valid ISO string for Calendar
+features: [Temporal]
+---*/
+
+const arg = "2016-12-31T23:59:60";
+const result = Temporal.Now.plainDateTime(arg);
+assert.sameValue(
+ result.calendarId,
+ "iso8601",
+ "leap second is a valid ISO string for Calendar"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/calendar-string.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/calendar-string.js
new file mode 100644
index 0000000000..a32c49d90d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/calendar-string.js
@@ -0,0 +1,16 @@
+// |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.now.plaindatetime
+description: A calendar ID is valid input for Calendar
+features: [Temporal]
+---*/
+
+const arg = "iso8601";
+
+const result = Temporal.Now.plainDateTime(arg);
+assert.sameValue(result.getISOFields().calendar, "iso8601", `Calendar created from string "${arg}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/calendar-temporal-object.js
new file mode 100644
index 0000000000..e27c61be58
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/calendar-temporal-object.js
@@ -0,0 +1,41 @@
+// |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.now.plaindatetime
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal-totemporalcalendar step 1.b:
+ b. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const plainDate = new Temporal.PlainDate(2000, 5, 2);
+const plainDateTime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const plainMonthDay = new Temporal.PlainMonthDay(5, 2);
+const plainYearMonth = new Temporal.PlainYearMonth(2000, 5);
+const zonedDateTime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+
+[plainDate, plainDateTime, plainMonthDay, plainYearMonth, zonedDateTime].forEach((arg) => {
+ const actual = [];
+ const expected = [];
+
+ const calendar = arg.getISOFields().calendar;
+
+ Object.defineProperty(arg, "calendar", {
+ get() {
+ actual.push("get calendar");
+ return calendar;
+ },
+ });
+
+ const result = Temporal.Now.plainDateTime(arg);
+ assert.sameValue(result.getISOFields().calendar, calendar, "Temporal object coerced to calendar");
+
+ assert.compareArray(actual, expected, "calendar getter not called");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/calendar-undefined.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/calendar-undefined.js
new file mode 100644
index 0000000000..7264027698
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/calendar-undefined.js
@@ -0,0 +1,14 @@
+// |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.now.plaindatetime
+description: Throws when the calendar argument is undefined
+features: [Temporal]
+---*/
+
+assert.throws(TypeError, () => Temporal.Now.plainDateTime(), "implicit");
+assert.throws(TypeError, () => Temporal.Now.plainDateTime(undefined), "implicit");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/calendar-wrong-type.js
new file mode 100644
index 0000000000..b25781ba86
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/calendar-wrong-type.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.now.plaindatetime
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or object for Calendar
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const primitiveTests = [
+ [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,
+ () => Temporal.Now.plainDateTime(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => Temporal.Now.plainDateTime(arg), `${description} is not a valid object and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/extensible.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/extensible.js
new file mode 100644
index 0000000000..e733a9cbdd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/extensible.js
@@ -0,0 +1,15 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.now.plaindatetime
+description: Temporal.Now.plainDateTime is extensible.
+features: [Temporal]
+---*/
+
+assert(
+ Object.isExtensible(Temporal.Now.plainDateTime),
+ 'Object.isExtensible(Temporal.Now.plainDateTime) must return true'
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/length.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/length.js
new file mode 100644
index 0000000000..151be87056
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/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.now.plaindatetime
+description: The `length` property of Temporal.Now.plainDateTime
+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.Now.plainDateTime, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/name.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/name.js
new file mode 100644
index 0000000000..671a4b5ec6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/name.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.now.plainDateTime
+description: Temporal.Now.plainDateTime.name is "plainDateTime".
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ Temporal.Now.plainDateTime.name,
+ 'plainDateTime',
+ 'The value of Temporal.Now.plainDateTime.name is expected to be "plainDateTime"'
+);
+
+verifyProperty(Temporal.Now.plainDateTime, 'name', {
+ enumerable: false,
+ writable: false,
+ configurable: true
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/not-a-constructor.js
new file mode 100644
index 0000000000..1ac643010b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/not-a-constructor.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.now.plaindatetime
+description: Temporal.Now.plainDateTime does not implement [[Construct]]
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal, arrow-function]
+---*/
+
+assert.sameValue(isConstructor(Temporal.Now.plainDateTime), false, 'isConstructor(Temporal.Now.plainDateTime) must return false');
+
+assert.throws(TypeError, () => {
+ new Temporal.Now.plainDateTime();
+}, 'new Temporal.Now.plainDateTime() throws a TypeError exception');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/prop-desc.js
new file mode 100644
index 0000000000..287f857843
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/prop-desc.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.now.plaindatetime
+description: The "plainDateTime" property of Temporal.Now
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(typeof Temporal.Now.plainDateTime, "function", "typeof is function");
+
+verifyProperty(Temporal.Now, 'plainDateTime', {
+ enumerable: false,
+ writable: true,
+ configurable: true
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/return-value.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/return-value.js
new file mode 100644
index 0000000000..4fde14062e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/return-value.js
@@ -0,0 +1,25 @@
+// |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.now.plaindatetime
+description: Return value describes the start of a day
+features: [BigInt, Temporal]
+---*/
+const calendar = Temporal.Calendar.from('iso8601');
+
+const timeZone = {
+ id: 'Etc/Test',
+ getPossibleInstantsFor() { return []; },
+ getOffsetNanosecondsFor(instant) {
+ return -Number(instant.epochNanoseconds % 86400000000000n);
+ }
+};
+
+const result = Temporal.Now.plainDateTime(calendar, timeZone);
+
+for (const property of ['hour', 'minute', 'second', 'millisecond', 'microsecond', 'nanosecond']) {
+ assert.sameValue(result[property], 0, 'The value of result[property] is expected to be 0');
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/shell.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/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/Now/plainDateTime/time-zone-undefined.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/time-zone-undefined.js
new file mode 100644
index 0000000000..3363861747
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/time-zone-undefined.js
@@ -0,0 +1,38 @@
+// |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.now.plaindatetime
+description: Functions when time zone argument is omitted
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const expected = [];
+
+Object.defineProperty(Temporal.TimeZone, "from", {
+ get() {
+ actual.push("get Temporal.TimeZone.from");
+ return undefined;
+ },
+});
+
+const resultExplicit = Temporal.Now.plainDateTime("iso8601", undefined);
+assert(
+ resultExplicit instanceof Temporal.PlainDateTime,
+ 'The result of evaluating (resultExplicit instanceof Temporal.PlainDateTime) is expected to be true'
+);
+
+assert.compareArray(actual, expected, 'The value of actual is expected to equal the value of expected');
+
+const resultImplicit = Temporal.Now.plainDateTime("iso8601");
+assert(
+ resultImplicit instanceof Temporal.PlainDateTime,
+ 'The result of evaluating (resultImplicit instanceof Temporal.PlainDateTime) is expected to be true'
+);
+
+assert.compareArray(actual, expected, 'The value of actual is expected to equal the value of expected');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/timezone-getoffsetnanosecondsfor-invocation.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/timezone-getoffsetnanosecondsfor-invocation.js
new file mode 100644
index 0000000000..e9824d30dd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/timezone-getoffsetnanosecondsfor-invocation.js
@@ -0,0 +1,33 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.now.plaindatetime
+description: Correctly invokes `getOffsetNanosecondsFor` method of TimeZone-like objects
+features: [Temporal]
+---*/
+
+var calls = [];
+var timeZone = {
+ id: 'Etc/Test',
+ getPossibleInstantsFor() { return []; },
+ getOffsetNanosecondsFor: function() {
+ calls.push({
+ args: arguments,
+ this: this
+ });
+ return 0;
+ },
+};
+
+Temporal.Now.plainDateTime('iso8601', timeZone);
+
+assert.sameValue(calls.length, 1, 'The value of calls.length is expected to be 1');
+assert.sameValue(calls[0].args.length, 1, 'The value of calls[0].args.length is expected to be 1');
+assert(
+ calls[0].args[0] instanceof Temporal.Instant,
+ 'The result of evaluating (calls[0].args[0] instanceof Temporal.Instant) is expected to be true'
+);
+assert.sameValue(calls[0].this, timeZone, 'The value of calls[0].this is expected to equal the value of timeZone');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..60d5b6a4c1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/timezone-getoffsetnanosecondsfor-non-integer.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.now.plaindatetime
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal, arrow-function]
+includes: [temporalHelpers.js]
+---*/
+[3600000000000.5, NaN].forEach(wrongOffset => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+
+ assert.throws(
+ RangeError,
+ () => Temporal.Now.plainDateTime('iso8601', timeZone),
+ 'Temporal.Now.plainDateTime("iso8601", timeZone) throws a RangeError exception'
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/timezone-getoffsetnanosecondsfor-non-method.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/timezone-getoffsetnanosecondsfor-non-method.js
new file mode 100644
index 0000000000..e85b0cf7d8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/timezone-getoffsetnanosecondsfor-non-method.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.now.plaindatetime
+description: Rejects when `getOffsetNanosecondsFor` property is not a method
+features: [Temporal]
+---*/
+
+var timeZone = {
+ getOffsetNanosecondsFor: 7
+};
+
+assert.throws(TypeError, function() {
+ Temporal.Now.plainDateTime('iso8601', timeZone);
+}, 'Temporal.Now.plainDateTime("iso8601", timeZone) throws a TypeError exception');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/timezone-getoffsetnanosecondsfor-not-a-number.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/timezone-getoffsetnanosecondsfor-not-a-number.js
new file mode 100644
index 0000000000..dca83d87a7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/timezone-getoffsetnanosecondsfor-not-a-number.js
@@ -0,0 +1,42 @@
+// |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.now.plaindatetime
+description: Rejects non-numeric nanosecond values reported by TimeZone-like object
+features: [BigInt, Symbol, Temporal, arrow-function]
+---*/
+const invalidValues = [
+ undefined,
+ null,
+ true,
+ '2020-01-01T12:45:36',
+ Symbol(),
+ 2n,
+ {},
+ Temporal.PlainDateTime,
+ Temporal.PlainDateTime.prototype
+];
+
+for (const dateTime of invalidValues) {
+ let callCount = 0;
+
+ const timeZone = {
+ id: 'Etc/Test',
+ getPossibleInstantsFor() { return []; },
+ getOffsetNanosecondsFor() {
+ callCount += 1;
+ return dateTime;
+ }
+ };
+
+ assert.throws(
+ TypeError,
+ () => Temporal.Now.plainDateTime('iso8601', timeZone),
+ 'Temporal.Now.plainDateTime("iso8601", timeZone) throws a TypeError exception'
+ );
+
+ assert.sameValue(callCount, 1, 'The value of callCount is expected to be 1');
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..f44d0e902b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/timezone-getoffsetnanosecondsfor-not-callable.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.now.plaindatetime
+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");
+
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => Temporal.Now.plainDateTime('iso8601', timeZone),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..389c03a656
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/timezone-getoffsetnanosecondsfor-out-of-range.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.now.plaindatetime
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal, arrow-function]
+includes: [temporalHelpers.js]
+---*/
+[-86400000000001, 86400000000001, -Infinity, Infinity].forEach(wrongOffset => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+
+ assert.throws(
+ RangeError,
+ () => Temporal.Now.plainDateTime('iso8601', timeZone),
+ 'Temporal.Now.plainDateTime("iso8601", timeZone) throws a RangeError exception'
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/timezone-getoffsetnanosecondsfor-poisoned.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/timezone-getoffsetnanosecondsfor-poisoned.js
new file mode 100644
index 0000000000..504383d0f1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/timezone-getoffsetnanosecondsfor-poisoned.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.now.plaindatetime
+description: Forwards error when accessing `getOffsetNanosecondsFor` property throws
+features: [Temporal]
+---*/
+
+var timeZone = {
+ id: 'Etc/Test',
+ getPossibleInstantsFor() { return []; },
+ get getOffsetNanosecondsFor() {
+ throw new Test262Error();
+ }
+};
+
+assert.throws(Test262Error, function() {
+ Temporal.Now.plainDateTime('iso8601', timeZone);
+}, 'Temporal.Now.plainDateTime("iso8601", timeZone) throws a Test262Error exception');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/timezone-getoffsetnanosecondsfor-throws.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/timezone-getoffsetnanosecondsfor-throws.js
new file mode 100644
index 0000000000..06e5139fb6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/timezone-getoffsetnanosecondsfor-throws.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.now.plaindatetime
+description: Forwards error when `getOffsetNanosecondsFor` throws
+features: [Temporal]
+---*/
+
+var timeZone = {
+ id: 'Etc/Test',
+ getPossibleInstantsFor() { return []; },
+ getOffsetNanosecondsFor() {
+ throw new Test262Error();
+ }
+};
+
+assert.throws(Test262Error, function() {
+ Temporal.Now.plainDateTime('iso8601', timeZone);
+}, 'Temporal.Now.plainDateTime("iso8601", timeZone) throws a Test262Error exception');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..4e7779a9e1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/timezone-getoffsetnanosecondsfor-wrong-type.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.now.plaindatetime
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [BigInt, Symbol, Temporal, arrow-function]
+includes: [temporalHelpers.js]
+---*/
+[undefined, null, true, '+01:00', Symbol(), 3600000000000n, {}, {
+ valueOf() {
+ return 3600000000000;
+ }
+}].forEach(wrongOffset => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+
+ assert.throws(
+ TypeError,
+ () => Temporal.Now.plainDateTime('iso8601', timeZone),
+ 'Temporal.Now.plainDateTime("iso8601", timeZone) throws a TypeError exception'
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/timezone-object.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/timezone-object.js
new file mode 100644
index 0000000000..03df690275
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/timezone-object.js
@@ -0,0 +1,42 @@
+// |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.now.plaindatetime
+description: Observable interactions with the provided timezone-like object
+includes: [compareArray.js, temporalHelpers.js]
+features: [BigInt, Proxy, Temporal]
+---*/
+const actual = [];
+
+const expected = [
+ 'has timeZone.getOffsetNanosecondsFor',
+ 'has timeZone.getPossibleInstantsFor',
+ 'has timeZone.id',
+ 'get timeZone.getOffsetNanosecondsFor',
+ 'call timeZone.getOffsetNanosecondsFor'
+];
+
+const timeZone = TemporalHelpers.timeZoneObserver(actual, "timeZone", {
+ getOffsetNanosecondsFor(instant) {
+ assert.sameValue(
+ instant instanceof Temporal.Instant,
+ true,
+ 'The result of evaluating (instant instanceof Temporal.Instant) is expected to be true'
+ );
+
+ return -Number(instant.epochNanoseconds % 86400000000000n);
+ }
+});
+
+Object.defineProperty(Temporal.TimeZone, 'from', {
+ get() {
+ actual.push('get Temporal.TimeZone.from');
+ return undefined;
+ }
+});
+
+Temporal.Now.plainDateTime('iso8601', timeZone);
+assert.compareArray(actual, expected, 'The value of actual is expected to equal the value of expected');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/timezone-string-datetime.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/timezone-string-datetime.js
new file mode 100644
index 0000000000..f6484b628d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/timezone-string-datetime.js
@@ -0,0 +1,64 @@
+// |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.now.plaindatetime
+description: Conversion of ISO date-time strings to Temporal.TimeZone instances
+features: [Temporal]
+---*/
+
+let timeZone = "2021-08-19T17:30";
+assert.throws(RangeError, () => Temporal.Now.plainDateTime("iso8601", timeZone), "bare date-time string is not a time zone");
+
+[
+ "2021-08-19T17:30-07:00:01",
+ "2021-08-19T17:30-07:00:00",
+ "2021-08-19T17:30-07:00:00.1",
+ "2021-08-19T17:30-07:00:00.0",
+ "2021-08-19T17:30-07:00:00.01",
+ "2021-08-19T17:30-07:00:00.00",
+ "2021-08-19T17:30-07:00:00.001",
+ "2021-08-19T17:30-07:00:00.000",
+ "2021-08-19T17:30-07:00:00.0001",
+ "2021-08-19T17:30-07:00:00.0000",
+ "2021-08-19T17:30-07:00:00.00001",
+ "2021-08-19T17:30-07:00:00.00000",
+ "2021-08-19T17:30-07:00:00.000001",
+ "2021-08-19T17:30-07:00:00.000000",
+ "2021-08-19T17:30-07:00:00.0000001",
+ "2021-08-19T17:30-07:00:00.0000000",
+ "2021-08-19T17:30-07:00:00.00000001",
+ "2021-08-19T17:30-07:00:00.00000000",
+ "2021-08-19T17:30-07:00:00.000000001",
+ "2021-08-19T17:30-07:00:00.000000000",
+].forEach((timeZone) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.Now.plainDateTime("iso8601", timeZone),
+ `ISO string ${timeZone} with a sub-minute offset is not a valid time zone`
+ );
+});
+
+// The following are all valid strings so should not throw:
+
+[
+ "2021-08-19T17:30Z",
+ "2021-08-19T1730Z",
+ "2021-08-19T17:30-07:00",
+ "2021-08-19T1730-07:00",
+ "2021-08-19T17:30-0700",
+ "2021-08-19T1730-0700",
+ "2021-08-19T17:30[UTC]",
+ "2021-08-19T1730[UTC]",
+ "2021-08-19T17:30Z[UTC]",
+ "2021-08-19T1730Z[UTC]",
+ "2021-08-19T17:30-07:00[UTC]",
+ "2021-08-19T1730-07:00[UTC]",
+ "2021-08-19T17:30-0700[UTC]",
+ "2021-08-19T1730-0700[UTC]",
+].forEach((timeZone) => {
+ Temporal.Now.plainDateTime("iso8601", timeZone);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/timezone-string-leap-second.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/timezone-string-leap-second.js
new file mode 100644
index 0000000000..6ad5ef4231
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/timezone-string-leap-second.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.now.plaindatetime
+description: Leap second is a valid ISO string for TimeZone
+features: [Temporal]
+---*/
+
+let timeZone = "2016-12-31T23:59:60+00:00[UTC]";
+
+// A string with a leap second is a valid ISO string, so the following
+// operation should not throw
+
+Temporal.Now.plainDateTime("iso8601", timeZone);
+
+timeZone = "2021-08-19T17:30:45.123456789+23:59[+23:59:60]";
+assert.throws(RangeError, () => Temporal.Now.plainDateTime("iso8601", timeZone), "leap second in time zone name not valid");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/timezone-string-year-zero.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/timezone-string-year-zero.js
new file mode 100644
index 0000000000..50680bd26f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/timezone-string-year-zero.js
@@ -0,0 +1,23 @@
+// |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.now.plaindatetime
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+invalidStrings.forEach((timeZone) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.Now.plainDateTime("iso8601", timeZone),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/timezone-string.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/timezone-string.js
new file mode 100644
index 0000000000..b1c6ebe866
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/timezone-string.js
@@ -0,0 +1,38 @@
+// |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.now.plaindatetime
+description: Time zone IDs are valid input for a time zone
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getPossibleInstantsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getPossibleInstantsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be looked up");
+ },
+});
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+
+// The following are all valid strings so should not throw:
+
+["UTC", "+01:00"].forEach((timeZone) => {
+ Temporal.Now.plainDateTime("iso8601", timeZone);
+});
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", getPossibleInstantsForOriginal);
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/timezone-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/timezone-wrong-type.js
new file mode 100644
index 0000000000..bcbe6ea57e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/timezone-wrong-type.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.now.plaindatetime
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or object for TimeZone
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [19761118, "number that would convert to a valid ISO string in other contexts"],
+ [1n, "bigint"],
+];
+
+for (const [timeZone, description] of primitiveTests) {
+ assert.throws(
+ typeof timeZone === 'string' ? RangeError : TypeError,
+ () => Temporal.Now.plainDateTime("iso8601", timeZone),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "object not implementing time zone protocol"],
+ [new Temporal.Calendar("iso8601"), "calendar instance"],
+];
+
+for (const [timeZone, description] of typeErrorTests) {
+ assert.throws(TypeError, () => Temporal.Now.plainDateTime("iso8601", timeZone), `${description} is not a valid object and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/timezone.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/timezone.js
new file mode 100644
index 0000000000..bd55e490d8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTime/timezone.js
@@ -0,0 +1,37 @@
+// |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.now.plaindatetime
+description: The value returned by TimeZone.getOffsetNanosecondsFor affects the result
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const expected = [
+ "has timeZone.getOffsetNanosecondsFor",
+ "has timeZone.getPossibleInstantsFor",
+ "has timeZone.id",
+ "get timeZone.getOffsetNanosecondsFor",
+ "call timeZone.getOffsetNanosecondsFor",
+];
+
+const timeZone = TemporalHelpers.timeZoneObserver(actual, "timeZone", {
+ getOffsetNanosecondsFor(instant) {
+ assert.sameValue(instant instanceof Temporal.Instant, true, "Instant");
+ return -Number(instant.epochNanoseconds % 86400_000_000_000n);
+ },
+});
+
+const calendar = Temporal.Calendar.from("iso8601");
+
+const result = Temporal.Now.plainDateTime(calendar, timeZone);
+for (const property of ["hour", "minute", "second", "millisecond", "microsecond", "nanosecond"]) {
+ assert.sameValue(result[property], 0, property);
+}
+
+assert.compareArray(actual, expected);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/browser.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/extensible.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/extensible.js
new file mode 100644
index 0000000000..125dc883f0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/extensible.js
@@ -0,0 +1,15 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.now.plaindatetimeiso
+description: Temporal.Now.plainDateTimeISO is extensible.
+features: [Temporal]
+---*/
+
+assert(
+ Object.isExtensible(Temporal.Now.plainDateTimeISO),
+ 'Object.isExtensible(Temporal.Now.plainDateTimeISO) must return true'
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/length.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/length.js
new file mode 100644
index 0000000000..eb5af27fe3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/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.now.plaindatetimeiso
+description: The `length` property of Temporal.Now.plainDateTimeISO
+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.Now.plainDateTimeISO, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/name.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/name.js
new file mode 100644
index 0000000000..df33e9b16a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/name.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.now.plainDateTimeISO
+description: Temporal.Now.plainDateTimeISO.name is "plainDateTimeISO".
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ Temporal.Now.plainDateTimeISO.name,
+ 'plainDateTimeISO',
+ 'The value of Temporal.Now.plainDateTimeISO.name is expected to be "plainDateTimeISO"'
+);
+
+verifyProperty(Temporal.Now.plainDateTimeISO, 'name', {
+ enumerable: false,
+ writable: false,
+ configurable: true
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/not-a-constructor.js
new file mode 100644
index 0000000000..68b4cac602
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/not-a-constructor.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.now.plaindatetimeiso
+description: Temporal.Now.plainDateTimeISO does not implement [[Construct]]
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal, arrow-function]
+---*/
+
+assert.sameValue(isConstructor(Temporal.Now.plainDateTimeISO), false, 'isConstructor(Temporal.Now.plainDateTimeISO) must return false');
+
+assert.throws(TypeError, () => {
+ new Temporal.Now.plainDateTimeISO();
+}, 'new Temporal.Now.plainDateTimeISO() throws a TypeError exception');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/prop-desc.js
new file mode 100644
index 0000000000..f6ffe7b035
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/prop-desc.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.now.plaindatetimeiso
+description: The "plainDateTimeISO" property of Temporal.Now
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(typeof Temporal.Now.plainDateTimeISO, "function", "typeof is function");
+
+verifyProperty(Temporal.Now, 'plainDateTimeISO', {
+ enumerable: false,
+ writable: true,
+ configurable: true
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/return-value-calendar.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/return-value-calendar.js
new file mode 100644
index 0000000000..70ad889384
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/return-value-calendar.js
@@ -0,0 +1,14 @@
+// |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.now.plaindatetimeiso
+description: Temporal.Now.plainDateTimeISO is extensible.
+features: [Temporal]
+---*/
+
+const result = Temporal.Now.plainDateTimeISO();
+assert.sameValue(result.getISOFields().calendar, "iso8601", "calendar slot should store a string");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/return-value.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/return-value.js
new file mode 100644
index 0000000000..079d644a6f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/return-value.js
@@ -0,0 +1,23 @@
+// |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.now.plaindatetimeiso
+description: Return value describes the start of a day
+features: [BigInt, Temporal]
+---*/
+const timeZone = {
+ id: 'Etc/Test',
+ getPossibleInstantsFor() { return []; },
+ getOffsetNanosecondsFor(instant) {
+ return -Number(instant.epochNanoseconds % 86400000000000n);
+ }
+};
+
+const result = Temporal.Now.plainDateTimeISO(timeZone);
+
+for (const property of ['hour', 'minute', 'second', 'millisecond', 'microsecond', 'nanosecond']) {
+ assert.sameValue(result[property], 0, 'The value of result[property] is expected to be 0');
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/shell.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/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/Now/plainDateTimeISO/time-zone-undefined.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/time-zone-undefined.js
new file mode 100644
index 0000000000..6109001fb7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/time-zone-undefined.js
@@ -0,0 +1,38 @@
+// |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.now.plaindatetimeiso
+description: Functions when time zone argument is omitted
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const expected = [];
+
+Object.defineProperty(Temporal.TimeZone, "from", {
+ get() {
+ actual.push("get Temporal.TimeZone.from");
+ return undefined;
+ },
+});
+
+const resultExplicit = Temporal.Now.plainDateTimeISO(undefined);
+assert(
+ resultExplicit instanceof Temporal.PlainDateTime,
+ 'The result of evaluating (resultExplicit instanceof Temporal.PlainDateTime) is expected to be true'
+);
+
+assert.compareArray(actual, expected, 'The value of actual is expected to equal the value of expected');
+
+const resultImplicit = Temporal.Now.plainDateTimeISO();
+assert(
+ resultImplicit instanceof Temporal.PlainDateTime,
+ 'The result of evaluating (resultImplicit instanceof Temporal.PlainDateTime) is expected to be true'
+);
+
+assert.compareArray(actual, expected, 'The value of actual is expected to equal the value of expected');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/timezone-getoffsetnanosecondsfor-invocation.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/timezone-getoffsetnanosecondsfor-invocation.js
new file mode 100644
index 0000000000..9ed1473ec7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/timezone-getoffsetnanosecondsfor-invocation.js
@@ -0,0 +1,33 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.now.plaindatetimeiso
+description: Correctly invokes `getOffsetNanosecondsFor` method of TimeZone-like objects
+features: [Temporal]
+---*/
+
+var calls = [];
+var timeZone = {
+ id: 'Etc/Test',
+ getPossibleInstantsFor() { return []; },
+ getOffsetNanosecondsFor: function() {
+ calls.push({
+ args: arguments,
+ this: this
+ });
+ return 0;
+ },
+};
+
+Temporal.Now.plainDateTimeISO(timeZone);
+
+assert.sameValue(calls.length, 1, 'The value of calls.length is expected to be 1');
+assert.sameValue(calls[0].args.length, 1, 'The value of calls[0].args.length is expected to be 1');
+assert(
+ calls[0].args[0] instanceof Temporal.Instant,
+ 'The result of evaluating (calls[0].args[0] instanceof Temporal.Instant) is expected to be true'
+);
+assert.sameValue(calls[0].this, timeZone, 'The value of calls[0].this is expected to equal the value of timeZone');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..e79a3a974c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/timezone-getoffsetnanosecondsfor-non-integer.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.now.plaindatetimeiso
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal, arrow-function]
+includes: [temporalHelpers.js]
+---*/
+[3600000000000.5, NaN].forEach(wrongOffset => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+
+ assert.throws(
+ RangeError,
+ () => Temporal.Now.plainDateTimeISO(timeZone),
+ 'Temporal.Now.plainDateTimeISO(timeZone) throws a RangeError exception'
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/timezone-getoffsetnanosecondsfor-non-method.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/timezone-getoffsetnanosecondsfor-non-method.js
new file mode 100644
index 0000000000..e0fd1c15a2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/timezone-getoffsetnanosecondsfor-non-method.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.now.plaindatetimeiso
+description: Rejects when `getOffsetNanosecondsFor` property is not a method
+features: [Temporal]
+---*/
+
+var timeZone = {
+ getOffsetNanosecondsFor: 7
+};
+
+assert.throws(TypeError, function() {
+ Temporal.Now.plainDateTimeISO(timeZone);
+}, 'Temporal.Now.plainDateTimeISO(timeZone) throws a TypeError exception');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/timezone-getoffsetnanosecondsfor-not-a-number.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/timezone-getoffsetnanosecondsfor-not-a-number.js
new file mode 100644
index 0000000000..1f268d1246
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/timezone-getoffsetnanosecondsfor-not-a-number.js
@@ -0,0 +1,42 @@
+// |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.now.plaindatetimeiso
+description: Rejects non-numeric nanosecond values reported by TimeZone-like object
+features: [BigInt, Symbol, Temporal, arrow-function]
+---*/
+const invalidValues = [
+ undefined,
+ null,
+ true,
+ '2020-01-01T12:45:36',
+ Symbol(),
+ 2n,
+ {},
+ Temporal.PlainDateTime,
+ Temporal.PlainDateTime.prototype
+];
+
+for (const dateTime of invalidValues) {
+ let callCount = 0;
+
+ const timeZone = {
+ id: 'Etc/Test',
+ getPossibleInstantsFor() { return []; },
+ getOffsetNanosecondsFor(instant, calendar) {
+ callCount += 1;
+ return dateTime;
+ }
+ };
+
+ assert.throws(
+ TypeError,
+ () => Temporal.Now.plainDateTimeISO(timeZone),
+ 'Temporal.Now.plainDateTimeISO(timeZone) throws a TypeError exception'
+ );
+
+ assert.sameValue(callCount, 1, 'The value of callCount is expected to be 1');
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..c0f9cf8941
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/timezone-getoffsetnanosecondsfor-not-callable.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.now.plaindatetimeiso
+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");
+
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => Temporal.Now.plainDateTimeISO(timeZone),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..1a2800336a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/timezone-getoffsetnanosecondsfor-out-of-range.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.now.plaindatetimeiso
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal, arrow-function]
+includes: [temporalHelpers.js]
+---*/
+[-86400000000001, 86400000000001, -Infinity, Infinity].forEach(wrongOffset => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+
+ assert.throws(
+ RangeError,
+ () => Temporal.Now.plainDateTimeISO(timeZone),
+ 'Temporal.Now.plainDateTimeISO(timeZone) throws a RangeError exception'
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/timezone-getoffsetnanosecondsfor-poisoned.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/timezone-getoffsetnanosecondsfor-poisoned.js
new file mode 100644
index 0000000000..6713fba519
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/timezone-getoffsetnanosecondsfor-poisoned.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.now.plaindatetimeiso
+description: Forwards error when accessing `getOffsetNanosecondsFor` property throws
+features: [Temporal]
+---*/
+
+var timeZone = {
+ id: 'Etc/Test',
+ getPossibleInstantsFor() { return []; },
+ get getOffsetNanosecondsFor() {
+ throw new Test262Error();
+ }
+};
+
+assert.throws(Test262Error, function() {
+ Temporal.Now.plainDateTimeISO(timeZone);
+}, 'Temporal.Now.plainDateTimeISO(timeZone) throws a Test262Error exception');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/timezone-getoffsetnanosecondsfor-throws.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/timezone-getoffsetnanosecondsfor-throws.js
new file mode 100644
index 0000000000..42b4bdf183
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/timezone-getoffsetnanosecondsfor-throws.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.now.plaindatetimeiso
+description: Forwards error when `getOffsetNanosecondsFor` throws
+features: [Temporal]
+---*/
+
+var timeZone = {
+ id: 'Etc/Test',
+ getPossibleInstantsFor() { return []; },
+ getOffsetNanosecondsFor() {
+ throw new Test262Error();
+ }
+};
+
+assert.throws(Test262Error, function() {
+ Temporal.Now.plainDateTimeISO(timeZone);
+}, 'Temporal.Now.plainDateTimeISO(timeZone) throws a Test262Error exception');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..f40b716b15
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/timezone-getoffsetnanosecondsfor-wrong-type.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.now.plaindatetimeiso
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [BigInt, Symbol, Temporal, arrow-function]
+includes: [temporalHelpers.js]
+---*/
+[undefined, null, true, '+01:00', Symbol(), 3600000000000n, {}, {
+ valueOf() {
+ return 3600000000000;
+ }
+}].forEach(wrongOffset => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+
+ assert.throws(
+ TypeError,
+ () => Temporal.Now.plainDateTimeISO(timeZone),
+ 'Temporal.Now.plainDateTimeISO(timeZone) throws a TypeError exception'
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/timezone-object.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/timezone-object.js
new file mode 100644
index 0000000000..420fbc2aca
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/timezone-object.js
@@ -0,0 +1,42 @@
+// |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.now.plaindatetimeiso
+description: Observable interactions with the provided timezone-like object
+includes: [compareArray.js, temporalHelpers.js]
+features: [BigInt, Proxy, Temporal]
+---*/
+const actual = [];
+
+const expected = [
+ 'has timeZone.getOffsetNanosecondsFor',
+ 'has timeZone.getPossibleInstantsFor',
+ 'has timeZone.id',
+ 'get timeZone.getOffsetNanosecondsFor',
+ 'call timeZone.getOffsetNanosecondsFor'
+];
+
+const timeZone = TemporalHelpers.timeZoneObserver(actual, "timeZone", {
+ getOffsetNanosecondsFor(instant) {
+ assert.sameValue(
+ instant instanceof Temporal.Instant,
+ true,
+ 'The result of evaluating (instant instanceof Temporal.Instant) is expected to be true'
+ );
+
+ return -Number(instant.epochNanoseconds % 86400000000000n);
+ }
+});
+
+Object.defineProperty(Temporal.TimeZone, 'from', {
+ get() {
+ actual.push('get Temporal.TimeZone.from');
+ return undefined;
+ }
+});
+
+Temporal.Now.plainDateTimeISO(timeZone);
+assert.compareArray(actual, expected, 'The value of actual is expected to equal the value of expected');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/timezone-string-datetime.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/timezone-string-datetime.js
new file mode 100644
index 0000000000..e656427eac
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/timezone-string-datetime.js
@@ -0,0 +1,64 @@
+// |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.now.plaindatetimeiso
+description: Conversion of ISO date-time strings to Temporal.TimeZone instances
+features: [Temporal]
+---*/
+
+let timeZone = "2021-08-19T17:30";
+assert.throws(RangeError, () => Temporal.Now.plainDateTimeISO(timeZone), "bare date-time string is not a time zone");
+
+[
+ "2021-08-19T17:30-07:00:01",
+ "2021-08-19T17:30-07:00:00",
+ "2021-08-19T17:30-07:00:00.1",
+ "2021-08-19T17:30-07:00:00.0",
+ "2021-08-19T17:30-07:00:00.01",
+ "2021-08-19T17:30-07:00:00.00",
+ "2021-08-19T17:30-07:00:00.001",
+ "2021-08-19T17:30-07:00:00.000",
+ "2021-08-19T17:30-07:00:00.0001",
+ "2021-08-19T17:30-07:00:00.0000",
+ "2021-08-19T17:30-07:00:00.00001",
+ "2021-08-19T17:30-07:00:00.00000",
+ "2021-08-19T17:30-07:00:00.000001",
+ "2021-08-19T17:30-07:00:00.000000",
+ "2021-08-19T17:30-07:00:00.0000001",
+ "2021-08-19T17:30-07:00:00.0000000",
+ "2021-08-19T17:30-07:00:00.00000001",
+ "2021-08-19T17:30-07:00:00.00000000",
+ "2021-08-19T17:30-07:00:00.000000001",
+ "2021-08-19T17:30-07:00:00.000000000",
+].forEach((timeZone) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.Now.plainDateTimeISO(timeZone),
+ `ISO string ${timeZone} with a sub-minute offset is not a valid time zone`
+ );
+});
+
+// The following are all valid strings so should not throw:
+
+[
+ "2021-08-19T17:30Z",
+ "2021-08-19T1730Z",
+ "2021-08-19T17:30-07:00",
+ "2021-08-19T1730-07:00",
+ "2021-08-19T17:30-0700",
+ "2021-08-19T1730-0700",
+ "2021-08-19T17:30[UTC]",
+ "2021-08-19T1730[UTC]",
+ "2021-08-19T17:30Z[UTC]",
+ "2021-08-19T1730Z[UTC]",
+ "2021-08-19T17:30-07:00[UTC]",
+ "2021-08-19T1730-07:00[UTC]",
+ "2021-08-19T17:30-0700[UTC]",
+ "2021-08-19T1730-0700[UTC]",
+].forEach((timeZone) => {
+ Temporal.Now.plainDateTimeISO(timeZone);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/timezone-string-leap-second.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/timezone-string-leap-second.js
new file mode 100644
index 0000000000..71d5d98510
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/timezone-string-leap-second.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.now.plaindatetimeiso
+description: Leap second is a valid ISO string for TimeZone
+features: [Temporal]
+---*/
+
+let timeZone = "2016-12-31T23:59:60+00:00[UTC]";
+
+// A string with a leap second is a valid ISO string, so the following
+// operation should not throw
+
+Temporal.Now.plainDateTimeISO(timeZone);
+
+timeZone = "2021-08-19T17:30:45.123456789+23:59[+23:59:60]";
+assert.throws(RangeError, () => Temporal.Now.plainDateTimeISO(timeZone), "leap second in time zone name not valid");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/timezone-string-year-zero.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/timezone-string-year-zero.js
new file mode 100644
index 0000000000..5bcb8c9673
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/timezone-string-year-zero.js
@@ -0,0 +1,23 @@
+// |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.now.plaindatetimeiso
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+invalidStrings.forEach((timeZone) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.Now.plainDateTimeISO(timeZone),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/timezone-string.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/timezone-string.js
new file mode 100644
index 0000000000..cfcbd648e6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/timezone-string.js
@@ -0,0 +1,38 @@
+// |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.now.plaindatetimeiso
+description: Time zone IDs are valid input for a time zone
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getPossibleInstantsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getPossibleInstantsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be looked up");
+ },
+});
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+
+// The following are all valid strings so should not throw:
+
+["UTC", "+01:00"].forEach((timeZone) => {
+ Temporal.Now.plainDateTimeISO(timeZone);
+});
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", getPossibleInstantsForOriginal);
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/timezone-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/timezone-wrong-type.js
new file mode 100644
index 0000000000..bd7bc0b4b9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainDateTimeISO/timezone-wrong-type.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.now.plaindatetimeiso
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or object for TimeZone
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [19761118, "number that would convert to a valid ISO string in other contexts"],
+ [1n, "bigint"],
+];
+
+for (const [timeZone, description] of primitiveTests) {
+ assert.throws(
+ typeof timeZone === 'string' ? RangeError : TypeError,
+ () => Temporal.Now.plainDateTimeISO(timeZone),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "object not implementing time zone protocol"],
+ [new Temporal.Calendar("iso8601"), "calendar instance"],
+];
+
+for (const [timeZone, description] of typeErrorTests) {
+ assert.throws(TypeError, () => Temporal.Now.plainDateTimeISO(timeZone), `${description} is not a valid object and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainTimeISO/browser.js b/js/src/tests/test262/built-ins/Temporal/Now/plainTimeISO/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainTimeISO/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainTimeISO/length.js b/js/src/tests/test262/built-ins/Temporal/Now/plainTimeISO/length.js
new file mode 100644
index 0000000000..781dac2fdc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainTimeISO/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.now.plaintimeiso
+description: Temporal.Now.plainTimeISO.length is 0
+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.Now.plainTimeISO, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainTimeISO/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Now/plainTimeISO/prop-desc.js
new file mode 100644
index 0000000000..9502b67dbf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainTimeISO/prop-desc.js
@@ -0,0 +1,20 @@
+// |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.now.plaintimeiso
+description: The "plainTimeISO" property of Temporal.Now
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(typeof Temporal.Now.plainTimeISO, "function", "typeof is function");
+
+verifyProperty(Temporal.Now, "plainTimeISO", {
+ enumerable: false,
+ writable: true,
+ configurable: true
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainTimeISO/return-value.js b/js/src/tests/test262/built-ins/Temporal/Now/plainTimeISO/return-value.js
new file mode 100644
index 0000000000..1743820216
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainTimeISO/return-value.js
@@ -0,0 +1,14 @@
+// |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.now.plaintimeiso
+description: Functions when time zone argument is omitted
+features: [Temporal]
+---*/
+
+const t = Temporal.Now.plainTimeISO();
+assert(t instanceof Temporal.PlainTime);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainTimeISO/shell.js b/js/src/tests/test262/built-ins/Temporal/Now/plainTimeISO/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainTimeISO/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainTimeISO/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/Now/plainTimeISO/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..2b16192689
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainTimeISO/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,18 @@
+// |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.now.plaintimeiso
+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].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+
+ assert.throws(RangeError, () => Temporal.Now.plainTimeISO(timeZone));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainTimeISO/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/Now/plainTimeISO/timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..f0f67023fb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainTimeISO/timezone-getoffsetnanosecondsfor-not-callable.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.now.plaintimeiso
+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");
+
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => Temporal.Now.plainTimeISO(timeZone),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainTimeISO/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/Now/plainTimeISO/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..7306c614a8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainTimeISO/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,18 @@
+// |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.now.plaintimeiso
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+
+ assert.throws(RangeError, () => Temporal.Now.plainTimeISO(timeZone));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainTimeISO/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Now/plainTimeISO/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..fac91302c3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainTimeISO/timezone-getoffsetnanosecondsfor-wrong-type.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.now.plaintimeiso
+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);
+
+ assert.throws(TypeError, () => Temporal.Now.plainTimeISO(timeZone));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainTimeISO/timezone-string-datetime.js b/js/src/tests/test262/built-ins/Temporal/Now/plainTimeISO/timezone-string-datetime.js
new file mode 100644
index 0000000000..1134064f63
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainTimeISO/timezone-string-datetime.js
@@ -0,0 +1,64 @@
+// |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.now.plaintimeiso
+description: Conversion of ISO date-time strings to Temporal.TimeZone instances
+features: [Temporal]
+---*/
+
+let timeZone = "2021-08-19T17:30";
+assert.throws(RangeError, () => Temporal.Now.plainTimeISO(timeZone), "bare date-time string is not a time zone");
+
+[
+ "2021-08-19T17:30-07:00:01",
+ "2021-08-19T17:30-07:00:00",
+ "2021-08-19T17:30-07:00:00.1",
+ "2021-08-19T17:30-07:00:00.0",
+ "2021-08-19T17:30-07:00:00.01",
+ "2021-08-19T17:30-07:00:00.00",
+ "2021-08-19T17:30-07:00:00.001",
+ "2021-08-19T17:30-07:00:00.000",
+ "2021-08-19T17:30-07:00:00.0001",
+ "2021-08-19T17:30-07:00:00.0000",
+ "2021-08-19T17:30-07:00:00.00001",
+ "2021-08-19T17:30-07:00:00.00000",
+ "2021-08-19T17:30-07:00:00.000001",
+ "2021-08-19T17:30-07:00:00.000000",
+ "2021-08-19T17:30-07:00:00.0000001",
+ "2021-08-19T17:30-07:00:00.0000000",
+ "2021-08-19T17:30-07:00:00.00000001",
+ "2021-08-19T17:30-07:00:00.00000000",
+ "2021-08-19T17:30-07:00:00.000000001",
+ "2021-08-19T17:30-07:00:00.000000000",
+].forEach((timeZone) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.Now.plainTimeISO(timeZone),
+ `ISO string ${timeZone} with a sub-minute offset is not a valid time zone`
+ );
+});
+
+// The following are all valid strings so should not throw:
+
+[
+ "2021-08-19T17:30Z",
+ "2021-08-19T1730Z",
+ "2021-08-19T17:30-07:00",
+ "2021-08-19T1730-07:00",
+ "2021-08-19T17:30-0700",
+ "2021-08-19T1730-0700",
+ "2021-08-19T17:30[UTC]",
+ "2021-08-19T1730[UTC]",
+ "2021-08-19T17:30Z[UTC]",
+ "2021-08-19T1730Z[UTC]",
+ "2021-08-19T17:30-07:00[UTC]",
+ "2021-08-19T1730-07:00[UTC]",
+ "2021-08-19T17:30-0700[UTC]",
+ "2021-08-19T1730-0700[UTC]",
+].forEach((timeZone) => {
+ Temporal.Now.plainTimeISO(timeZone);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainTimeISO/timezone-string-leap-second.js b/js/src/tests/test262/built-ins/Temporal/Now/plainTimeISO/timezone-string-leap-second.js
new file mode 100644
index 0000000000..6f0e59f8db
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainTimeISO/timezone-string-leap-second.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.now.plaintimeiso
+description: Leap second is a valid ISO string for TimeZone
+features: [Temporal]
+---*/
+
+let timeZone = "2016-12-31T23:59:60+00:00[UTC]";
+
+// A string with a leap second is a valid ISO string, so the following
+// operation should not throw
+
+Temporal.Now.plainTimeISO(timeZone);
+
+timeZone = "2021-08-19T17:30:45.123456789+23:59[+23:59:60]";
+assert.throws(RangeError, () => Temporal.Now.plainTimeISO(timeZone), "leap second in time zone name not valid");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainTimeISO/timezone-string-year-zero.js b/js/src/tests/test262/built-ins/Temporal/Now/plainTimeISO/timezone-string-year-zero.js
new file mode 100644
index 0000000000..52b7928f69
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainTimeISO/timezone-string-year-zero.js
@@ -0,0 +1,23 @@
+// |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.now.plaintimeiso
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+invalidStrings.forEach((timeZone) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.Now.plainTimeISO(timeZone),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainTimeISO/timezone-string.js b/js/src/tests/test262/built-ins/Temporal/Now/plainTimeISO/timezone-string.js
new file mode 100644
index 0000000000..521c638bf6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainTimeISO/timezone-string.js
@@ -0,0 +1,38 @@
+// |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.now.plaintimeiso
+description: Time zone IDs are valid input for a time zone
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getPossibleInstantsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getPossibleInstantsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be looked up");
+ },
+});
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+
+// The following are all valid strings so should not throw:
+
+["UTC", "+01:00"].forEach((timeZone) => {
+ Temporal.Now.plainTimeISO(timeZone);
+});
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", getPossibleInstantsForOriginal);
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainTimeISO/timezone-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Now/plainTimeISO/timezone-wrong-type.js
new file mode 100644
index 0000000000..6c46aa7bb4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainTimeISO/timezone-wrong-type.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.now.plaintimeiso
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or object for TimeZone
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [19761118, "number that would convert to a valid ISO string in other contexts"],
+ [1n, "bigint"],
+];
+
+for (const [timeZone, description] of primitiveTests) {
+ assert.throws(
+ typeof timeZone === 'string' ? RangeError : TypeError,
+ () => Temporal.Now.plainTimeISO(timeZone),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "object not implementing time zone protocol"],
+ [new Temporal.Calendar("iso8601"), "calendar instance"],
+];
+
+for (const [timeZone, description] of typeErrorTests) {
+ assert.throws(TypeError, () => Temporal.Now.plainTimeISO(timeZone), `${description} is not a valid object and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainTimeISO/timezone.js b/js/src/tests/test262/built-ins/Temporal/Now/plainTimeISO/timezone.js
new file mode 100644
index 0000000000..b71010f6b6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainTimeISO/timezone.js
@@ -0,0 +1,36 @@
+// |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.now.plaintimeiso
+description: The value returned by TimeZone.getOffsetNanosecondsFor affects the result
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const expected = [
+ "has timeZone.getOffsetNanosecondsFor",
+ "has timeZone.getPossibleInstantsFor",
+ "has timeZone.id",
+ "get timeZone.getOffsetNanosecondsFor",
+ "call timeZone.getOffsetNanosecondsFor",
+];
+
+const timeZone = TemporalHelpers.timeZoneObserver(actual, "timeZone", {
+ getOffsetNanosecondsFor(instant) {
+ assert.sameValue(instant instanceof Temporal.Instant, true, "Instant");
+ return -Number(instant.epochNanoseconds % 86400_000_000_000n);
+ },
+});
+
+const result = Temporal.Now.plainTimeISO(timeZone);
+assert.sameValue(result instanceof Temporal.PlainTime, true);
+for (const property of ["hour", "minute", "second", "millisecond", "microsecond", "nanosecond"]) {
+ assert.sameValue(result[property], 0, property);
+}
+
+assert.compareArray(actual, expected);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/plainTimeISO/toPlainTime-override.js b/js/src/tests/test262/built-ins/Temporal/Now/plainTimeISO/toPlainTime-override.js
new file mode 100644
index 0000000000..bfebad4fc2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/plainTimeISO/toPlainTime-override.js
@@ -0,0 +1,45 @@
+// |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.now.plaintimeiso
+description: PlainDateTime.toPlainTime is not observably called
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const expected = [
+ "has timeZone.getOffsetNanosecondsFor",
+ "has timeZone.getPossibleInstantsFor",
+ "has timeZone.id",
+ "get timeZone.getOffsetNanosecondsFor",
+ "call timeZone.getOffsetNanosecondsFor",
+];
+
+Object.defineProperty(Temporal.PlainDateTime.prototype, "toPlainTime", {
+ get() {
+ actual.push("get Temporal.PlainDateTime.prototype.toPlainTime");
+ return function() {
+ actual.push("call Temporal.PlainDateTime.prototype.toPlainTime");
+ };
+ },
+});
+
+const timeZone = TemporalHelpers.timeZoneObserver(actual, "timeZone", {
+ getOffsetNanosecondsFor(instant) {
+ assert.sameValue(instant instanceof Temporal.Instant, true, "Instant");
+ return -Number(instant.epochNanoseconds % 86400_000_000_000n);
+ },
+});
+
+const result = Temporal.Now.plainTimeISO(timeZone);
+assert.sameValue(result instanceof Temporal.PlainTime, true);
+for (const property of ["hour", "minute", "second", "millisecond", "microsecond", "nanosecond"]) {
+ assert.sameValue(result[property], 0, property);
+}
+
+assert.compareArray(actual, expected);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Now/prop-desc.js
new file mode 100644
index 0000000000..1173b5d786
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/prop-desc.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.now
+description: The "Now" property of Temporal
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Now,
+ "object",
+ "`typeof Now` is `object`"
+);
+
+verifyProperty(Temporal, "Now", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/shell.js b/js/src/tests/test262/built-ins/Temporal/Now/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/timeZoneId/browser.js b/js/src/tests/test262/built-ins/Temporal/Now/timeZoneId/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/timeZoneId/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/timeZoneId/extensible.js b/js/src/tests/test262/built-ins/Temporal/Now/timeZoneId/extensible.js
new file mode 100644
index 0000000000..4f7b3fd2d9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/timeZoneId/extensible.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.now.timezoneid
+description: Temporal.Now.timeZoneId is extensible.
+info: |
+ ## 17 ECMAScript Standard Built-in Objects
+
+ Unless specified otherwise, the [[Extensible]] internal slot
+ of a built-in object initially has the value true.
+features: [Temporal]
+---*/
+
+assert(
+ Object.isExtensible(Temporal.Now.timeZoneId),
+ 'Object.isExtensible(Temporal.Now.timeZoneId) must return true'
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/timeZoneId/length.js b/js/src/tests/test262/built-ins/Temporal/Now/timeZoneId/length.js
new file mode 100644
index 0000000000..d9a74eb06c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/timeZoneId/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.now.timezoneid
+description: Temporal.Now.timeZoneId.length is 0
+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.Now.timeZoneId, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/timeZoneId/name.js b/js/src/tests/test262/built-ins/Temporal/Now/timeZoneId/name.js
new file mode 100644
index 0000000000..3f7e35a7f3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/timeZoneId/name.js
@@ -0,0 +1,33 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.now.timezoneid
+description: Temporal.Now.timeZoneId.name is "timeZoneId".
+info: |
+ ## 17 ECMAScript Standard Built-in Objects:
+ 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, 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]
+---*/
+
+assert.sameValue(
+ Temporal.Now.timeZoneId.name,
+ 'timeZoneId',
+ 'The value of Temporal.Now.timeZoneId.name is expected to be "timeZoneId"'
+);
+
+verifyProperty(Temporal.Now.timeZoneId, 'name', {
+ enumerable: false,
+ writable: false,
+ configurable: true
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/timeZoneId/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Now/timeZoneId/not-a-constructor.js
new file mode 100644
index 0000000000..d1e25fa235
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/timeZoneId/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.now.timezoneid
+description: Temporal.Now.timeZoneId does not implement [[Construct]]
+info: |
+ ECMAScript Function Objects
+
+ 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, arrow-function]
+---*/
+
+assert.sameValue(isConstructor(Temporal.Now.timeZoneId), false, 'isConstructor(Temporal.Now.timeZoneId) must return false');
+
+assert.throws(TypeError, () => {
+ new Temporal.Now.timeZoneId();
+}, 'new Temporal.Now.timeZoneId() throws a TypeError exception');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/timeZoneId/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Now/timeZoneId/prop-desc.js
new file mode 100644
index 0000000000..821185d2d3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/timeZoneId/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.now.timezoneid
+description: The "timeZoneId" property of Temporal.Now
+info: |
+ Section 17: Every other data property described in clauses 18 through 26
+ and in Annex B.2 has the attributes { [[Writable]]: true,
+ [[Enumerable]]: false, [[Configurable]]: true } unless otherwise specified.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(typeof Temporal.Now.timeZoneId, "function", "typeof is function");
+
+verifyProperty(Temporal.Now, 'timeZoneId', {
+ enumerable: false,
+ writable: true,
+ configurable: true
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/timeZoneId/return-value.js b/js/src/tests/test262/built-ins/Temporal/Now/timeZoneId/return-value.js
new file mode 100644
index 0000000000..3e6f866611
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/timeZoneId/return-value.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.now.timezoneid
+description: Temporal.Now.timeZoneId returns a string
+info: |
+ 1. Return DefaultTimeZone().
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Now.timeZoneId(),
+ "string",
+ "Temporal.Now.timeZoneId() returns a string"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/timeZoneId/shell.js b/js/src/tests/test262/built-ins/Temporal/Now/timeZoneId/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/timeZoneId/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/Now/toStringTag/browser.js b/js/src/tests/test262/built-ins/Temporal/Now/toStringTag/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/toStringTag/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/toStringTag/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Now/toStringTag/prop-desc.js
new file mode 100644
index 0000000000..1740cc9de2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/toStringTag/prop-desc.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-now-@@tostringtag
+description: The @@toStringTag property of Temporal.Now
+includes: [propertyHelper.js]
+features: [Symbol.toStringTag, Temporal]
+---*/
+
+verifyProperty(Temporal.Now, Symbol.toStringTag, {
+ value: "Temporal.Now",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/toStringTag/shell.js b/js/src/tests/test262/built-ins/Temporal/Now/toStringTag/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/toStringTag/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/toStringTag/string.js b/js/src/tests/test262/built-ins/Temporal/Now/toStringTag/string.js
new file mode 100644
index 0000000000..3d19121f69
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/toStringTag/string.js
@@ -0,0 +1,13 @@
+// |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-now-@@tostringtag
+description: The @@toStringTag property of Temporal.Now produces the correct value in toString
+features: [Symbol.toStringTag, Temporal]
+---*/
+
+assert.sameValue(String(Temporal.Now), "[object Temporal.Now]");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/browser.js b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/calendar-case-insensitive.js
new file mode 100644
index 0000000000..60a4d85a16
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/calendar-case-insensitive.js
@@ -0,0 +1,16 @@
+// |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.now.zoneddatetime
+description: Calendar names are case-insensitive
+features: [Temporal]
+---*/
+
+const arg = "iSo8601";
+
+const result = Temporal.Now.zonedDateTime(arg);
+assert.sameValue(result.calendarId, "iso8601", "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/calendar-function.js b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/calendar-function.js
new file mode 100644
index 0000000000..091fb37fd7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/calendar-function.js
@@ -0,0 +1,61 @@
+// |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.now.zoneddatetime
+description: Behavior when provided calendar value is a function
+includes: [compareArray.js, temporalHelpers.js]
+features: [BigInt, Proxy, Temporal]
+---*/
+const actual = [];
+const expected = [
+ "has timeZone.getOffsetNanosecondsFor",
+ "has timeZone.getPossibleInstantsFor",
+ "has timeZone.id",
+];
+
+const calendar = function() {};
+calendar.dateAdd = () => {};
+calendar.dateFromFields = () => {};
+calendar.dateUntil = () => {};
+calendar.day = () => {};
+calendar.dayOfWeek = () => {};
+calendar.dayOfYear = () => {};
+calendar.daysInMonth = () => {};
+calendar.daysInWeek = () => {};
+calendar.daysInYear = () => {};
+calendar.fields = () => {};
+calendar.id = "test-calendar";
+calendar.inLeapYear = () => {};
+calendar.mergeFields = () => {};
+calendar.month = () => {};
+calendar.monthCode = () => {};
+calendar.monthDayFromFields = () => {};
+calendar.monthsInYear = () => {};
+calendar.weekOfYear = () => {};
+calendar.year = () => {};
+calendar.yearMonthFromFields = () => {};
+calendar.yearOfWeek = () => {};
+
+const timeZone = TemporalHelpers.timeZoneObserver(actual, "timeZone", {
+ getOffsetNanosecondsFor(instant) {
+ return -Number(instant.epochNanoseconds % 86400000000000n);
+ }
+});
+
+Object.defineProperty(Temporal.Calendar, 'from', {
+ get() {
+ actual.push('get Temporal.Calendar.from');
+ return undefined;
+ }
+});
+
+const result = Temporal.Now.zonedDateTime(calendar, timeZone);
+
+assert.compareArray(actual, expected, 'order of observable operations');
+
+for (const property of ['hour', 'minute', 'second', 'millisecond', 'microsecond', 'nanosecond']) {
+ assert.sameValue(result[property], 0, 'The value of result[property] is expected to be 0');
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/calendar-number.js b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/calendar-number.js
new file mode 100644
index 0000000000..7ca0b1f8fc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/calendar-number.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.now.zoneddatetime
+description: A number is not allowed to be a calendar
+features: [Temporal]
+---*/
+
+const numbers = [
+ 1,
+ -19761118,
+ 19761118,
+ 1234567890,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => Temporal.Now.zonedDateTime(arg),
+ "A number is not a valid ISO string for Calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/calendar-object.js b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/calendar-object.js
new file mode 100644
index 0000000000..8f475c312e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/calendar-object.js
@@ -0,0 +1,52 @@
+// |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.now.zoneddatetime
+description: Observable interactions with the provided calendar-like object
+includes: [compareArray.js, temporalHelpers.js]
+features: [Proxy, Temporal]
+---*/
+
+const actual = [];
+const expected = [
+ "has calendar.dateAdd",
+ "has calendar.dateFromFields",
+ "has calendar.dateUntil",
+ "has calendar.day",
+ "has calendar.dayOfWeek",
+ "has calendar.dayOfYear",
+ "has calendar.daysInMonth",
+ "has calendar.daysInWeek",
+ "has calendar.daysInYear",
+ "has calendar.fields",
+ "has calendar.id",
+ "has calendar.inLeapYear",
+ "has calendar.mergeFields",
+ "has calendar.month",
+ "has calendar.monthCode",
+ "has calendar.monthDayFromFields",
+ "has calendar.monthsInYear",
+ "has calendar.weekOfYear",
+ "has calendar.year",
+ "has calendar.yearMonthFromFields",
+ "has calendar.yearOfWeek",
+];
+
+const calendar = TemporalHelpers.calendarObserver(actual, "calendar", {
+ toString: "iso8601",
+});
+
+Object.defineProperty(Temporal.Calendar, 'from', {
+ get() {
+ actual.push('get Temporal.Calendar.from');
+ return undefined;
+ },
+});
+
+Temporal.Now.zonedDateTime(calendar);
+
+assert.compareArray(actual, expected, 'order of observable operations');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/calendar-string-leap-second.js b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/calendar-string-leap-second.js
new file mode 100644
index 0000000000..620831b191
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/calendar-string-leap-second.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.now.zoneddatetime
+description: Leap second is a valid ISO string for Calendar
+features: [Temporal]
+---*/
+
+const arg = "2016-12-31T23:59:60";
+const result = Temporal.Now.zonedDateTime(arg);
+assert.sameValue(
+ result.calendarId,
+ "iso8601",
+ "leap second is a valid ISO string for Calendar"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/calendar-string.js b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/calendar-string.js
new file mode 100644
index 0000000000..d71b32a43c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/calendar-string.js
@@ -0,0 +1,16 @@
+// |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.now.zoneddatetime
+description: A calendar ID is valid input for Calendar
+features: [Temporal]
+---*/
+
+const arg = "iso8601";
+
+const result = Temporal.Now.zonedDateTime(arg);
+assert.sameValue(result.getISOFields().calendar, "iso8601", `Calendar created from string "${arg}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/calendar-temporal-object.js
new file mode 100644
index 0000000000..c164246dd7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/calendar-temporal-object.js
@@ -0,0 +1,41 @@
+// |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.now.zoneddatetime
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal-totemporalcalendar step 1.b:
+ b. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const plainDate = new Temporal.PlainDate(2000, 5, 2);
+const plainDateTime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const plainMonthDay = new Temporal.PlainMonthDay(5, 2);
+const plainYearMonth = new Temporal.PlainYearMonth(2000, 5);
+const zonedDateTime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+
+[plainDate, plainDateTime, plainMonthDay, plainYearMonth, zonedDateTime].forEach((arg) => {
+ const actual = [];
+ const expected = [];
+
+ const calendar = arg.getISOFields().calendar;
+
+ Object.defineProperty(arg, "calendar", {
+ get() {
+ actual.push("get calendar");
+ return calendar;
+ },
+ });
+
+ const result = Temporal.Now.zonedDateTime(arg);
+ assert.sameValue(result.getISOFields().calendar, calendar, "Temporal object coerced to calendar");
+
+ assert.compareArray(actual, expected, "calendar getter not called");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/calendar-undefined.js b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/calendar-undefined.js
new file mode 100644
index 0000000000..6ae05c6ed9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/calendar-undefined.js
@@ -0,0 +1,14 @@
+// |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.now.zoneddatetime
+description: Throws when the calendar argument is undefined
+features: [Temporal]
+---*/
+
+assert.throws(TypeError, () => Temporal.Now.zonedDateTime(), "implicit");
+assert.throws(TypeError, () => Temporal.Now.zonedDateTime(undefined), "implicit");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/calendar-wrong-type.js
new file mode 100644
index 0000000000..b70f5717b5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/calendar-wrong-type.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.now.zoneddatetime
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or object for Calendar
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const primitiveTests = [
+ [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,
+ () => Temporal.Now.zonedDateTime(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => Temporal.Now.zonedDateTime(arg), `${description} is not a valid object and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/extensible.js b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/extensible.js
new file mode 100644
index 0000000000..b7a7be9c97
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/extensible.js
@@ -0,0 +1,15 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.now.zoneddatetime
+description: Temporal.Now.zonedDateTime is extensible.
+features: [Temporal]
+---*/
+
+assert(
+ Object.isExtensible(Temporal.Now.zonedDateTime),
+ 'Object.isExtensible(Temporal.Now.zonedDateTime) must return true'
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/length.js b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/length.js
new file mode 100644
index 0000000000..8924ba58b8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/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.now.zoneddatetime
+description: The `length` property of Temporal.Now.zonedDateTime
+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.Now.zonedDateTime, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/name.js b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/name.js
new file mode 100644
index 0000000000..3b2348ab08
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/name.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.now.zoneddatetime
+description: Temporal.Now.zonedDateTime.name is "zonedDateTime".
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ Temporal.Now.zonedDateTime.name,
+ 'zonedDateTime',
+ 'The value of Temporal.Now.zonedDateTime.name is expected to be "zonedDateTime"'
+);
+
+verifyProperty(Temporal.Now.zonedDateTime, 'name', {
+ enumerable: false,
+ writable: false,
+ configurable: true
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/not-a-constructor.js
new file mode 100644
index 0000000000..cfee45f600
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/not-a-constructor.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.now.zoneddatetime
+description: Temporal.Now.zonedDateTime does not implement [[Construct]]
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal, arrow-function]
+---*/
+
+assert.sameValue(isConstructor(Temporal.Now.zonedDateTime), false, 'isConstructor(Temporal.Now.zonedDateTime) must return false');
+
+assert.throws(TypeError, () => {
+ new Temporal.Now.zonedDateTime();
+}, 'new Temporal.Now.zonedDateTime() throws a TypeError exception');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/prop-desc.js
new file mode 100644
index 0000000000..d80bffb58a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/prop-desc.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.now.zoneddatetime
+description: The "zonedDateTime" property of Temporal.Now
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(typeof Temporal.Now.zonedDateTime, "function", "typeof is function");
+
+verifyProperty(Temporal.Now, 'zonedDateTime', {
+ enumerable: false,
+ writable: true,
+ configurable: true
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/shell.js b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/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/Now/zonedDateTime/time-zone-undefined.js b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/time-zone-undefined.js
new file mode 100644
index 0000000000..0bf343f597
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/time-zone-undefined.js
@@ -0,0 +1,34 @@
+// |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.now.zoneddatetime
+description: Functions when time zone argument is omitted
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const expected = [];
+
+Object.defineProperty(Temporal.TimeZone, "from", {
+ get() {
+ actual.push("get Temporal.TimeZone.from");
+ return undefined;
+ },
+});
+
+const systemTimeZone = Temporal.Now.timeZoneId();
+
+const resultExplicit = Temporal.Now.zonedDateTime('iso8601', undefined);
+assert.sameValue(resultExplicit.getISOFields().timeZone, systemTimeZone, "time zone slot should store a string");
+
+assert.compareArray(actual, expected, "Temporal.TimeZone.from should not be called");
+
+const resultImplicit = Temporal.Now.zonedDateTime('iso8601');
+assert.sameValue(resultImplicit.getISOFields().timeZone, systemTimeZone, "time zone slot should store a string");
+
+assert.compareArray(actual, expected, "Temporal.TimeZone.from should not be called");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/timezone-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/timezone-case-insensitive.js
new file mode 100644
index 0000000000..e9dc04755c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/timezone-case-insensitive.js
@@ -0,0 +1,15 @@
+// |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.now.zoneddatetime
+description: Time zone names are case insensitive
+features: [Temporal]
+---*/
+
+const timeZone = 'UtC';
+const result = Temporal.Now.zonedDateTime("iso8601", timeZone);
+assert.sameValue(result.timeZoneId, 'UTC', `Time zone created from string "${timeZone}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/timezone-object.js b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/timezone-object.js
new file mode 100644
index 0000000000..8fb2aa5091
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/timezone-object.js
@@ -0,0 +1,39 @@
+// |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.now.zoneddatetime
+description: Observable interactions with the provided timezone-like object
+includes: [compareArray.js, temporalHelpers.js]
+features: [BigInt, Proxy, Temporal]
+---*/
+const actual = [];
+const expected = [
+ "has timeZone.getOffsetNanosecondsFor",
+ "has timeZone.getPossibleInstantsFor",
+ "has timeZone.id",
+];
+
+const timeZone = TemporalHelpers.timeZoneObserver(actual, "timeZone", {
+ getOffsetNanosecondsFor(instant) {
+ assert.sameValue(
+ instant instanceof Temporal.Instant,
+ true,
+ 'The result of evaluating (instant instanceof Temporal.Instant) is expected to be true'
+ );
+
+ return -Number(instant.epochNanoseconds % 86400000000000n);
+ }
+});
+
+Object.defineProperty(Temporal.TimeZone, 'from', {
+ get() {
+ actual.push('get Temporal.TimeZone.from');
+ return undefined;
+ }
+});
+
+Temporal.Now.zonedDateTime('iso8601', timeZone);
+assert.compareArray(actual, expected, 'order of observable operations');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/timezone-string-datetime.js b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/timezone-string-datetime.js
new file mode 100644
index 0000000000..0b00051b22
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/timezone-string-datetime.js
@@ -0,0 +1,63 @@
+// |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.now.zoneddatetime
+description: Conversion of ISO date-time strings to Temporal.TimeZone instances
+features: [Temporal]
+---*/
+
+let timeZone = "2021-08-19T17:30";
+assert.throws(RangeError, () => Temporal.Now.zonedDateTime("iso8601", timeZone), "bare date-time string is not a time zone");
+
+[
+ "2021-08-19T17:30-07:00:01",
+ "2021-08-19T17:30-07:00:00",
+ "2021-08-19T17:30-07:00:00.1",
+ "2021-08-19T17:30-07:00:00.0",
+ "2021-08-19T17:30-07:00:00.01",
+ "2021-08-19T17:30-07:00:00.00",
+ "2021-08-19T17:30-07:00:00.001",
+ "2021-08-19T17:30-07:00:00.000",
+ "2021-08-19T17:30-07:00:00.0001",
+ "2021-08-19T17:30-07:00:00.0000",
+ "2021-08-19T17:30-07:00:00.00001",
+ "2021-08-19T17:30-07:00:00.00000",
+ "2021-08-19T17:30-07:00:00.000001",
+ "2021-08-19T17:30-07:00:00.000000",
+ "2021-08-19T17:30-07:00:00.0000001",
+ "2021-08-19T17:30-07:00:00.0000000",
+ "2021-08-19T17:30-07:00:00.00000001",
+ "2021-08-19T17:30-07:00:00.00000000",
+ "2021-08-19T17:30-07:00:00.000000001",
+ "2021-08-19T17:30-07:00:00.000000000",
+].forEach((timeZone) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.Now.zonedDateTime("iso8601", timeZone),
+ `ISO string ${timeZone} with a sub-minute offset is not a valid time zone`
+ );
+});
+
+timeZone = "2021-08-19T17:30Z";
+const result1 = Temporal.Now.zonedDateTime("iso8601", timeZone);
+assert.sameValue(result1.timeZoneId, "UTC", "date-time + Z is UTC time zone");
+
+timeZone = "2021-08-19T17:30-07:00";
+const result2 = Temporal.Now.zonedDateTime("iso8601", timeZone);
+assert.sameValue(result2.timeZoneId, "-07:00", "date-time + offset is the offset time zone");
+
+timeZone = "2021-08-19T17:30[UTC]";
+const result3 = Temporal.Now.zonedDateTime("iso8601", timeZone);
+assert.sameValue(result3.timeZoneId, "UTC", "date-time + IANA annotation is the IANA time zone");
+
+timeZone = "2021-08-19T17:30Z[UTC]";
+const result4 = Temporal.Now.zonedDateTime("iso8601", timeZone);
+assert.sameValue(result4.timeZoneId, "UTC", "date-time + Z + IANA annotation is the IANA time zone");
+
+timeZone = "2021-08-19T17:30-07:00[UTC]";
+const result5 = Temporal.Now.zonedDateTime("iso8601", timeZone);
+assert.sameValue(result5.timeZoneId, "UTC", "date-time + offset + IANA annotation is the IANA time zone");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/timezone-string-leap-second.js b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/timezone-string-leap-second.js
new file mode 100644
index 0000000000..832d0030de
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/timezone-string-leap-second.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.now.zoneddatetime
+description: Leap second is a valid ISO string for TimeZone
+features: [Temporal]
+---*/
+
+let timeZone = "2016-12-31T23:59:60+00:00[UTC]";
+
+const result = Temporal.Now.zonedDateTime("iso8601", timeZone);
+assert.sameValue(result.timeZoneId, "UTC", "leap second is a valid ISO string for TimeZone");
+
+timeZone = "2021-08-19T17:30:45.123456789+23:59[+23:59:60]";
+assert.throws(RangeError, () => Temporal.Now.zonedDateTime("iso8601", timeZone), "leap second in time zone name not valid");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/timezone-string-multiple-offsets.js b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/timezone-string-multiple-offsets.js
new file mode 100644
index 0000000000..189a4180ad
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/timezone-string-multiple-offsets.js
@@ -0,0 +1,16 @@
+// |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.now.zoneddatetime
+description: Time zone parsing from ISO strings uses the bracketed offset, not the ISO string offset
+features: [Temporal]
+---*/
+
+const timeZone = "2021-08-19T17:30:45.123456789-12:12[+01:46]";
+
+const result = Temporal.Now.zonedDateTime("iso8601", timeZone);
+assert.sameValue(result.timeZoneId, "+01:46", "Time zone string determined from bracket name");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/timezone-string-year-zero.js b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/timezone-string-year-zero.js
new file mode 100644
index 0000000000..53838d3200
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/timezone-string-year-zero.js
@@ -0,0 +1,23 @@
+// |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.now.zoneddatetime
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+invalidStrings.forEach((timeZone) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.Now.zonedDateTime("iso8601", timeZone),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/timezone-string.js b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/timezone-string.js
new file mode 100644
index 0000000000..ec1e308ad2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/timezone-string.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.now.zoneddatetime
+description: Time zone IDs are valid input for a time zone
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getPossibleInstantsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getPossibleInstantsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be looked up");
+ },
+});
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+
+["UTC", "+01:30"].forEach((timeZone) => {
+ const result = Temporal.Now.zonedDateTime("iso8601", timeZone);
+ assert.sameValue(result.getISOFields().timeZone, timeZone, `Time zone created from string "${timeZone}"`);
+});
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", getPossibleInstantsForOriginal);
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/timezone-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/timezone-wrong-type.js
new file mode 100644
index 0000000000..82e7fac283
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTime/timezone-wrong-type.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.now.zoneddatetime
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or object for TimeZone
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [19761118, "number that would convert to a valid ISO string in other contexts"],
+ [1n, "bigint"],
+];
+
+for (const [timeZone, description] of primitiveTests) {
+ assert.throws(
+ typeof timeZone === 'string' ? RangeError : TypeError,
+ () => Temporal.Now.zonedDateTime("iso8601", timeZone),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "object not implementing time zone protocol"],
+ [new Temporal.Calendar("iso8601"), "calendar instance"],
+];
+
+for (const [timeZone, description] of typeErrorTests) {
+ assert.throws(TypeError, () => Temporal.Now.zonedDateTime("iso8601", timeZone), `${description} is not a valid object and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/browser.js b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/extensible.js b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/extensible.js
new file mode 100644
index 0000000000..e4238c3d62
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/extensible.js
@@ -0,0 +1,15 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.now.zoneddatetimeiso
+description: Temporal.Now.zonedDateTimeISO is extensible.
+features: [Temporal]
+---*/
+
+assert(
+ Object.isExtensible(Temporal.Now.zonedDateTimeISO),
+ 'Object.isExtensible(Temporal.Now.zonedDateTimeISO) must return true'
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/length.js b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/length.js
new file mode 100644
index 0000000000..62322e606b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/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.now.zoneddatetimeiso
+description: The `length` property of Temporal.Now.zonedDateTimeISO
+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.Now.zonedDateTimeISO, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/name.js b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/name.js
new file mode 100644
index 0000000000..807c6da180
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/name.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.now.zoneddatetimeiso
+description: Temporal.Now.zonedDateTimeISO.name is "zonedDateTimeISO".
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ Temporal.Now.zonedDateTimeISO.name,
+ 'zonedDateTimeISO',
+ 'The value of Temporal.Now.zonedDateTimeISO.name is expected to be "zonedDateTimeISO"'
+);
+
+verifyProperty(Temporal.Now.zonedDateTimeISO, 'name', {
+ enumerable: false,
+ writable: false,
+ configurable: true
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/not-a-constructor.js
new file mode 100644
index 0000000000..248a662906
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/not-a-constructor.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.now.zoneddatetimeiso
+description: Temporal.Now.zonedDateTimeISO does not implement [[Construct]]
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal, arrow-function]
+---*/
+
+assert.sameValue(isConstructor(Temporal.Now.zonedDateTimeISO), false, 'isConstructor(Temporal.Now.zonedDateTimeISO) must return false');
+
+assert.throws(TypeError, () => {
+ new Temporal.Now.zonedDateTimeISO();
+}, 'new Temporal.Now.zonedDateTimeISO() throws a TypeError exception');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/prop-desc.js
new file mode 100644
index 0000000000..a69f7d874d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/prop-desc.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.now.zoneddatetimeiso
+description: The "zonedDateTimeISO" property of Temporal.Now
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(typeof Temporal.Now.zonedDateTimeISO, "function", "typeof is function");
+
+verifyProperty(Temporal.Now, 'zonedDateTimeISO', {
+ enumerable: false,
+ writable: true,
+ configurable: true
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/return-value.js b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/return-value.js
new file mode 100644
index 0000000000..1f31373f95
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/return-value.js
@@ -0,0 +1,17 @@
+// |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.now.zoneddatetimeiso
+description: Functions when time zone argument is omitted
+features: [Temporal]
+---*/
+
+const zdt = Temporal.Now.zonedDateTimeISO();
+const tz = Temporal.Now.timeZoneId();
+assert(zdt instanceof Temporal.ZonedDateTime);
+assert.sameValue(zdt.getISOFields().calendar, "iso8601", "calendar slot should store a string");
+assert.sameValue(zdt.getISOFields().timeZone, tz, "time zone slot should store a string");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/shell.js b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/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/Now/zonedDateTimeISO/time-zone-undefined.js b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/time-zone-undefined.js
new file mode 100644
index 0000000000..53fc34e61c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/time-zone-undefined.js
@@ -0,0 +1,34 @@
+// |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.now.zoneddatetimeiso
+description: Functions when time zone argument is omitted
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const expected = [];
+
+Object.defineProperty(Temporal.TimeZone, "from", {
+ get() {
+ actual.push("get Temporal.TimeZone.from");
+ return undefined;
+ },
+});
+
+const systemTimeZone = Temporal.Now.timeZoneId();
+
+const resultExplicit = Temporal.Now.zonedDateTimeISO(undefined);
+assert.sameValue(resultExplicit.getISOFields().timeZone, systemTimeZone, "time zone slot should store a string");
+
+assert.compareArray(actual, expected, "Temporal.TimeZone.from should not be called");
+
+const resultImplicit = Temporal.Now.zonedDateTimeISO();
+assert.sameValue(resultImplicit.getISOFields().timeZone, systemTimeZone, "time zone slot should store a string");
+
+assert.compareArray(actual, expected, "Temporal.TimeZone.from should not be called");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/timezone-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/timezone-case-insensitive.js
new file mode 100644
index 0000000000..778d2d783a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/timezone-case-insensitive.js
@@ -0,0 +1,15 @@
+// |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.now.zoneddatetimeiso
+description: Time zone names are case insensitive
+features: [Temporal]
+---*/
+
+const timeZone = 'UtC';
+const result = Temporal.Now.zonedDateTimeISO(timeZone);
+assert.sameValue(result.timeZoneId, 'UTC', `Time zone created from string "${timeZone}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/timezone-object.js b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/timezone-object.js
new file mode 100644
index 0000000000..2b28058db6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/timezone-object.js
@@ -0,0 +1,39 @@
+// |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.now.zoneddatetime
+description: Observable interactions with the provided timezone-like object
+includes: [compareArray.js, temporalHelpers.js]
+features: [BigInt, Proxy, Temporal]
+---*/
+const actual = [];
+const expected = [
+ "has timeZone.getOffsetNanosecondsFor",
+ "has timeZone.getPossibleInstantsFor",
+ "has timeZone.id",
+];
+
+const timeZone = TemporalHelpers.timeZoneObserver(actual, "timeZone", {
+ getOffsetNanosecondsFor(instant) {
+ assert.sameValue(
+ instant instanceof Temporal.Instant,
+ true,
+ 'The result of evaluating (instant instanceof Temporal.Instant) is expected to be true'
+ );
+
+ return -Number(instant.epochNanoseconds % 86400000000000n);
+ }
+});
+
+Object.defineProperty(Temporal.TimeZone, 'from', {
+ get() {
+ actual.push('get Temporal.TimeZone.from');
+ return undefined;
+ }
+});
+
+Temporal.Now.zonedDateTimeISO(timeZone);
+assert.compareArray(actual, expected, 'order of observable operations');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/timezone-string-datetime.js b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/timezone-string-datetime.js
new file mode 100644
index 0000000000..d9b2389f07
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/timezone-string-datetime.js
@@ -0,0 +1,63 @@
+// |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.now.zoneddatetimeiso
+description: Conversion of ISO date-time strings to Temporal.TimeZone instances
+features: [Temporal]
+---*/
+
+let timeZone = "2021-08-19T17:30";
+assert.throws(RangeError, () => Temporal.Now.zonedDateTimeISO(timeZone), "bare date-time string is not a time zone");
+
+[
+ "2021-08-19T17:30-07:00:01",
+ "2021-08-19T17:30-07:00:00",
+ "2021-08-19T17:30-07:00:00.1",
+ "2021-08-19T17:30-07:00:00.0",
+ "2021-08-19T17:30-07:00:00.01",
+ "2021-08-19T17:30-07:00:00.00",
+ "2021-08-19T17:30-07:00:00.001",
+ "2021-08-19T17:30-07:00:00.000",
+ "2021-08-19T17:30-07:00:00.0001",
+ "2021-08-19T17:30-07:00:00.0000",
+ "2021-08-19T17:30-07:00:00.00001",
+ "2021-08-19T17:30-07:00:00.00000",
+ "2021-08-19T17:30-07:00:00.000001",
+ "2021-08-19T17:30-07:00:00.000000",
+ "2021-08-19T17:30-07:00:00.0000001",
+ "2021-08-19T17:30-07:00:00.0000000",
+ "2021-08-19T17:30-07:00:00.00000001",
+ "2021-08-19T17:30-07:00:00.00000000",
+ "2021-08-19T17:30-07:00:00.000000001",
+ "2021-08-19T17:30-07:00:00.000000000",
+].forEach((timeZone) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.Now.zonedDateTimeISO(timeZone),
+ `ISO string ${timeZone} with a sub-minute offset is not a valid time zone`
+ );
+});
+
+timeZone = "2021-08-19T17:30Z";
+const result1 = Temporal.Now.zonedDateTimeISO(timeZone);
+assert.sameValue(result1.timeZoneId, "UTC", "date-time + Z is UTC time zone");
+
+timeZone = "2021-08-19T17:30-07:00";
+const result2 = Temporal.Now.zonedDateTimeISO(timeZone);
+assert.sameValue(result2.timeZoneId, "-07:00", "date-time + offset is the offset time zone");
+
+timeZone = "2021-08-19T17:30[UTC]";
+const result3 = Temporal.Now.zonedDateTimeISO(timeZone);
+assert.sameValue(result3.timeZoneId, "UTC", "date-time + IANA annotation is the IANA time zone");
+
+timeZone = "2021-08-19T17:30Z[UTC]";
+const result4 = Temporal.Now.zonedDateTimeISO(timeZone);
+assert.sameValue(result4.timeZoneId, "UTC", "date-time + Z + IANA annotation is the IANA time zone");
+
+timeZone = "2021-08-19T17:30-07:00[UTC]";
+const result5 = Temporal.Now.zonedDateTimeISO(timeZone);
+assert.sameValue(result5.timeZoneId, "UTC", "date-time + offset + IANA annotation is the IANA time zone");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/timezone-string-leap-second.js b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/timezone-string-leap-second.js
new file mode 100644
index 0000000000..3619c66ff9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/timezone-string-leap-second.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.now.zoneddatetimeiso
+description: Leap second is a valid ISO string for TimeZone
+features: [Temporal]
+---*/
+
+let timeZone = "2016-12-31T23:59:60+00:00[UTC]";
+
+const result = Temporal.Now.zonedDateTimeISO(timeZone);
+assert.sameValue(result.timeZoneId, "UTC", "leap second is a valid ISO string for TimeZone");
+
+timeZone = "2021-08-19T17:30:45.123456789+23:59[+23:59:60]";
+assert.throws(RangeError, () => Temporal.Now.zonedDateTimeISO(timeZone), "leap second in time zone name not valid");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/timezone-string-multiple-offsets.js b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/timezone-string-multiple-offsets.js
new file mode 100644
index 0000000000..3f38eca0b0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/timezone-string-multiple-offsets.js
@@ -0,0 +1,16 @@
+// |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.now.zoneddatetimeiso
+description: Time zone parsing from ISO strings uses the bracketed offset, not the ISO string offset
+features: [Temporal]
+---*/
+
+const timeZone = "2021-08-19T17:30:45.123456789-12:12[+01:46]";
+
+const result = Temporal.Now.zonedDateTimeISO(timeZone);
+assert.sameValue(result.timeZoneId, "+01:46", "Time zone string determined from bracket name");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/timezone-string-year-zero.js b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/timezone-string-year-zero.js
new file mode 100644
index 0000000000..7f18946c89
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/timezone-string-year-zero.js
@@ -0,0 +1,23 @@
+// |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.now.zoneddatetimeiso
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+invalidStrings.forEach((timeZone) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.Now.zonedDateTimeISO(timeZone),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/timezone-string.js b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/timezone-string.js
new file mode 100644
index 0000000000..c9245b79b7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/timezone-string.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.now.zoneddatetimeiso
+description: Time zone IDs are valid input for a time zone
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getPossibleInstantsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getPossibleInstantsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be looked up");
+ },
+});
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+
+["UTC", "+01:30"].forEach((timeZone) => {
+ const result = Temporal.Now.zonedDateTimeISO(timeZone);
+ assert.sameValue(result.getISOFields().timeZone, timeZone, `Time zone created from string "${timeZone}"`);
+});
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", getPossibleInstantsForOriginal);
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/timezone-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/timezone-wrong-type.js
new file mode 100644
index 0000000000..be71841c8f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/Now/zonedDateTimeISO/timezone-wrong-type.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.now.zoneddatetimeiso
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or object for TimeZone
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [19761118, "number that would convert to a valid ISO string in other contexts"],
+ [1n, "bigint"],
+];
+
+for (const [timeZone, description] of primitiveTests) {
+ assert.throws(
+ typeof timeZone === 'string' ? RangeError : TypeError,
+ () => Temporal.Now.zonedDateTimeISO(timeZone),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "object not implementing time zone protocol"],
+ [new Temporal.Calendar("iso8601"), "calendar instance"],
+];
+
+for (const [timeZone, description] of typeErrorTests) {
+ assert.throws(TypeError, () => Temporal.Now.zonedDateTimeISO(timeZone), `${description} is not a valid object and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/argument-convert.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/argument-convert.js
new file mode 100644
index 0000000000..678c6a5f00
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/argument-convert.js
@@ -0,0 +1,55 @@
+// |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.plaindate
+description: PlainDate constructor with non-integer arguments.
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.assertPlainDate(new Temporal.PlainDate(2020.6, 11.7, 24.1),
+ 2020, 11, "M11", 24, "positive fractional");
+
+TemporalHelpers.assertPlainDate(new Temporal.PlainDate(-2020.6, 11.7, 24.1),
+ -2020, 11, "M11", 24, "negative fractional");
+
+TemporalHelpers.assertPlainDate(new Temporal.PlainDate(null, 11, 24),
+ 0, 11, "M11", 24, "null");
+
+TemporalHelpers.assertPlainDate(new Temporal.PlainDate(true, 11, 24),
+ 1, 11, "M11", 24, "boolean");
+
+TemporalHelpers.assertPlainDate(new Temporal.PlainDate("2020.6", "11.7", "24.1"),
+ 2020, 11, "M11", 24, "fractional strings");
+
+for (const invalid of [Symbol(), 1n]) {
+ assert.throws(TypeError, () => new Temporal.PlainDate(invalid, 11, 24), `year ${typeof invalid}`);
+ assert.throws(TypeError, () => new Temporal.PlainDate(2020, invalid, 24), `month ${typeof invalid}`);
+ assert.throws(TypeError, () => new Temporal.PlainDate(2020, 11, invalid), `day ${typeof invalid}`);
+}
+
+for (const invalid of [undefined, "invalid"]) {
+ assert.throws(RangeError, () => new Temporal.PlainDate(invalid, 11, 24), `year ${typeof invalid}`);
+ assert.throws(RangeError, () => new Temporal.PlainDate(2020, invalid, 24), `month ${typeof invalid}`);
+ assert.throws(RangeError, () => new Temporal.PlainDate(2020, 11, invalid), `day ${typeof invalid}`);
+}
+const actual = [];
+const args = [
+ TemporalHelpers.toPrimitiveObserver(actual, 2020, "year"),
+ TemporalHelpers.toPrimitiveObserver(actual, 11, "month"),
+ TemporalHelpers.toPrimitiveObserver(actual, 24, "day"),
+];
+TemporalHelpers.assertPlainDate(new Temporal.PlainDate(...args),
+ 2020, 11, "M11", 24, "invalid string");
+assert.compareArray(actual, [
+ "get year.valueOf",
+ "call year.valueOf",
+ "get month.valueOf",
+ "call month.valueOf",
+ "get day.valueOf",
+ "call day.valueOf",
+]);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/argument-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/argument-invalid.js
new file mode 100644
index 0000000000..4e1191ee98
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/argument-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.plaindate
+description: PlainDate constructor with invalid iso dates
+features: [Temporal]
+---*/
+
+const tests = [
+ [2020, 0, 24],
+ [2020, 13, 24],
+ [2020, -3, 24],
+ [2020, 12, 32],
+ [2020, 2, 30],
+ [2019, 2, 29],
+ [2019, 2, 0],
+ [2019, 2, -20],
+];
+
+for (const [year, month, day] of tests) {
+ assert.throws(RangeError, () => new Temporal.PlainDate(year, month, day),
+ `year=${year}, month=${month}, day=${day}`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/basic.js
new file mode 100644
index 0000000000..fab877e5e5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/basic.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.plaindate
+description: Basic tests for the PlainDate constructor.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+Object.defineProperty(Temporal.Calendar, "from", {
+ get() {
+ throw new Test262Error("Should not get Calendar.from");
+ },
+});
+
+const calendar = new Temporal.Calendar("iso8601");
+const plainDateWithObject = new Temporal.PlainDate(2020, 12, 24, calendar);
+TemporalHelpers.assertPlainDate(plainDateWithObject, 2020, 12, "M12", 24, "with object");
+assert.sameValue(plainDateWithObject.getCalendar(), calendar);
+
+const plainDateWithString = new Temporal.PlainDate(2020, 12, 24, "iso8601");
+TemporalHelpers.assertPlainDate(plainDateWithString, 2020, 12, "M12", 24, "with string");
+assert.sameValue(plainDateWithString.getISOFields().calendar, "iso8601", "calendar slot should store a string");
+assert.notSameValue(plainDateWithString.getCalendar(), calendar);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/builtin.js
new file mode 100644
index 0000000000..bc862877d0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/builtin.js
@@ -0,0 +1,30 @@
+// |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.plaindate
+description: Tests that Temporal.PlainDate meets the requirements for built-in objects
+info: |
+ 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.PlainDate),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDate),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDate),
+ Function.prototype, "prototype");
+
+assert.sameValue(typeof Temporal.PlainDate.prototype,
+ "object", "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/calendar-case-insensitive.js
new file mode 100644
index 0000000000..406dbc44c4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/calendar-case-insensitive.js
@@ -0,0 +1,16 @@
+// |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.plaindate
+description: Calendar names are case-insensitive
+features: [Temporal]
+---*/
+
+const arg = "iSo8601";
+
+const result = new Temporal.PlainDate(2000, 5, 2, arg);
+assert.sameValue(result.calendarId, "iso8601", "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/calendar-number.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/calendar-number.js
new file mode 100644
index 0000000000..cd0f23b107
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/calendar-number.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.plaindate
+description: A number is not allowed to be a calendar
+features: [Temporal]
+---*/
+
+const numbers = [
+ 1,
+ -19761118,
+ 19761118,
+ 1234567890,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => new Temporal.PlainDate(2000, 5, 2, arg),
+ "A number is not a valid ISO string for Calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/calendar-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/calendar-string.js
new file mode 100644
index 0000000000..d4be3beed6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/calendar-string.js
@@ -0,0 +1,16 @@
+// |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.plaindate.constructor
+description: A calendar ID is valid input for Calendar
+features: [Temporal]
+---*/
+
+const arg = "iso8601";
+
+const result = new Temporal.PlainDate(2000, 5, 2, arg);
+assert.sameValue(result.getISOFields().calendar, "iso8601", `Calendar created from string "${arg}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/calendar-temporal-object.js
new file mode 100644
index 0000000000..73f0b2f227
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/calendar-temporal-object.js
@@ -0,0 +1,41 @@
+// |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.plaindate
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal-totemporalcalendar step 1.b:
+ b. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const plainDate = new Temporal.PlainDate(2000, 5, 2);
+const plainDateTime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const plainMonthDay = new Temporal.PlainMonthDay(5, 2);
+const plainYearMonth = new Temporal.PlainYearMonth(2000, 5);
+const zonedDateTime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+
+[plainDate, plainDateTime, plainMonthDay, plainYearMonth, zonedDateTime].forEach((arg) => {
+ const actual = [];
+ const expected = [];
+
+ const calendar = arg.getISOFields().calendar;
+
+ Object.defineProperty(arg, "calendar", {
+ get() {
+ actual.push("get calendar");
+ return calendar;
+ },
+ });
+
+ const result = new Temporal.PlainDate(2000, 5, 2, arg);
+ assert.sameValue(result.getISOFields().calendar, calendar, "Temporal object coerced to calendar");
+
+ assert.compareArray(actual, expected, "calendar getter not called");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/calendar-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/calendar-undefined.js
new file mode 100644
index 0000000000..64d68fd512
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/calendar-undefined.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.plaindate
+description: Calendar argument defaults to the built-in ISO 8601 calendar
+features: [Temporal]
+---*/
+
+const args = [2020, 12, 24];
+
+Object.defineProperty(Temporal.Calendar, "from", {
+ get() {
+ throw new Test262Error("Should not get Calendar.from");
+ },
+});
+
+const dateExplicit = new Temporal.PlainDate(...args, undefined);
+assert.sameValue(dateExplicit.getISOFields().calendar, "iso8601", "calendar slot should store string");
+
+const dateImplicit = new Temporal.PlainDate(...args);
+assert.sameValue(dateImplicit.getISOFields().calendar, "iso8601", "calendar slot should store string");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/calendar-wrong-type.js
new file mode 100644
index 0000000000..21897ea402
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/calendar-wrong-type.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.plaindate
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or object for Calendar
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const primitiveTests = [
+ [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,
+ () => new Temporal.PlainDate(2000, 5, 2, arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => new Temporal.PlainDate(2000, 5, 2, arg), `${description} is not a valid object and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..33567d5266
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-builtin-calendar-no-array-iteration.js
@@ -0,0 +1,24 @@
+// |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.plaindate.compare
+description: >
+ Calling the method with a property bag argument with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const arg = { year: 2000, month: 5, day: 2, calendar: "iso8601" };
+Temporal.PlainDate.compare(arg, new Temporal.PlainDate(1976, 11, 18));
+Temporal.PlainDate.compare(new Temporal.PlainDate(1976, 11, 18), arg);
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-calendar-datefromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..08733a161f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-calendar-datefromfields-called-with-null-prototype-fields.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.plaindate.compare
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const arg1 = { year: 2000, month: 5, day: 2, calendar };
+const arg2 = new Temporal.PlainDate(1976, 11, 18);
+
+Temporal.PlainDate.compare(arg1, arg2);
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar (first argument)");
+
+calendar.dateFromFieldsCallCount = 0;
+
+Temporal.PlainDate.compare(arg2, arg1);
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar (first argument)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..d7f5b98479
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-constructor-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |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.plaindate.prototype.compare
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+
+assert.throws(RangeError, () => Temporal.PlainDate.compare(arg, new Temporal.PlainDate(1976, 11, 18)));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-duplicate-calendar-fields.js
new file mode 100644
index 0000000000..2898cce8e0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-duplicate-calendar-fields.js
@@ -0,0 +1,19 @@
+// |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.plaindate.prototype.compare
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['day'], ['month'], ['monthCode'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar };
+
+ assert.throws(RangeError, () => Temporal.PlainDate.compare(arg, new Temporal.PlainDate(1976, 11, 18)));
+ assert.throws(RangeError, () => Temporal.PlainDate.compare(new Temporal.PlainDate(1976, 11, 18), arg));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-number.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-number.js
new file mode 100644
index 0000000000..62c0596003
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-number.js
@@ -0,0 +1,31 @@
+// |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.plaindate.compare
+description: A number cannot be used in place of a Temporal.PlainDate
+features: [Temporal]
+---*/
+
+const numbers = [
+ 1,
+ 19761118,
+ -19761118,
+ 1234567890,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => Temporal.PlainDate.compare(arg, new Temporal.PlainDate(1976, 11, 18)),
+ "A number is not a valid ISO string for PlainDate (first argument)"
+ );
+ assert.throws(
+ TypeError,
+ () => Temporal.PlainDate.compare(new Temporal.PlainDate(1976, 11, 18), arg),
+ "A number is not a valid ISO string for PlainDate (second argument)"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-object.js
new file mode 100644
index 0000000000..ad96dcbb27
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-object.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.plaindate.compare
+description: basic object coercion in arguments
+features: [Temporal]
+---*/
+
+const d1 = Temporal.PlainDate.from("1976-11-18");
+const d2 = Temporal.PlainDate.from("2019-06-30");
+
+assert.sameValue(Temporal.PlainDate.compare({ year: 1976, month: 11, day: 18 }, d2), -1, "first argument");
+assert.sameValue(Temporal.PlainDate.compare({ year: 2019, month: 6, day: 30 }, d2), 0, "first argument");
+assert.sameValue(Temporal.PlainDate.compare({ year: 2024, month: 1, day: 12 }, d2), 1, "first argument");
+
+assert.sameValue(Temporal.PlainDate.compare(d1, { year: 2024, month: 1, day: 12 }), -1, "second argument");
+assert.sameValue(Temporal.PlainDate.compare(d1, { year: 1976, month: 11, day: 18 }), 0, "second argument");
+assert.sameValue(Temporal.PlainDate.compare(d1, { year: 1926, month: 7, day: 7 }), 1, "second argument");
+
+assert.throws(TypeError, () => Temporal.PlainDate.compare({ year: 1976 }, d2), "only year in first argument");
+assert.throws(TypeError, () => Temporal.PlainDate.compare(d1, { year: 2019 }), "only year in second argument");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-plaindatetime.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-plaindatetime.js
new file mode 100644
index 0000000000..5666cdb980
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-plaindatetime.js
@@ -0,0 +1,53 @@
+// |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.plaindate.compare
+description: Fast path for converting Temporal.PlainDateTime to Temporal.PlainDate by reading internal slots
+info: |
+ sec-temporal.plaindate.compare steps 1–2:
+ 1. Set _one_ to ? ToTemporalDate(_one_).
+ 2. Set _two_ to ? ToTemporalDate(_two_).
+ sec-temporal-totemporaldate step 2.b:
+ b. If _item_ has an [[InitializedTemporalDateTime]] internal slot, then
+ i. Return ! CreateTemporalDate(_item_.[[ISOYear]], _item_.[[ISOMonth]], _item_.[[ISODay]], _item_.[[Calendar]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const sameDate = new Temporal.PlainDate(2000, 5, 2);
+const earlierDate = new Temporal.PlainDate(1920, 7, 3);
+const laterDate = new Temporal.PlainDate(2005, 1, 12);
+
+TemporalHelpers.checkPlainDateTimeConversionFastPath((datetime) => {
+ const result = Temporal.PlainDate.compare(datetime, sameDate);
+ assert.sameValue(result, 0, "First argument, same date: comparison result");
+}, "First argument, same date");
+
+TemporalHelpers.checkPlainDateTimeConversionFastPath((datetime) => {
+ const result = Temporal.PlainDate.compare(datetime, earlierDate);
+ assert.sameValue(result, 1, "First argument, earlier date: comparison result");
+}, "First argument, earlier date");
+
+TemporalHelpers.checkPlainDateTimeConversionFastPath((datetime) => {
+ const result = Temporal.PlainDate.compare(datetime, laterDate);
+ assert.sameValue(result, -1, "First argument, later date: comparison result");
+}, "First argument, later date");
+
+TemporalHelpers.checkPlainDateTimeConversionFastPath((datetime) => {
+ const result = Temporal.PlainDate.compare(sameDate, datetime);
+ assert.sameValue(result, 0, "Second argument, same date: comparison result");
+}, "Second argument, same date");
+
+TemporalHelpers.checkPlainDateTimeConversionFastPath((datetime) => {
+ const result = Temporal.PlainDate.compare(earlierDate, datetime);
+ assert.sameValue(result, -1, "Second argument, earlier date: comparison result");
+}, "Second argument, earlier date");
+
+TemporalHelpers.checkPlainDateTimeConversionFastPath((datetime) => {
+ const result = Temporal.PlainDate.compare(laterDate, datetime);
+ assert.sameValue(result, 1, "Second argument, later date: comparison result");
+}, "Second argument, later date");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-propertybag-calendar-case-insensitive.js
new file mode 100644
index 0000000000..531868629a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-propertybag-calendar-case-insensitive.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.plaindate.compare
+description: The calendar name is case-insensitive
+features: [Temporal]
+---*/
+
+const calendar = "IsO8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result1 = Temporal.PlainDate.compare(arg, new Temporal.PlainDate(1976, 11, 18));
+assert.sameValue(result1, 0, "Calendar is case-insensitive (first argument)");
+const result2 = Temporal.PlainDate.compare(new Temporal.PlainDate(1976, 11, 18), arg);
+assert.sameValue(result2, 0, "Calendar is case-insensitive (second argument)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-propertybag-calendar-leap-second.js
new file mode 100644
index 0000000000..34e69918a2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-propertybag-calendar-leap-second.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.plaindate.compare
+description: Leap second is a valid ISO string for a calendar in a property bag
+features: [Temporal]
+---*/
+
+const calendar = "2016-12-31T23:59:60";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result1 = Temporal.PlainDate.compare(arg, new Temporal.PlainDate(1976, 11, 18));
+assert.sameValue(result1, 0, "leap second is a valid ISO string for calendar (first argument)");
+const result2 = Temporal.PlainDate.compare(new Temporal.PlainDate(1976, 11, 18), arg);
+assert.sameValue(result2, 0, "leap second is a valid ISO string for calendar (first argument)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-propertybag-calendar-number.js
new file mode 100644
index 0000000000..076c5088cf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-propertybag-calendar-number.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.plaindate.compare
+description: A number as calendar in a property bag is not accepted
+features: [Temporal]
+---*/
+
+const numbers = [
+ 1,
+ 19970327,
+ -19970327,
+ 1234567890,
+];
+
+for (const calendar of numbers) {
+ const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+ assert.throws(
+ TypeError,
+ () => Temporal.PlainDate.compare(arg, new Temporal.PlainDate(1976, 11, 18)),
+ "A number is not a valid ISO string for calendar (first argument)"
+ );
+ assert.throws(
+ TypeError,
+ () => Temporal.PlainDate.compare(new Temporal.PlainDate(1976, 11, 18), arg),
+ "A number is not a valid ISO string for calendar (second argument)"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-propertybag-calendar-string.js
new file mode 100644
index 0000000000..3c47b5e003
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-propertybag-calendar-string.js
@@ -0,0 +1,33 @@
+// |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.plaindate.compare
+description: A calendar ID is valid input for Calendar
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dateFromFieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dateFromFields");
+Object.defineProperty(Temporal.Calendar.prototype, "dateFromFields", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("dateFromFields should not be looked up");
+ },
+});
+
+const calendar = "iso8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+
+const result1 = Temporal.PlainDate.compare(arg, new Temporal.PlainDate(1976, 11, 18));
+assert.sameValue(result1, 0, `Calendar created from string "${arg}" (first argument)`);
+
+const result2 = Temporal.PlainDate.compare(new Temporal.PlainDate(1976, 11, 18), arg);
+assert.sameValue(result2, 0, `Calendar created from string "${arg}" (second argument)`);
+
+Object.defineProperty(Temporal.Calendar.prototype, "dateFromFields", dateFromFieldsOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-propertybag-calendar-wrong-type.js
new file mode 100644
index 0000000000..a347aeee90
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-propertybag-calendar-wrong-type.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.plaindate.compare
+description: >
+ Appropriate error thrown when a calendar property from a property bag cannot
+ be converted to a calendar object or string
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [calendar, description] of primitiveTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ typeof calendar === "string" ? RangeError : TypeError,
+ () => Temporal.PlainDate.compare(arg, new Temporal.PlainDate(1976, 11, 18)),
+ `${description} does not convert to a valid ISO string (first argument)`
+ );
+ assert.throws(
+ typeof calendar === "string" ? RangeError : TypeError,
+ () => Temporal.PlainDate.compare(new Temporal.PlainDate(1976, 11, 18), arg),
+ `${description} does not convert to a valid ISO string (second argument)`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+ [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields()
+];
+
+for (const [calendar, description] of typeErrorTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(TypeError, () => Temporal.PlainDate.compare(arg, new Temporal.PlainDate(1976, 11, 18)), `${description} is not a valid property bag and does not convert to a string (first argument)`);
+ assert.throws(TypeError, () => Temporal.PlainDate.compare(new Temporal.PlainDate(1976, 11, 18), arg), `${description} is not a valid property bag and does not convert to a string (second argument)`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-propertybag-calendar-year-zero.js
new file mode 100644
index 0000000000..4bee8a16e2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-propertybag-calendar-year-zero.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.plaindate.compare
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T17:45",
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+01:00",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDate.compare(arg, new Temporal.PlainDate(1976, 11, 18)),
+ "reject minus zero as extended year (first argument)"
+ );
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDate.compare(new Temporal.PlainDate(1976, 11, 18), arg),
+ "reject minus zero as extended year (second argument)"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-proto-in-calendar-fields.js
new file mode 100644
index 0000000000..70463984e9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-proto-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |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.plaindate.prototype.compare
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+
+assert.throws(RangeError, () => Temporal.PlainDate.compare(arg, new Temporal.PlainDate(1976, 11, 18)));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..67725aae57
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-calendar-annotation.js
@@ -0,0 +1,31 @@
+// |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.plaindate.compare
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02[u-ca=iso8601]", "without time or time zone"],
+ ["2000-05-02[UTC][u-ca=iso8601]", "with time zone and no time"],
+ ["2000-05-02T15:23[u-ca=iso8601]", "without time zone"],
+ ["2000-05-02T15:23[UTC][u-ca=iso8601]", "with time zone"],
+ ["2000-05-02T15:23[!u-ca=iso8601]", "with ! and no time zone"],
+ ["2000-05-02T15:23[UTC][!u-ca=iso8601]", "with ! and time zone"],
+ ["2000-05-02T15:23[u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.PlainDate.compare(arg, arg);
+
+ assert.sameValue(
+ result,
+ 0,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..375258ee4b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-critical-unknown-annotation.js
@@ -0,0 +1,33 @@
+// |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.plaindate.compare
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[!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]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDate.compare(arg, new Temporal.PlainDate(1976, 11, 18)),
+ `reject unknown annotation with critical flag: ${arg} (first argument)`
+ );
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDate.compare(new Temporal.PlainDate(1976, 11, 18), arg),
+ `reject unknown annotation with critical flag: ${arg} (second argument)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..a2d429d48f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-date-with-utc-offset.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.plaindate.compare
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+---*/
+
+const validStrings = [
+ "2000-05-02T00+00:00",
+ "2000-05-02T00+00:00[UTC]",
+ "2000-05-02T00+00:00[!UTC]",
+ "2000-05-02T00-02:30[America/St_Johns]",
+];
+
+for (const arg of validStrings) {
+ const result = Temporal.PlainDate.compare(arg, arg);
+
+ assert.sameValue(
+ result,
+ 0,
+ `"${arg}" is a valid UTC offset with time for PlainDate`
+ );
+}
+
+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,
+ () => Temporal.PlainDate.compare(arg, new Temporal.PlainDate(1976, 11, 18)),
+ `"${arg}" UTC offset without time is not valid for PlainDate (first argument)`
+ );
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDate.compare(new Temporal.PlainDate(1976, 11, 18), arg),
+ `"${arg}" UTC offset without time is not valid for PlainDate (second argument)`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-invalid.js
new file mode 100644
index 0000000000..7e50608f49
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-invalid.js
@@ -0,0 +1,69 @@
+// |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.plaindate.compare
+description: >
+ RangeError thrown if an invalid ISO string (or syntactically valid ISO string
+ that is not supported) is used as a PlainDate
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ // invalid ISO strings:
+ "",
+ "invalid iso8601",
+ "2020-01-00",
+ "2020-01-32",
+ "2020-02-30",
+ "2021-02-29",
+ "2020-00-01",
+ "2020-13-01",
+ "2020-01-01T",
+ "2020-01-01T25:00:00",
+ "2020-01-01T01:60:00",
+ "2020-01-01T01:60:61",
+ "2020-01-01junk",
+ "2020-01-01T00:00:00junk",
+ "2020-01-01T00:00:00+00:00junk",
+ "2020-01-01T00:00:00+00:00[UTC]junk",
+ "2020-01-01T00:00:00+00:00[UTC][u-ca=iso8601]junk",
+ "02020-01-01",
+ "2020-001-01",
+ "2020-01-001",
+ "2020-01-01T001",
+ "2020-01-01T01:001",
+ "2020-01-01T01:01:001",
+ // valid, but forms not supported in Temporal:
+ "2020-W01-1",
+ "2020-001",
+ "+0002020-01-01",
+ // valid, but this calendar must not exist:
+ "2020-01-01[u-ca=notexist]",
+ // may be valid in other contexts, but insufficient information for PlainDate:
+ "2020-01",
+ "+002020-01",
+ "01-01",
+ "2020-W01",
+ "P1Y",
+ "-P12Y",
+ // valid, but outside the supported range:
+ "-999999-01-01",
+ "+999999-01-01",
+];
+const other = new Temporal.PlainDate(2020, 1, 1);
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDate.compare(arg, other),
+ `"${arg}" should not be a valid ISO string for a PlainDate (first argument)`
+ );
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDate.compare(other, arg),
+ `"${arg}" should not be a valid ISO string for a PlainDate (second argument)`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..816af9c8cb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-multiple-calendar.js
@@ -0,0 +1,37 @@
+// |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.plaindate.compare
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[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]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDate.compare(arg, new Temporal.PlainDate(1976, 11, 18)),
+ `reject more than one calendar annotation if any critical: ${arg} (first argument)`
+ );
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDate.compare(new Temporal.PlainDate(1976, 11, 18), arg),
+ `reject more than one calendar annotation if any critical: ${arg} (second argument)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..ea7794faf2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-multiple-time-zone.js
@@ -0,0 +1,33 @@
+// |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.plaindate.compare
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[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]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDate.compare(arg, new Temporal.PlainDate(1976, 11, 18)),
+ `reject more than one time zone annotation: ${arg} (first argument)`
+ );
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDate.compare(new Temporal.PlainDate(1976, 11, 18), arg),
+ `reject more than one time zone annotation: ${arg} (second argument)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-time-separators.js
new file mode 100644
index 0000000000..7d3a055703
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/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.plaindate.compare
+description: Time separator in string argument can vary
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(2000, 5, 2);
+const tests = [
+ ["2000-05-02T15:23", "uppercase T"],
+ ["2000-05-02t15:23", "lowercase T"],
+ ["2000-05-02 15:23", "space between date and time"],
+];
+
+tests.forEach(([arg, description]) => {
+ assert.sameValue(
+ Temporal.PlainDate.compare(arg, date),
+ 0,
+ `variant time separators (${description}), first argument`
+ );
+
+ assert.sameValue(
+ Temporal.PlainDate.compare(date, arg),
+ 0,
+ `variant time separators (${description}), second argument`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..a7cd502511
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-time-zone-annotation.js
@@ -0,0 +1,36 @@
+// |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.plaindate.compare
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ['2000-05-02[Asia/Kolkata]', 'named, with no time'],
+ ['2000-05-02[!Europe/Vienna]', 'named, with ! and no time'],
+ ['2000-05-02[+00:00]', 'numeric, with no time'],
+ ['2000-05-02[!-02:30]', 'numeric, with ! and no time'],
+ ["2000-05-02T15:23[America/Sao_Paulo]", "named, with no offset"],
+ ["2000-05-02T15:23[!Asia/Tokyo]", "named, with ! and no offset"],
+ ["2000-05-02T15:23[-02:30]", "numeric, with no offset"],
+ ["2000-05-02T15:23[!+00:00]", "numeric, with ! and no offset"],
+ ["2000-05-02T15:23+00:00[America/New_York]", "named, with offset"],
+ ["2000-05-02T15:23+00:00[!UTC]", "named, with offset and !"],
+ ["2000-05-02T15:23+00:00[+01:00]", "numeric, with offset"],
+ ["2000-05-02T15:23+00:00[!-08:00]", "numeric, with offset and !"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.PlainDate.compare(arg, arg);
+
+ assert.sameValue(
+ result,
+ 0,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..869ac1e464
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-unknown-annotation.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.plaindate.compare
+description: Various forms of unknown annotation
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02[foo=bar]", "without time"],
+ ["2000-05-02T15:23[foo=bar]", "alone"],
+ ["2000-05-02T15:23[UTC][foo=bar]", "with time zone"],
+ ["2000-05-02T15:23[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["2000-05-02T15:23[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["2000-05-02T15:23[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.PlainDate.compare(arg, arg);
+
+ assert.sameValue(
+ result,
+ 0,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..d434dbc6a6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-with-utc-designator.js
@@ -0,0 +1,29 @@
+// |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.plaindate.compare
+description: RangeError thrown if a string with UTC designator is used as a PlainDate
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "2019-10-01T09:00:00Z",
+ "2019-10-01T09:00:00Z[UTC]",
+];
+const plainDate = new Temporal.PlainDate(2000, 5, 2);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDate.compare(arg, plainDate),
+ "String with UTC designator should not be valid as a PlainDate (first argument)"
+ );
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDate.compare(plainDate, arg),
+ "String with UTC designator should not be valid as a PlainDate (second argument)"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string.js
new file mode 100644
index 0000000000..cb7969a861
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string.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.plaindate.compare
+description: basic string coercion in arguments
+features: [Temporal]
+---*/
+
+const d1 = Temporal.PlainDate.from("1976-11-18");
+const d2 = Temporal.PlainDate.from("2019-06-30");
+
+assert.sameValue(Temporal.PlainDate.compare("1976-11-18", d2), -1, "first argument");
+assert.sameValue(Temporal.PlainDate.compare("2019-06-30", d2), 0, "first argument");
+assert.sameValue(Temporal.PlainDate.compare("2024-01-12", d2), 1, "first argument");
+
+assert.sameValue(Temporal.PlainDate.compare(d1, "2019-06-30"), -1, "second argument");
+assert.sameValue(Temporal.PlainDate.compare(d1, "1976-11-18"), 0, "second argument");
+assert.sameValue(Temporal.PlainDate.compare(d1, "1926-07-07"), 1, "second argument");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-wrong-type.js
new file mode 100644
index 0000000000..29d8f2ba6a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-wrong-type.js
@@ -0,0 +1,47 @@
+// |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.plaindate.compare
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainDate
+features: [BigInt, Symbol, Temporal]
+---*/
+
+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,
+ () => Temporal.PlainDate.compare(arg, new Temporal.PlainDate(1976, 11, 18)),
+ `${description} does not convert to a valid ISO string (first argument)`
+ );
+ assert.throws(
+ typeof arg === 'string' ? RangeError : TypeError,
+ () => Temporal.PlainDate.compare(new Temporal.PlainDate(1976, 11, 18), arg),
+ `${description} does not convert to a valid ISO string (second argument)`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.PlainDate, "Temporal.PlainDate, object"],
+ [Temporal.PlainDate.prototype, "Temporal.PlainDate.prototype, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => Temporal.PlainDate.compare(arg, new Temporal.PlainDate(1976, 11, 18)), `${description} is not a valid property bag and does not convert to a string (first argument)`);
+ assert.throws(TypeError, () => Temporal.PlainDate.compare(new Temporal.PlainDate(1976, 11, 18), arg), `${description} is not a valid property bag and does not convert to a string (second argument)`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-zoneddatetime-slots.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-zoneddatetime-slots.js
new file mode 100644
index 0000000000..deb5803a86
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-zoneddatetime-slots.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.plaindate.compare
+description: Getters are not called when converting a ZonedDateTime to a PlainDate.
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.ZonedDateTime.prototype);
+const getters = ["year", "month", "monthCode", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond", "calendar"];
+
+for (const property of getters) {
+ Object.defineProperty(Temporal.ZonedDateTime.prototype, property, {
+ get() {
+ actual.push(`get ${property}`);
+ const value = prototypeDescrs[property].get.call(this);
+ return {
+ toString() {
+ actual.push(`toString ${property}`);
+ return value.toString();
+ },
+ valueOf() {
+ actual.push(`valueOf ${property}`);
+ return value;
+ },
+ };
+ },
+ });
+}
+
+const arg = new Temporal.ZonedDateTime(0n, "UTC");
+Temporal.PlainDate.compare(arg, new Temporal.PlainDate(1976, 11, 18));
+Temporal.PlainDate.compare(new Temporal.PlainDate(1976, 11, 18), arg);
+assert.compareArray(actual, []);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..7dcdaf5d6f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.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.plaindate.compare
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const date = new Temporal.PlainDate(2000, 5, 2);
+
+ assert.throws(RangeError, () => Temporal.PlainDate.compare(datetime, date));
+ assert.throws(RangeError, () => Temporal.PlainDate.compare(date, datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..2ecaa0ad9d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.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.plaindate.compare
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => Temporal.PlainDate.compare(datetime, date),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+ assert.throws(
+ TypeError,
+ () => Temporal.PlainDate.compare(date, datetime),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..45bb1081c6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.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.plaindate.compare
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const date = new Temporal.PlainDate(2000, 5, 2);
+
+ assert.throws(RangeError, () => Temporal.PlainDate.compare(datetime, date));
+ assert.throws(RangeError, () => Temporal.PlainDate.compare(date, datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..669a216d5b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,30 @@
+// |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.plaindate.compare
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const date = new Temporal.PlainDate(2000, 5, 2);
+
+ assert.throws(TypeError, () => Temporal.PlainDate.compare(datetime, date));
+ assert.throws(TypeError, () => Temporal.PlainDate.compare(date, datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-zoneddatetime.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-zoneddatetime.js
new file mode 100644
index 0000000000..a6654b357b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-zoneddatetime.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.plaindate.compare
+description: ZonedDateTime is supported.
+features: [Temporal]
+---*/
+
+const zdt = new Temporal.ZonedDateTime(0n, "UTC");
+assert.sameValue(
+ Temporal.PlainDate.compare(zdt, new Temporal.PlainDate(1970, 1, 1)),
+ 0, "same date, ZDT first");
+assert.sameValue(
+ Temporal.PlainDate.compare(new Temporal.PlainDate(1970, 1, 1), zdt),
+ 0, "same date, ZDT second");
+assert.sameValue(
+ Temporal.PlainDate.compare(zdt, new Temporal.PlainDate(1976, 11, 18)),
+ -1, "different date, ZDT first");
+assert.sameValue(
+ Temporal.PlainDate.compare(new Temporal.PlainDate(1976, 11, 18), zdt),
+ 1, "different date, ZDT second");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/basic.js
new file mode 100644
index 0000000000..ffb86010e0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/basic.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.plaindate.compare
+description: basic tests
+features: [Temporal]
+---*/
+
+const d1 = Temporal.PlainDate.from("1976-11-18");
+const d2 = Temporal.PlainDate.from("2019-06-30");
+const d3 = Temporal.PlainDate.from("2019-06-30");
+assert.sameValue(Temporal.PlainDate.compare(d1, d1), 0, "same object");
+assert.sameValue(Temporal.PlainDate.compare(d1, d2), -1, "earlier");
+assert.sameValue(Temporal.PlainDate.compare(d2, d1), 1, "later");
+assert.sameValue(Temporal.PlainDate.compare(d2, d3), 0, "same date");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/builtin.js
new file mode 100644
index 0000000000..943c3e4c9b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/builtin.js
@@ -0,0 +1,33 @@
+// |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.plaindate.compare
+description: Tests that Temporal.PlainDate.compare meets the requirements for built-in objects
+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.PlainDate.compare),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDate.compare),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDate.compare),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDate.compare.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/calendar-datefromfields-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/calendar-datefromfields-called-with-options-undefined.js
new file mode 100644
index 0000000000..4884cd37a8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/calendar-datefromfields-called-with-options-undefined.js
@@ -0,0 +1,18 @@
+// |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.plaindate.prototype.compare
+description: >
+ Calendar.dateFromFields method is called with undefined as the options value
+ when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
+Temporal.PlainDate.compare({ year: 2000, month: 5, day: 2, calendar }, { year: 2000, month: 5, day: 3, calendar });
+assert.sameValue(calendar.dateFromFieldsCallCount, 2);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/calendar-fields-iterable.js
new file mode 100644
index 0000000000..08ec8dd712
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/calendar-fields-iterable.js
@@ -0,0 +1,41 @@
+// |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.plaindate.compare
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plaindate.compare steps 1–2:
+ 1. Set _one_ to ? ToTemporalDate(_one_).
+ 2. Set _two_ to ? ToTemporalDate(_two_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+Temporal.PlainDate.compare(
+ { year: 2000, month: 5, day: 2, calendar: calendar1 },
+ { year: 2001, month: 6, day: 3, calendar: calendar2 },
+);
+
+assert.sameValue(calendar1.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar1.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar1.iteratorExhausted[0], "iterated through the whole iterable");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/calendar-temporal-object.js
new file mode 100644
index 0000000000..e7f8b6216a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/calendar-temporal-object.js
@@ -0,0 +1,32 @@
+// |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.plaindate.compare
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plaindate.compare steps 1–2:
+ 1. Set _one_ to ? ToTemporalDate(_one_).
+ 2. Set _two_ to ? ToTemporalDate(_two_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ Temporal.PlainDate.compare(
+ { year: 2000, month: 5, day: 2, calendar: temporalObject },
+ { year: 2001, month: 6, day: 3, calendar: temporalObject },
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/calendar.js
new file mode 100644
index 0000000000..6089850753
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/calendar.js
@@ -0,0 +1,40 @@
+// |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.plaindate.compare
+description: basic tests
+features: [Temporal]
+---*/
+
+class CalendarTraceToString extends Temporal.Calendar {
+ constructor(id) {
+ super("iso8601");
+ this.id_ = id;
+ this.calls = 0;
+ }
+ toString() {
+ ++this.calls;
+ return this.id_;
+ }
+};
+
+const calendar1 = new CalendarTraceToString("a");
+const date1 = new Temporal.PlainDate(1914, 2, 23, calendar1);
+
+const calendar2 = new CalendarTraceToString("a");
+const date2 = new Temporal.PlainDate(1914, 2, 23, calendar2);
+
+const calendar3 = new CalendarTraceToString("b");
+const date3 = new Temporal.PlainDate(1914, 2, 23, calendar3);
+
+assert.sameValue(Temporal.PlainDate.compare(date1, date1), 0, "same object");
+assert.sameValue(Temporal.PlainDate.compare(date1, date2), 0, "same date");
+assert.sameValue(Temporal.PlainDate.compare(date1, date3), 0, "same date, different calendar");
+
+assert.sameValue(calendar1.calls, 0, "calendar1 toString() calls");
+assert.sameValue(calendar2.calls, 0, "calendar2 toString() calls");
+assert.sameValue(calendar3.calls, 0, "calendar3 toString() calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/exhaustive.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/exhaustive.js
new file mode 100644
index 0000000000..9858787bcb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/exhaustive.js
@@ -0,0 +1,71 @@
+// |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.plaindate.compare
+description: Tests for compare() with each possible outcome
+features: [Temporal]
+---*/
+
+const cal1 = "iso8601";
+const cal2 = new (class extends Temporal.Calendar { id = "custom"; })("iso8601");
+
+assert.sameValue(
+ Temporal.PlainDate.compare(
+ new Temporal.PlainDate(2000, 5, 31, cal1),
+ new Temporal.PlainDate(1987, 5, 31, cal2)
+ ),
+ 1,
+ "year >"
+);
+assert.sameValue(
+ Temporal.PlainDate.compare(
+ new Temporal.PlainDate(1981, 12, 15, cal1),
+ new Temporal.PlainDate(2048, 12, 15, cal2)
+ ),
+ -1,
+ "year <"
+);
+assert.sameValue(
+ Temporal.PlainDate.compare(
+ new Temporal.PlainDate(2000, 5, 31, cal1),
+ new Temporal.PlainDate(2000, 3, 31, cal2)
+ ),
+ 1,
+ "month >"
+);
+assert.sameValue(
+ Temporal.PlainDate.compare(
+ new Temporal.PlainDate(1981, 4, 15, cal1),
+ new Temporal.PlainDate(1981, 12, 15, cal2)
+ ),
+ -1,
+ "month <"
+);
+assert.sameValue(
+ Temporal.PlainDate.compare(
+ new Temporal.PlainDate(2000, 5, 31, cal1),
+ new Temporal.PlainDate(2000, 5, 14, cal2)
+ ),
+ 1,
+ "day >"
+);
+assert.sameValue(
+ Temporal.PlainDate.compare(
+ new Temporal.PlainDate(1981, 4, 15, cal1),
+ new Temporal.PlainDate(1981, 4, 21, cal2)
+ ),
+ -1,
+ "day <"
+);
+assert.sameValue(
+ Temporal.PlainDate.compare(
+ new Temporal.PlainDate(2000, 5, 31, cal1),
+ new Temporal.PlainDate(2000, 5, 31, cal2)
+ ),
+ 0,
+ "="
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..0f62ef3be2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/infinity-throws-rangeerror.js
@@ -0,0 +1,33 @@
+// |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.
+
+/*---
+description: Throws if any value in a property bag for either argument is Infinity or -Infinity
+esid: sec-temporal.plaindate.compare
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const other = new Temporal.PlainDate(2000, 5, 2);
+const base = { year: 2000, month: 5, day: 2 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day"].forEach((prop) => {
+ assert.throws(RangeError, () => Temporal.PlainDate.compare({ ...base, [prop]: inf }, other), `${prop} property cannot be ${inf}`);
+
+ assert.throws(RangeError, () => Temporal.PlainDate.compare(other, { ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls1 = [];
+ const obj1 = TemporalHelpers.toPrimitiveObserver(calls1, inf, prop);
+ assert.throws(RangeError, () => Temporal.PlainDate.compare({ ...base, [prop]: obj1 }, other));
+ assert.compareArray(calls1, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+
+ const calls2 = [];
+ const obj2 = TemporalHelpers.toPrimitiveObserver(calls2, inf, prop);
+ assert.throws(RangeError, () => Temporal.PlainDate.compare(other, { ...base, [prop]: obj2 }));
+ assert.compareArray(calls2, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/leap-second.js
new file mode 100644
index 0000000000..a815452419
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/leap-second.js
@@ -0,0 +1,23 @@
+// |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.plaindate.compare
+description: Leap second is a valid ISO string for PlainDate
+features: [Temporal]
+---*/
+
+let arg = "2016-12-31T23:59:60";
+let result = Temporal.PlainDate.compare(arg, new Temporal.PlainDate(2016, 12, 31));
+assert.sameValue(result, 0, "leap second is a valid ISO string for PlainDate (first argument)");
+result = Temporal.PlainDate.compare(new Temporal.PlainDate(2016, 12, 31), arg);
+assert.sameValue(result, 0, "leap second is a valid ISO string for PlainDate (second argument)");
+
+arg = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+result = Temporal.PlainDate.compare(arg, new Temporal.PlainDate(2016, 12, 31));
+assert.sameValue(result, 0, "second: 60 is ignored in property bag for PlainDate (first argument)");
+result = Temporal.PlainDate.compare(new Temporal.PlainDate(2016, 12, 31), arg);
+assert.sameValue(result, 0, "second: 60 is ignored in property bag for PlainDate (second argument)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/length.js
new file mode 100644
index 0000000000..97245bc5b9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/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.plaindate.compare
+description: Temporal.PlainDate.compare.length is 2
+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.PlainDate.compare, "length", {
+ value: 2,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/name.js
new file mode 100644
index 0000000000..ea93f5cc06
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/name.js
@@ -0,0 +1,26 @@
+// |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.plaindate.compare
+description: Temporal.PlainDate.compare.name is "compare"
+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.PlainDate.compare, "name", {
+ value: "compare",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/not-a-constructor.js
new file mode 100644
index 0000000000..d7ee33d50d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/not-a-constructor.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.plaindate.compare
+description: Temporal.PlainDate.compare 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.PlainDate.compare();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDate.compare), false,
+ "isConstructor(Temporal.PlainDate.compare)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/prop-desc.js
new file mode 100644
index 0000000000..39f85f1760
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/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.plaindate.compare
+description: The "compare" property of Temporal.PlainDate
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDate.compare,
+ "function",
+ "`typeof PlainDate.compare` is `function`"
+);
+
+verifyProperty(Temporal.PlainDate, "compare", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/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/PlainDate/compare/use-internal-slots.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/use-internal-slots.js
new file mode 100644
index 0000000000..2f8a0c4a2a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/use-internal-slots.js
@@ -0,0 +1,29 @@
+// |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-compareisodate
+description: compare() ignores the observable properties and uses internal slots
+features: [Temporal]
+---*/
+
+function CustomError() {}
+
+class AvoidGettersDate extends Temporal.PlainDate {
+ get year() {
+ throw new CustomError();
+ }
+ get month() {
+ throw new CustomError();
+ }
+ get day() {
+ throw new CustomError();
+ }
+}
+
+const one = new AvoidGettersDate(2000, 5, 2);
+const two = new AvoidGettersDate(2006, 3, 25);
+assert.sameValue(Temporal.PlainDate.compare(one, two), -1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/year-zero.js
new file mode 100644
index 0000000000..4af85b15ae
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/year-zero.js
@@ -0,0 +1,31 @@
+// |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.
+
+/*---
+description: Negative zero, as an extended year, fails
+esid: sec-temporal.plaindate.compare
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T00:45",
+ "-000000-10-31T00:45+01:00",
+ "-000000-10-31T00:45+00:00[UTC]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(RangeError,
+ () => Temporal.PlainDate.compare(arg, instance),
+ "Minus zero is an invalid extended year (first argument)"
+ );
+
+ assert.throws(RangeError,
+ () => Temporal.PlainDate.compare(instance, arg),
+ "Minus zero is an invalid extended year (second argument)"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/constructor.js
new file mode 100644
index 0000000000..091305f0e3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/constructor.js
@@ -0,0 +1,15 @@
+// |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.plaindate
+description: Temporal.PlainDate constructor cannot be called as a function
+info: |
+ 1. If NewTarget is undefined, throw a TypeError exception.
+features: [Temporal]
+---*/
+
+assert.throws(TypeError, () => Temporal.PlainDate(1970, 1, 2));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..4bc627ff84
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-builtin-calendar-no-array-iteration.js
@@ -0,0 +1,23 @@
+// |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.plaindate.from
+description: >
+ Calling the method with a property bag argument with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const arg = { year: 2000, month: 5, day: 2, calendar: "iso8601" };
+Temporal.PlainDate.from(arg);
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-calendar-datefromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..0a72943f56
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-calendar-datefromfields-called-with-null-prototype-fields.js
@@ -0,0 +1,18 @@
+// |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.plaindate.from
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const arg = { year: 2000, month: 5, day: 2, calendar };
+Temporal.PlainDate.from(arg);
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..a3e0d96bf8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-constructor-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |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.plaindate.prototype.from
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+
+assert.throws(RangeError, () => Temporal.PlainDate.from(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-duplicate-calendar-fields.js
new file mode 100644
index 0000000000..bf3482afba
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-duplicate-calendar-fields.js
@@ -0,0 +1,18 @@
+// |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.plaindate.prototype.from
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['day'], ['month'], ['monthCode'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar };
+
+ assert.throws(RangeError, () => Temporal.PlainDate.from(arg));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-leap-second.js
new file mode 100644
index 0000000000..c3436f7d6b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-leap-second.js
@@ -0,0 +1,44 @@
+// |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.plaindate.from
+description: Leap second is a valid ISO string for PlainDate
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+let arg = "2016-12-31T23:59:60";
+
+const result1 = Temporal.PlainDate.from(arg);
+TemporalHelpers.assertPlainDate(
+ result1,
+ 2016, 12, "M12", 31,
+ "leap second is a valid ISO string for PlainDate"
+);
+
+const result2 = Temporal.PlainDate.from(arg, { overflow: "reject" });
+TemporalHelpers.assertPlainDate(
+ result2,
+ 2016, 12, "M12", 31,
+ "leap second is a valid ISO string for PlainDate"
+);
+
+arg = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+
+const result3 = Temporal.PlainDate.from(arg);
+TemporalHelpers.assertPlainDate(
+ result3,
+ 2016, 12, "M12", 31,
+ "second: 60 is ignored in property bag for PlainDate"
+);
+
+const result4 = Temporal.PlainDate.from(arg, { overflow: "reject" });
+TemporalHelpers.assertPlainDate(
+ result4,
+ 2016, 12, "M12", 31,
+ "second: 60 is ignored in property bag for PlainDate even with overflow: reject"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-number.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-number.js
new file mode 100644
index 0000000000..492564b840
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-number.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.plaindate.from
+description: A number cannot be used in place of a Temporal.PlainDate
+features: [Temporal]
+---*/
+
+const numbers = [
+ 1,
+ 19761118,
+ -19761118,
+ 1234567890,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => Temporal.PlainDate.from(arg),
+ 'Numbers cannot be used in place of an ISO string for PlainDate'
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-object-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-object-invalid.js
new file mode 100644
index 0000000000..5c683bcff3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-object-invalid.js
@@ -0,0 +1,44 @@
+// |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.plaindate.from
+description: Property bag is correctly converted into PlainDate
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const badFields = { year: 2019, month: 1, day: 32 };
+assert.throws(RangeError, () => Temporal.PlainDate.from(badFields, { overflow: "reject" }),
+ "bad fields with reject");
+TemporalHelpers.assertPlainDate(Temporal.PlainDate.from(badFields),
+ 2019, 1, "M01", 31, "bad fields with missing overflow");
+TemporalHelpers.assertPlainDate(Temporal.PlainDate.from(badFields, { overflow: "constrain" }),
+ 2019, 1, "M01", 31, "bad fields with constrain");
+
+assert.throws(RangeError,
+ () => Temporal.PlainDate.from({ year: 1976, month: 11, monthCode: "M12", day: 18 }),
+ "month and monthCode must agree");
+
+assert.throws(TypeError,
+ () => Temporal.PlainDate.from({ year: 2019, day: 15 }),
+ "missing month");
+
+assert.throws(TypeError,
+ () => Temporal.PlainDate.from({}),
+ "no properties");
+
+assert.throws(TypeError,
+ () => Temporal.PlainDate.from({ month: 12 }),
+ "missing year, day");
+
+assert.throws(TypeError,
+ () => Temporal.PlainDate.from({ year: 1976, months: 11, day: 18 }),
+ "misspelled month");
+
+assert.throws(TypeError,
+ () => Temporal.PlainDate.from({ year: undefined, month: 11, day: 18 }),
+ "year undefined");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-object-valid.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-object-valid.js
new file mode 100644
index 0000000000..c02ee292b4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-object-valid.js
@@ -0,0 +1,36 @@
+// |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.plaindate.from
+description: Property bag is correctly converted into PlainDate
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const valid = [
+ [
+ { year: 2019, month: 10, monthCode: "M10", day: 1, hour: 14, minute: 20, second: 36 },
+ 2019, 10, "M10", 1
+ ],
+ [
+ { year: 1976, month: 11, day: 18 },
+ 1976, 11, "M11", 18
+ ],
+ [
+ { year: 1976, monthCode: "M11", day: 18 },
+ 1976, 11, "M11", 18
+ ],
+ [
+ { year: 1976, month: 11, day: 18, days: 15 },
+ 1976, 11, "M11", 18
+ ],
+];
+
+for (const [dateTimeFields, ...expected] of valid) {
+ const plainDate = Temporal.PlainDate.from(dateTimeFields);
+ TemporalHelpers.assertPlainDate(plainDate, ...expected, `from(${JSON.stringify(dateTimeFields)}`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-plaindate.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-plaindate.js
new file mode 100644
index 0000000000..38c381790e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-plaindate.js
@@ -0,0 +1,29 @@
+// |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.plaindate.from
+description: A PlainDate object is copied, not returned directly
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const orig = new Temporal.PlainDate(2000, 5, 2);
+const result = Temporal.PlainDate.from(orig);
+
+TemporalHelpers.assertPlainDate(
+ result,
+ 2000, 5, "M05", 2,
+ "PlainDate is copied"
+);
+
+assert.sameValue(result.getISOFields().calendar, orig.getISOFields().calendar, "Calendar is copied");
+
+assert.notSameValue(
+ result,
+ orig,
+ "When a PlainDate is given, the returned value is not the original PlainDate"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-plaindatetime.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-plaindatetime.js
new file mode 100644
index 0000000000..a98ce76168
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-plaindatetime.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.plaindate.from
+description: Fast path for converting Temporal.PlainDateTime to Temporal.PlainDate by reading internal slots
+info: |
+ sec-temporal.plaindate.from step 3:
+ 3. Return ? ToTemporalDate(_item_, _options_).
+ sec-temporal-totemporaldate step 2.b:
+ b. If _item_ has an [[InitializedTemporalDateTime]] internal slot, then
+ i. Return ! CreateTemporalDate(_item_.[[ISOYear]], _item_.[[ISOMonth]], _item_.[[ISODay]], _item_.[[Calendar]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkPlainDateTimeConversionFastPath((datetime, calendar) => {
+ const result = Temporal.PlainDate.from(datetime);
+ TemporalHelpers.assertPlainDate(result, 2000, 5, "M05", 2);
+ assert.sameValue(result.getCalendar(), calendar, "calendar result");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar-case-insensitive.js
new file mode 100644
index 0000000000..877c77b77b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar-case-insensitive.js
@@ -0,0 +1,18 @@
+// |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.plaindate.from
+description: The calendar name is case-insensitive
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = "IsO8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = Temporal.PlainDate.from(arg);
+TemporalHelpers.assertPlainDate(result, 1976, 11, "M11", 18, "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar-leap-second.js
new file mode 100644
index 0000000000..21fea60629
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar-leap-second.js
@@ -0,0 +1,22 @@
+// |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.plaindate.from
+description: Leap second is a valid ISO string for a calendar in a property bag
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = "2016-12-31T23:59:60";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = Temporal.PlainDate.from(arg);
+TemporalHelpers.assertPlainDate(
+ result,
+ 1976, 11, "M11", 18,
+ "leap second is a valid ISO string for calendar"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar-number.js
new file mode 100644
index 0000000000..f33a84fb5e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar-number.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.plaindate.from
+description: A number as calendar in a property bag is not accepted
+features: [Temporal]
+---*/
+
+const numbers = [
+ 1,
+ 19970327,
+ -19970327,
+ 1234567890,
+];
+
+for (const calendar of numbers) {
+ const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+ assert.throws(
+ TypeError,
+ () => Temporal.PlainDate.from(arg),
+ "Numbers cannot be used as a calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar-string.js
new file mode 100644
index 0000000000..405ef9d022
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar-string.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.plaindate.from
+description: A calendar ID is valid input for Calendar
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = "iso8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = Temporal.PlainDate.from(arg);
+TemporalHelpers.assertPlainDate(result, 1976, 11, "M11", 18, `Calendar created from string "${calendar}"`);
+assert.sameValue(result.getISOFields().calendar, "iso8601", "calendar slot stores a string");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar-wrong-type.js
new file mode 100644
index 0000000000..0fc09911a1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar-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.plaindate.from
+description: >
+ Appropriate error thrown when a calendar property from a property bag cannot
+ be converted to a calendar object or string
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [calendar, description] of primitiveTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ typeof calendar === 'string' ? RangeError : TypeError,
+ () => Temporal.PlainDate.from(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+ [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields()
+];
+
+for (const [calendar, description] of typeErrorTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(TypeError, () => Temporal.PlainDate.from(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/PlainDate/from/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar-year-zero.js
new file mode 100644
index 0000000000..fc65d8fd12
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar-year-zero.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.plaindate.from
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T17:45",
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+01:00",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDate.from(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar.js
new file mode 100644
index 0000000000..8c9ae4c2a9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar.js
@@ -0,0 +1,17 @@
+// |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.plaindate.from
+description: Property bag is correctly converted into PlainDate
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = new Temporal.Calendar("iso8601");
+const plainDate = Temporal.PlainDate.from({ year: 1976, month: 11, day: 18, calendar });
+TemporalHelpers.assertPlainDate(plainDate, 1976, 11, "M11", 18);
+assert.sameValue(plainDate.getISOFields().calendar, "iso8601", "calendar slot should store a string");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-proto-in-calendar-fields.js
new file mode 100644
index 0000000000..19253021b2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-proto-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |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.plaindate.prototype.from
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+
+assert.throws(RangeError, () => Temporal.PlainDate.from(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..d7d6696d2f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-calendar-annotation.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.plaindate.from
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2000-05-02[u-ca=iso8601]", "without time or time zone"],
+ ["2000-05-02[UTC][u-ca=iso8601]", "with time zone and no time"],
+ ["2000-05-02T15:23[u-ca=iso8601]", "without time zone"],
+ ["2000-05-02T15:23[UTC][u-ca=iso8601]", "with time zone"],
+ ["2000-05-02T15:23[!u-ca=iso8601]", "with ! and no time zone"],
+ ["2000-05-02T15:23[UTC][!u-ca=iso8601]", "with ! and time zone"],
+ ["2000-05-02T15:23[u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.PlainDate.from(arg);
+
+ TemporalHelpers.assertPlainDate(
+ result,
+ 2000, 5, "M05", 2,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..b1ac214b95
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-critical-unknown-annotation.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.plaindate.from
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[!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]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDate.from(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..14cba00fe5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-date-with-utc-offset.js
@@ -0,0 +1,47 @@
+// |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.plaindate.from
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const validStrings = [
+ "2000-05-02T00+00:00",
+ "2000-05-02T00+00:00[UTC]",
+ "2000-05-02T00+00:00[!UTC]",
+ "2000-05-02T00-02:30[America/St_Johns]",
+];
+
+for (const arg of validStrings) {
+ const result = Temporal.PlainDate.from(arg);
+
+ TemporalHelpers.assertPlainDate(
+ result,
+ 2000, 5, "M05", 2,
+ `"${arg}" is a valid UTC offset with time for PlainDate`
+ );
+}
+
+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,
+ () => Temporal.PlainDate.from(arg),
+ `"${arg}" UTC offset without time is not valid for PlainDate`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-invalid.js
new file mode 100644
index 0000000000..d8cd182a20
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-invalid.js
@@ -0,0 +1,63 @@
+// |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.plaindate.from
+description: >
+ RangeError thrown if an invalid ISO string (or syntactically valid ISO string
+ that is not supported) is used as a PlainDate
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ // invalid ISO strings:
+ "",
+ "invalid iso8601",
+ "2020-01-00",
+ "2020-01-32",
+ "2020-02-30",
+ "2021-02-29",
+ "2020-00-01",
+ "2020-13-01",
+ "2020-01-01T",
+ "2020-01-01T25:00:00",
+ "2020-01-01T01:60:00",
+ "2020-01-01T01:60:61",
+ "2020-01-01junk",
+ "2020-01-01T00:00:00junk",
+ "2020-01-01T00:00:00+00:00junk",
+ "2020-01-01T00:00:00+00:00[UTC]junk",
+ "2020-01-01T00:00:00+00:00[UTC][u-ca=iso8601]junk",
+ "02020-01-01",
+ "2020-001-01",
+ "2020-01-001",
+ "2020-01-01T001",
+ "2020-01-01T01:001",
+ "2020-01-01T01:01:001",
+ // valid, but forms not supported in Temporal:
+ "2020-W01-1",
+ "2020-001",
+ "+0002020-01-01",
+ // valid, but this calendar must not exist:
+ "2020-01-01[u-ca=notexist]",
+ // may be valid in other contexts, but insufficient information for PlainDate:
+ "2020-01",
+ "+002020-01",
+ "01-01",
+ "2020-W01",
+ "P1Y",
+ "-P12Y",
+ // valid, but outside the supported range:
+ "-999999-01-01",
+ "+999999-01-01",
+];
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDate.from(arg),
+ `"${arg}" should not be a valid ISO string for a PlainDate`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..a305783053
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/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.plaindate.from
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[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]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDate.from(arg),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..13fd43cbcb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-multiple-time-zone.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.plaindate.from
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[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]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDate.from(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-time-separators.js
new file mode 100644
index 0000000000..6333981784
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-time-separators.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.plaindate.from
+description: Time separator in string argument can vary
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2000-05-02T15:23", "uppercase T"],
+ ["2000-05-02t15:23", "lowercase T"],
+ ["2000-05-02 15:23", "space between date and time"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.PlainDate.from(arg);
+
+ TemporalHelpers.assertPlainDate(
+ result,
+ 2000, 5, "M05", 2,
+ `variant time separators (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..6593865f70
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-time-zone-annotation.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.plaindate.from
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2000-05-02[Asia/Kolkata]", "named, with no time"],
+ ["2000-05-02[!Europe/Vienna]", "named, with ! and no time"],
+ ["2000-05-02[+00:00]", "numeric, with no time"],
+ ["2000-05-02[!-02:30]", "numeric, with ! and no time"],
+ ["2000-05-02T15:23[America/Sao_Paulo]", "named, with no offset"],
+ ["2000-05-02T15:23[!Asia/Tokyo]", "named, with ! and no offset"],
+ ["2000-05-02T15:23[-02:30]", "numeric, with no offset"],
+ ["2000-05-02T15:23[!+00:00]", "numeric, with ! and no offset"],
+ ["2000-05-02T15:23+00:00[America/New_York]", "named, with offset"],
+ ["2000-05-02T15:23+00:00[!UTC]", "named, with offset and !"],
+ ["2000-05-02T15:23+00:00[+01:00]", "numeric, with offset"],
+ ["2000-05-02T15:23+00:00[!-08:00]", "numeric, with offset and !"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.PlainDate.from(arg);
+
+ TemporalHelpers.assertPlainDate(
+ result,
+ 2000, 5, "M05", 2,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-trailing-junk.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-trailing-junk.js
new file mode 100644
index 0000000000..d14560a938
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-trailing-junk.js
@@ -0,0 +1,14 @@
+// |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.plaindate.from
+description: RangeError thrown if a string with trailing junk is used as a PlainDate
+features: [Temporal, arrow-function]
+---*/
+
+assert.throws(RangeError, () => Temporal.PlainDate.from("1976-11-18junk"),
+ "String with trailing junk should not be valid as a PlainDate");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..ff0eb36df6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-unknown-annotation.js
@@ -0,0 +1,31 @@
+// |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.plaindate.from
+description: Various forms of unknown annotation
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2000-05-02[foo=bar]", "without time"],
+ ["2000-05-02T15:23[foo=bar]", "alone"],
+ ["2000-05-02T15:23[UTC][foo=bar]", "with time zone"],
+ ["2000-05-02T15:23[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["2000-05-02T15:23[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["2000-05-02T15:23[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.PlainDate.from(arg);
+
+ TemporalHelpers.assertPlainDate(
+ result,
+ 2000, 5, "M05", 2,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..bdcf201bc4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-with-utc-designator.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.plaindate.from
+description: RangeError thrown if a string with UTC designator is used as a PlainDate
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "2019-10-01T09:00:00Z",
+ "2019-10-01T09:00:00Z[UTC]",
+];
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDate.from(arg),
+ "String with UTC designator should not be valid as a PlainDate"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string.js
new file mode 100644
index 0000000000..c56d47ca2c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string.js
@@ -0,0 +1,42 @@
+// |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.plaindate.from
+description: various interesting string arguments.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const tests = [
+ ["1976-11-18", 1976, 11, "M11", 18],
+ ["2019-06-30", 2019, 6, "M06", 30],
+ ["+000050-06-30", 50, 6, "M06", 30],
+ ["+010583-06-30", 10583, 6, "M06", 30],
+ ["-010583-06-30", -10583, 6, "M06", 30],
+ ["-000333-06-30", -333, 6, "M06", 30],
+ ["19761118", 1976, 11, "M11", 18],
+ ["+0019761118", 1976, 11, "M11", 18],
+ ["1976-11-18T152330.1+00:00", 1976, 11, "M11", 18],
+ ["19761118T15:23:30.1+00:00", 1976, 11, "M11", 18],
+ ["1976-11-18T15:23:30.1+0000", 1976, 11, "M11", 18],
+ ["1976-11-18T152330.1+0000", 1976, 11, "M11", 18],
+ ["19761118T15:23:30.1+0000", 1976, 11, "M11", 18],
+ ["19761118T152330.1+00:00", 1976, 11, "M11", 18],
+ ["19761118T152330.1+0000", 1976, 11, "M11", 18],
+ ["+001976-11-18T152330.1+00:00", 1976, 11, "M11", 18],
+ ["+0019761118T15:23:30.1+00:00", 1976, 11, "M11", 18],
+ ["+001976-11-18T15:23:30.1+0000", 1976, 11, "M11", 18],
+ ["+001976-11-18T152330.1+0000", 1976, 11, "M11", 18],
+ ["+0019761118T15:23:30.1+0000", 1976, 11, "M11", 18],
+ ["+0019761118T152330.1+00:00", 1976, 11, "M11", 18],
+ ["+0019761118T152330.1+0000", 1976, 11, "M11", 18],
+];
+
+for (const [input, ...expected] of tests) {
+ const result = Temporal.PlainDate.from(input);
+ TemporalHelpers.assertPlainDate(result, ...expected, `from(${input})`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-wrong-type.js
new file mode 100644
index 0000000000..8f7cf439c2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-wrong-type.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.plaindate.from
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainDate
+features: [BigInt, Symbol, Temporal]
+---*/
+
+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,
+ () => Temporal.PlainDate.from(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.PlainDate, "Temporal.PlainDate, object"],
+ [Temporal.PlainDate.prototype, "Temporal.PlainDate.prototype, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => Temporal.PlainDate.from(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/PlainDate/from/argument-zoneddatetime-convert.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-zoneddatetime-convert.js
new file mode 100644
index 0000000000..0134e8ab9a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-zoneddatetime-convert.js
@@ -0,0 +1,22 @@
+// |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.plaindate.from
+description: An exception from TimeZone#getOffsetNanosecondsFor() is propagated.
+features: [Temporal]
+---*/
+
+class TZ extends Temporal.TimeZone {
+ constructor() { super("UTC") }
+ getOffsetNanosecondsFor() { throw new Test262Error() }
+}
+
+const tz = new TZ();
+const arg = new Temporal.ZonedDateTime(0n, tz);
+const instance = new Temporal.PlainDate(1976, 11, 18);
+
+assert.throws(Test262Error, () => Temporal.PlainDate.from(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-zoneddatetime-slots.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-zoneddatetime-slots.js
new file mode 100644
index 0000000000..31e2dea78c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-zoneddatetime-slots.js
@@ -0,0 +1,39 @@
+// |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.plaindate.from
+description: Getters are not called when converting a ZonedDateTime to a PlainDate.
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.ZonedDateTime.prototype);
+const getters = ["year", "month", "monthCode", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond", "calendar"];
+
+for (const property of getters) {
+ Object.defineProperty(Temporal.ZonedDateTime.prototype, property, {
+ get() {
+ actual.push(`get ${property}`);
+ const value = prototypeDescrs[property].get.call(this);
+ return {
+ toString() {
+ actual.push(`toString ${property}`);
+ return value.toString();
+ },
+ valueOf() {
+ actual.push(`valueOf ${property}`);
+ return value;
+ },
+ };
+ },
+ });
+}
+
+const arg = new Temporal.ZonedDateTime(0n, "UTC");
+Temporal.PlainDate.from(arg);
+assert.compareArray(actual, []);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..7b4c3b2721
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,18 @@
+// |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.plaindate.from
+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].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => Temporal.PlainDate.from(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..f8c5a7164c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.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.plaindate.from
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => Temporal.PlainDate.from(datetime),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..775095b425
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,18 @@
+// |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.plaindate.from
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => Temporal.PlainDate.from(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..cca5f38eb7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.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.plaindate.from
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => Temporal.PlainDate.from(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-zoneddatetime.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-zoneddatetime.js
new file mode 100644
index 0000000000..1601aa288a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-zoneddatetime.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.plaindate.from
+description: A ZonedDateTime object is handled separately
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = new Temporal.Calendar("iso8601");
+const zdt = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC", calendar);
+const result = Temporal.PlainDate.from(zdt);
+
+TemporalHelpers.assertPlainDate(
+ result,
+ 2001, 9, "M09", 9,
+ "ZonedDateTime is converted"
+);
+
+assert.sameValue(
+ result.getCalendar(),
+ calendar,
+ "Calendar is copied"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/builtin.js
new file mode 100644
index 0000000000..2658a4878b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/builtin.js
@@ -0,0 +1,33 @@
+// |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.plaindate.from
+description: Tests that Temporal.PlainDate.from meets the requirements for built-in objects
+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.PlainDate.from),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDate.from),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDate.from),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDate.from.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/calendar-fields-custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/calendar-fields-custom.js
new file mode 100644
index 0000000000..8c14dc3d6b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/calendar-fields-custom.js
@@ -0,0 +1,38 @@
+// |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.plaindate.from
+description: Verify the result of calendar.fields() is treated correctly.
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ dateFromFields(fields) {
+ assert.compareArray(Object.keys(fields), ["a", "b"]);
+ return new Temporal.PlainDate(2020, 7, 4);
+ }
+ fields(fields) {
+ assert.compareArray(fields, ["day", "month", "monthCode", "year"]);
+ return ["b", "a"];
+ }
+}
+
+const calendar = new CustomCalendar();
+const actual = [];
+const item = TemporalHelpers.propertyBagObserver(actual, { calendar }, "item");
+
+const plainDate = Temporal.PlainDate.from(item);
+TemporalHelpers.assertPlainDate(plainDate, 2020, 7, "M07", 4);
+assert.compareArray(actual, [
+ "get item.calendar",
+ "get item.a",
+ "get item.b",
+]);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/calendar-fields-iterable.js
new file mode 100644
index 0000000000..3fc52b4530
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/calendar-fields-iterable.js
@@ -0,0 +1,33 @@
+// |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.plaindate.from
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plaindate.from step 3:
+ 3. Return ? ToTemporalDate(_item_, _options_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+Temporal.PlainDate.from({ year: 2000, month: 5, day: 2, calendar });
+
+assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/calendar-temporal-object.js
new file mode 100644
index 0000000000..9d488d4eb1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/calendar-temporal-object.js
@@ -0,0 +1,29 @@
+// |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.plaindate.from
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plaindate.from step 3:
+ 3. Return ? ToTemporalDate(_item_, _options_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject, calendar) => {
+ const result = Temporal.PlainDate.from({ year: 2000, month: 5, day: 2, calendar: temporalObject });
+ assert.sameValue(result.getCalendar(), calendar, "Temporal object coerced to calendar");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..67480076c9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/infinity-throws-rangeerror.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.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plaindate.from
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const base = { year: 2000, month: 5, day: 2 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day"].forEach((prop) => {
+ ["constrain", "reject"].forEach((overflow) => {
+ assert.throws(RangeError, () => Temporal.PlainDate.from({ ...base, [prop]: inf }, { overflow }), `${prop} property cannot be ${inf} (overflow ${overflow}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => Temporal.PlainDate.from({ ...base, [prop]: obj }, { overflow }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/length.js
new file mode 100644
index 0000000000..f6c885ad4d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/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.plaindate.from
+description: Temporal.PlainDate.from.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.PlainDate.from, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/limits.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/limits.js
new file mode 100644
index 0000000000..0d223c582f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/limits.js
@@ -0,0 +1,31 @@
+// |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.plaindate.from
+description: PlainDate.from enforces the supported range
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const tooEarly = { year: -271821, month: 4, day: 18 };
+const tooLate = { year: 275760, month: 9, day: 14 };
+["reject", "constrain"].forEach((overflow) => {
+ [tooEarly, tooLate, "-271821-04-18", "+275760-09-14"].forEach((value) => {
+ assert.throws(RangeError, () => Temporal.PlainDate.from(value, { overflow }),
+ `${JSON.stringify(value)} with ${overflow}`);
+ });
+});
+
+TemporalHelpers.assertPlainDate(Temporal.PlainDate.from({ year: -271821, month: 4, day: 19 }),
+ -271821, 4, "M04", 19, "min object");
+TemporalHelpers.assertPlainDate(Temporal.PlainDate.from({ year: 275760, month: 9, day: 13 }),
+ 275760, 9, "M09", 13, "max object");
+
+TemporalHelpers.assertPlainDate(Temporal.PlainDate.from("-271821-04-19"),
+ -271821, 4, "M04", 19, "min string");
+TemporalHelpers.assertPlainDate(Temporal.PlainDate.from("+275760-09-13"),
+ 275760, 9, "M09", 13, "max string");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/name.js
new file mode 100644
index 0000000000..c1e782a12a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/name.js
@@ -0,0 +1,26 @@
+// |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.plaindate.from
+description: Temporal.PlainDate.from.name is "from"
+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.PlainDate.from, "name", {
+ value: "from",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/not-a-constructor.js
new file mode 100644
index 0000000000..019313865d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/not-a-constructor.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.plaindate.from
+description: Temporal.PlainDate.from 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.PlainDate.from();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDate.from), false,
+ "isConstructor(Temporal.PlainDate.from)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/observable-get-overflow-argument-primitive.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/observable-get-overflow-argument-primitive.js
new file mode 100644
index 0000000000..529b32d6d3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/observable-get-overflow-argument-primitive.js
@@ -0,0 +1,37 @@
+// |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.plaindate.from
+description: overflow property is extracted with string argument.
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "ownKeys options",
+ "getOwnPropertyDescriptor options.overflow",
+ "get options.overflow",
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+];
+
+let actual = [];
+const options = TemporalHelpers.propertyBagObserver(actual, { overflow: "constrain" }, "options");
+
+const result = Temporal.PlainDate.from("2021-05-17", options);
+assert.compareArray(actual, expected, "Successful call");
+TemporalHelpers.assertPlainDate(result, 2021, 5, "M05", 17);
+
+actual.splice(0); // empty it for the next check
+const failureExpected = [
+ "ownKeys options",
+ "getOwnPropertyDescriptor options.overflow",
+ "get options.overflow",
+];
+
+assert.throws(TypeError, () => Temporal.PlainDate.from(7, options));
+assert.compareArray(actual, failureExpected, "Failing call");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/observable-get-overflow-argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/observable-get-overflow-argument-string-invalid.js
new file mode 100644
index 0000000000..7d9308e508
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/observable-get-overflow-argument-string-invalid.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.plaindate.from
+description: overflow property is extracted with ISO-invalid string argument.
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "ownKeys options",
+ "getOwnPropertyDescriptor options.overflow",
+ "get options.overflow",
+];
+
+let actual = [];
+const options = TemporalHelpers.propertyBagObserver(actual, { overflow: "constrain" }, "options");
+
+assert.throws(RangeError, () => Temporal.PlainDate.from("2020-13-34", options));
+assert.compareArray(actual, expected);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/options-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/options-object.js
new file mode 100644
index 0000000000..4a4a89f966
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/options-object.js
@@ -0,0 +1,22 @@
+// |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.plaindate.prototype.from
+description: Empty object may be used as options
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.assertPlainDate(
+ Temporal.PlainDate.from({ year: 1976, month: 11, day: 18 }, {}), 1976, 11, "M11", 18,
+ "options may be an empty plain object"
+);
+
+TemporalHelpers.assertPlainDate(
+ Temporal.PlainDate.from({ year: 1976, month: 11, day: 18 }, () => {}), 1976, 11, "M11", 18,
+ "options may be an empty function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/options-undefined.js
new file mode 100644
index 0000000000..15b317f3ff
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/options-undefined.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.plaindate.from
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const fields = { year: 2000, month: 13, day: 2 };
+
+const explicit = Temporal.PlainDate.from(fields, undefined);
+assert.sameValue(explicit.month, 12, "default overflow is constrain");
+
+const implicit = Temporal.PlainDate.from(fields);
+assert.sameValue(implicit.month, 12, "default overflow is constrain");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/options-wrong-type.js
new file mode 100644
index 0000000000..5c5eb1fd63
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/options-wrong-type.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.plaindate.from
+description: TypeError thrown when options argument is a primitive
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const badOptions = [
+ null,
+ true,
+ "some string",
+ Symbol(),
+ 1,
+ 2n,
+];
+
+for (const value of badOptions) {
+ assert.throws(TypeError, () => Temporal.PlainDate.from({ year: 1976, month: 11, day: 18 }, value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/order-of-operations.js
new file mode 100644
index 0000000000..513ddcf51f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/order-of-operations.js
@@ -0,0 +1,79 @@
+// |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.plaindate.from
+description: Properties on an object passed to from() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "ownKeys options",
+ "getOwnPropertyDescriptor options.overflow",
+ "get options.overflow",
+ "getOwnPropertyDescriptor options.extra",
+ "get options.extra",
+ "get fields.calendar",
+ "has fields.calendar.dateAdd",
+ "has fields.calendar.dateFromFields",
+ "has fields.calendar.dateUntil",
+ "has fields.calendar.day",
+ "has fields.calendar.dayOfWeek",
+ "has fields.calendar.dayOfYear",
+ "has fields.calendar.daysInMonth",
+ "has fields.calendar.daysInWeek",
+ "has fields.calendar.daysInYear",
+ "has fields.calendar.fields",
+ "has fields.calendar.id",
+ "has fields.calendar.inLeapYear",
+ "has fields.calendar.mergeFields",
+ "has fields.calendar.month",
+ "has fields.calendar.monthCode",
+ "has fields.calendar.monthDayFromFields",
+ "has fields.calendar.monthsInYear",
+ "has fields.calendar.weekOfYear",
+ "has fields.calendar.year",
+ "has fields.calendar.yearMonthFromFields",
+ "has fields.calendar.yearOfWeek",
+ "get fields.calendar.dateFromFields",
+ "get fields.calendar.fields",
+ "call fields.calendar.fields",
+ "get fields.day",
+ "get fields.day.valueOf",
+ "call fields.day.valueOf",
+ "get fields.month",
+ "get fields.month.valueOf",
+ "call fields.month.valueOf",
+ "get fields.monthCode",
+ "get fields.monthCode.toString",
+ "call fields.monthCode.toString",
+ "get fields.year",
+ "get fields.year.valueOf",
+ "call fields.year.valueOf",
+ "call fields.calendar.dateFromFields",
+ // inside Calendar.p.dateFromFields
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+];
+const actual = [];
+
+const calendar = TemporalHelpers.calendarObserver(actual, "fields.calendar");
+const fields = TemporalHelpers.propertyBagObserver(actual, {
+ year: 1.7,
+ month: 1.7,
+ monthCode: "M01",
+ day: 1.7,
+ calendar,
+}, "fields");
+
+const options = TemporalHelpers.propertyBagObserver(actual, {
+ overflow: "constrain",
+ extra: "property",
+}, "options");
+
+const result = Temporal.PlainDate.from(fields, options);
+assert.compareArray(actual, expected, "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/overflow-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/overflow-invalid-string.js
new file mode 100644
index 0000000000..29655c52be
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/overflow-invalid-string.js
@@ -0,0 +1,45 @@
+// |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.plaindate.from
+description: RangeError thrown when overflow option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-totemporaldate steps 2–3:
+ 2. If Type(_item_) is Object, then
+ ...
+ g. Return ? DateFromFields(_calendar_, _fields_, _options_).
+ 3. Perform ? ToTemporalOverflow(_options_).
+ sec-temporal.plaindate.from steps 2–3:
+ 2. If Type(_item_) is Object and _item_ has an [[InitializedTemporalDate]] internal slot, then
+ a. Perform ? ToTemporalOverflow(_options_).
+ b. Return ...
+ 3. Return ? ToTemporalDate(_item_, _options_).
+features: [Temporal]
+---*/
+
+const validItems = [
+ new Temporal.PlainDate(2000, 5, 2),
+ new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"),
+ new Temporal.PlainDateTime(2000, 5, 2, 12),
+ { year: 2000, month: 5, day: 2 },
+ "2000-05-02",
+];
+
+const badOverflows = ["", "CONSTRAIN", "balance", "other string", "constra\u0131n", "reject\0"];
+for (const item of validItems) {
+ for (const overflow of badOverflows) {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDate.from(item, { overflow }),
+ `invalid overflow ("${overflow}")`
+ );
+ }
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/overflow-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/overflow-undefined.js
new file mode 100644
index 0000000000..08b28cb336
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/overflow-undefined.js
@@ -0,0 +1,48 @@
+// |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.plaindate.from
+description: Fallback value for overflow option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-totemporaldate steps 2–3:
+ 2. If Type(_item_) is Object, then
+ ...
+ g. Return ? DateFromFields(_calendar_, _fields_, _options_).
+ 3. Perform ? ToTemporalOverflow(_options_).
+ sec-temporal.plaindate.from steps 2–3:
+ 2. If Type(_item_) is Object and _item_ has an [[InitializedTemporalDate]] internal slot, then
+ a. Perform ? ToTemporalOverflow(_options_).
+ b. Return ...
+ 3. Return ? ToTemporalDate(_item_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const validValues = [
+ new Temporal.PlainDate(2000, 5, 2),
+ "2000-05-02",
+];
+validValues.forEach((value) => {
+ const explicit = Temporal.PlainDate.from(value, { overflow: undefined });
+ TemporalHelpers.assertPlainDate(explicit, 2000, 5, "M05", 2, "overflow is ignored");
+ const implicit = Temporal.PlainDate.from(value, {});
+ TemporalHelpers.assertPlainDate(implicit, 2000, 5, "M05", 2, "overflow is ignored");
+ const lambda = Temporal.PlainDate.from(value, () => {});
+ TemporalHelpers.assertPlainDate(lambda, 2000, 5, "M05", 2, "overflow is ignored");
+});
+
+const propertyBag = { year: 2000, month: 13, day: 34 };
+const explicit = Temporal.PlainDate.from(propertyBag, { overflow: undefined });
+TemporalHelpers.assertPlainDate(explicit, 2000, 12, "M12", 31, "default overflow is constrain");
+const implicit = Temporal.PlainDate.from(propertyBag, {});
+TemporalHelpers.assertPlainDate(implicit, 2000, 12, "M12", 31, "default overflow is constrain");
+const lambda = Temporal.PlainDate.from(propertyBag, () => {});
+TemporalHelpers.assertPlainDate(lambda, 2000, 12, "M12", 31, "default overflow is constrain");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/overflow-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/overflow-wrong-type.js
new file mode 100644
index 0000000000..8774278ffd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/overflow-wrong-type.js
@@ -0,0 +1,37 @@
+// |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.plaindate.from
+description: Type conversions for overflow option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-totemporaldate steps 2–3:
+ 2. If Type(_item_) is Object, then
+ ...
+ g. Return ? DateFromFields(_calendar_, _fields_, _options_).
+ 3. Perform ? ToTemporalOverflow(_options_).
+ sec-temporal.plaindate.from steps 2–3:
+ 2. If Type(_item_) is Object and _item_ has an [[InitializedTemporalDate]] internal slot, then
+ a. Perform ? ToTemporalOverflow(_options_).
+ b. Return ...
+ 3. Return ? ToTemporalDate(_item_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const validValues = [
+ new Temporal.PlainDate(2000, 5, 2),
+ { year: 2000, month: 5, day: 2 },
+ "2000-05-02",
+];
+validValues.forEach((value) => TemporalHelpers.checkStringOptionWrongType("overflow", "constrain",
+ (overflow) => Temporal.PlainDate.from(value, { overflow }),
+ (result, descr) => TemporalHelpers.assertPlainDate(result, 2000, 5, "M05", 2, descr),
+));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/prop-desc.js
new file mode 100644
index 0000000000..0664d2fe4c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/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.plaindate.from
+description: The "from" property of Temporal.PlainDate
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDate.from,
+ "function",
+ "`typeof PlainDate.from` is `function`"
+);
+
+verifyProperty(Temporal.PlainDate, "from", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/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/PlainDate/from/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/subclassing-ignored.js
new file mode 100644
index 0000000000..637f254848
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/subclassing-ignored.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.plaindate.from
+description: The receiver is never called when calling from()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnoredStatic(
+ Temporal.PlainDate,
+ "from",
+ ["2000-05-02"],
+ (result) => TemporalHelpers.assertPlainDate(result, 2000, 5, "M05", 2),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/year-zero.js
new file mode 100644
index 0000000000..6bd3006711
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/year-zero.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.plaindate.from
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T00:45",
+ "-000000-10-31T00:45+01:00",
+ "-000000-10-31T00:45+00:00[UTC]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDate.from(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..33355c37ca
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/infinity-throws-rangeerror.js
@@ -0,0 +1,42 @@
+// |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.
+
+/*---
+description: Temporal.PlainDate throws a RangeError if any value is Infinity
+esid: sec-temporal.plaindate
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+assert.throws(RangeError, () => new Temporal.PlainDate(Infinity, 1, 1));
+assert.throws(RangeError, () => new Temporal.PlainDate(1970, Infinity, 1));
+assert.throws(RangeError, () => new Temporal.PlainDate(1970, 1, Infinity));
+
+const O = (primitiveValue, propertyName) => (calls) => TemporalHelpers.toPrimitiveObserver(calls, primitiveValue, propertyName);
+const tests = [
+ [
+ "infinite year",
+ [O(Infinity, "year"), O(1, "month"), O(1, "day")],
+ ["get year.valueOf", "call year.valueOf"]
+ ],
+ [
+ "infinite month",
+ [O(2, "year"), O(Infinity, "month"), O(1, "day")],
+ ["get year.valueOf", "call year.valueOf", "get month.valueOf", "call month.valueOf"]
+ ],
+ [
+ "infinite day",
+ [O(2, "year"), O(1, "month"), O(Infinity, "day")],
+ ["get year.valueOf", "call year.valueOf", "get month.valueOf", "call month.valueOf", "get day.valueOf", "call day.valueOf"]
+ ],
+];
+
+for (const [description, args, expected] of tests) {
+ const actual = [];
+ const args_ = args.map((o) => o(actual));
+ assert.throws(RangeError, () => new Temporal.PlainDate(...args_), description);
+ assert.compareArray(actual, expected, `${description} order of operations`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/length.js
new file mode 100644
index 0000000000..bd19e68bae
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/length.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.plaindate
+description: Temporal.PlainDate.length is 3
+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.PlainDate, "length", {
+ value: 3,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/limits.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/limits.js
new file mode 100644
index 0000000000..10fb6a5e24
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/limits.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.plaindate
+description: Limits for the PlainDate constructor.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+assert.throws(RangeError, () => new Temporal.PlainDate(-271821, 4, 18), "min");
+assert.throws(RangeError, () => new Temporal.PlainDate(275760, 9, 14), "max");
+TemporalHelpers.assertPlainDate(new Temporal.PlainDate(-271821, 4, 19),
+ -271821, 4, "M04", 19, "min");
+TemporalHelpers.assertPlainDate(new Temporal.PlainDate(275760, 9, 13),
+ 275760, 9, "M09", 13, "max");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/missing-arguments.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/missing-arguments.js
new file mode 100644
index 0000000000..86a92f87ef
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/missing-arguments.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.plaindate
+description: RangeError thrown when constructor invoked with no argument
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "valueOf year",
+ "valueOf month",
+];
+const actual = [];
+const args = [
+ { valueOf() { actual.push("valueOf year"); return 1; } },
+ { valueOf() { actual.push("valueOf month"); return 1; } },
+];
+
+assert.throws(RangeError, () => new Temporal.PlainDate(...args));
+assert.compareArray(actual, expected, "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/name.js
new file mode 100644
index 0000000000..f5d4ba06ae
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/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.plaindate
+description: Temporal.PlainDate.name is "PlainDate"
+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.PlainDate, "name", {
+ value: "PlainDate",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/negative-infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/negative-infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..159ce4b9ed
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/negative-infinity-throws-rangeerror.js
@@ -0,0 +1,42 @@
+// |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.
+
+/*---
+description: Temporal.PlainDate throws a RangeError if any value is -Infinity
+esid: sec-temporal.plaindate
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+assert.throws(RangeError, () => new Temporal.PlainDate(-Infinity, 1, 1));
+assert.throws(RangeError, () => new Temporal.PlainDate(1970, -Infinity, 1));
+assert.throws(RangeError, () => new Temporal.PlainDate(1970, 1, -Infinity));
+
+const O = (primitiveValue, propertyName) => (calls) => TemporalHelpers.toPrimitiveObserver(calls, primitiveValue, propertyName);
+const tests = [
+ [
+ "infinite year",
+ [O(-Infinity, "year"), O(1, "month"), O(1, "day")],
+ ["get year.valueOf", "call year.valueOf"]
+ ],
+ [
+ "infinite month",
+ [O(2, "year"), O(-Infinity, "month"), O(1, "day")],
+ ["get year.valueOf", "call year.valueOf", "get month.valueOf", "call month.valueOf"]
+ ],
+ [
+ "infinite day",
+ [O(2, "year"), O(1, "month"), O(-Infinity, "day")],
+ ["get year.valueOf", "call year.valueOf", "get month.valueOf", "call month.valueOf", "get day.valueOf", "call day.valueOf"]
+ ],
+];
+
+for (const [description, args, expected] of tests) {
+ const actual = [];
+ const args_ = args.map((o) => o(actual));
+ assert.throws(RangeError, () => new Temporal.PlainDate(...args_), description);
+ assert.compareArray(actual, expected, `${description} order of operations`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prop-desc.js
new file mode 100644
index 0000000000..b8c8daf832
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/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.plaindate
+description: The "PlainDate" property of Temporal
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDate,
+ "function",
+ "`typeof PlainDate` is `function`"
+);
+
+verifyProperty(Temporal, "PlainDate", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-duration-max.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-duration-max.js
new file mode 100644
index 0000000000..22e0436354
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-duration-max.js
@@ -0,0 +1,58 @@
+// |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.plaindate.prototype.add
+description: Maximum allowed duration
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(1970, 1, 1);
+
+const maxCases = [
+ ["P273790Y8M12DT23H59M59.999999999S", "string with max years"],
+ [{ years: 273790, months: 8, days: 12, nanoseconds: 86399999999999 }, "property bag with max years"],
+ ["P3285488M12DT23H59M59.999999999S", "string with max months"],
+ [{ months: 3285488, days: 12, nanoseconds: 86399999999999 }, "property bag with max months"],
+ ["P14285714W2DT23H59M59.999999999S", "string with max weeks"],
+ [{ weeks: 14285714, days: 2, nanoseconds: 86399999999999 }, "property bag with max weeks"],
+ ["P100000000DT23H59M59.999999999S", "string with max days"],
+ [{ days: 100000000, nanoseconds: 86399999999999 }, "property bag with max days"],
+ ["PT2400000023H59M59.999999999S", "string with max hours"],
+ [{ hours: 2400000023, nanoseconds: 3599999999999 }, "property bag with max hours"],
+ ["PT144000001439M59.999999999S", "string with max minutes"],
+ [{ minutes: 144000001439, nanoseconds: 59999999999 }, "property bag with max minutes"],
+ ["PT8640000086399.999999999S", "string with max seconds"],
+ [{ seconds: 8640000086399, nanoseconds: 999999999 }, "property bag with max seconds"],
+];
+
+for (const [arg, descr] of maxCases) {
+ const result = instance.add(arg);
+ TemporalHelpers.assertPlainDate(result, 275760, 9, "M09", 13, `operation succeeds with ${descr}`);
+}
+
+const minCases = [
+ ["-P273790Y8M12DT23H59M59.999999999S", "string with min years"],
+ [{ years: -273790, months: -8, days: -12, nanoseconds: -86399999999999 }, "property bag with min years"],
+ ["-P3285488M12DT23H59M59.999999999S", "string with min months"],
+ [{ months: -3285488, days: -12, nanoseconds: -86399999999999 }, "property bag with min months"],
+ ["-P14285714W3DT23H59M59.999999999S", "string with min weeks"],
+ [{ weeks: -14285714, days: -3, nanoseconds: -86399999999999 }, "property bag with min weeks"],
+ ["-P100000001DT23H59M59.999999999S", "string with min days"],
+ [{ days: -100000001, nanoseconds: -86399999999999 }, "property bag with min days"],
+ ["-PT2400000047H59M59.999999999S", "string with min hours"],
+ [{ hours: -2400000047, nanoseconds: -3599999999999 }, "property bag with min hours"],
+ ["-PT144000002879M59.999999999S", "string with min minutes"],
+ [{ minutes: -144000002879, nanoseconds: -59999999999 }, "property bag with min minutes"],
+ ["-PT8640000172799.999999999S", "string with min seconds"],
+ [{ seconds: -8640000172799, nanoseconds: -999999999 }, "property bag with min seconds"],
+];
+
+for (const [arg, descr] of minCases) {
+ const result = instance.add(arg);
+ TemporalHelpers.assertPlainDate(result, -271821, 4, "M04", 19, `operation succeeds with ${descr}`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-duration-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-duration-out-of-range.js
new file mode 100644
index 0000000000..6067a9e27c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-duration-out-of-range.js
@@ -0,0 +1,75 @@
+// |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.plaindate.prototype.add
+description: Duration-like argument that is out of range
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(1970, 1, 1);
+
+const cases = [
+ // 2^32 = 4294967296
+ ["P4294967296Y", "string with years > max"],
+ [{ years: 4294967296 }, "property bag with years > max"],
+ ["-P4294967296Y", "string with years < min"],
+ [{ years: -4294967296 }, "property bag with years < min"],
+ ["P4294967296M", "string with months > max"],
+ [{ months: 4294967296 }, "property bag with months > max"],
+ ["-P4294967296M", "string with months < min"],
+ [{ months: -4294967296 }, "property bag with months < min"],
+ ["P4294967296W", "string with weeks > max"],
+ [{ weeks: 4294967296 }, "property bag with weeks > max"],
+ ["-P4294967296W", "string with weeks < min"],
+ [{ weeks: -4294967296 }, "property bag with weeks < min"],
+
+ // ceil(max safe integer / 86400) = 104249991375
+ ["P104249991375D", "string with days > max"],
+ [{ days: 104249991375 }, "property bag with days > max"],
+ ["P104249991374DT24H", "string where hours balance into days > max"],
+ [{ days: 104249991374, hours: 24 }, "property bag where hours balance into days > max"],
+ ["-P104249991375D", "string with days < min"],
+ [{ days: -104249991375 }, "property bag with days < min"],
+ ["-P104249991374DT24H", "string where hours balance into days < min"],
+ [{ days: -104249991374, hours: -24 }, "property bag where hours balance into days < min"],
+
+ // ceil(max safe integer / 3600) = 2501999792984
+ ["PT2501999792984H", "string with hours > max"],
+ [{ hours: 2501999792984 }, "property bag with hours > max"],
+ ["PT2501999792983H60M", "string where minutes balance into hours > max"],
+ [{ hours: 2501999792983, minutes: 60 }, "property bag where minutes balance into hours > max"],
+ ["-PT2501999792984H", "string with hours < min"],
+ [{ hours: -2501999792984 }, "property bag with hours < min"],
+ ["-PT2501999792983H60M", "string where minutes balance into hours < min"],
+ [{ hours: -2501999792983, minutes: -60 }, "property bag where minutes balance into hours < min"],
+
+ // ceil(max safe integer / 60) = 150119987579017
+ ["PT150119987579017M", "string with minutes > max"],
+ [{ minutes: 150119987579017 }, "property bag with minutes > max"],
+ ["PT150119987579016M60S", "string where seconds balance into minutes > max"],
+ [{ minutes: 150119987579016, seconds: 60 }, "property bag where seconds balance into minutes > max"],
+ ["-PT150119987579017M", "string with minutes < min"],
+ [{ minutes: -150119987579017 }, "property bag with minutes < min"],
+ ["-PT150119987579016M60S", "string where seconds balance into minutes < min"],
+ [{ minutes: -150119987579016, seconds: -60 }, "property bag where seconds balance into minutes < min"],
+
+ // 2^53 = 9007199254740992
+ ["PT9007199254740992S", "string with seconds > max"],
+ [{ seconds: 9007199254740992 }, "property bag with seconds > max"],
+ [{ seconds: 9007199254740991, milliseconds: 1000 }, "property bag where milliseconds balance into seconds > max"],
+ [{ seconds: 9007199254740991, microseconds: 1000000 }, "property bag where microseconds balance into seconds > max"],
+ [{ seconds: 9007199254740991, nanoseconds: 1000000000 }, "property bag where nanoseconds balance into seconds > max"],
+ ["-PT9007199254740992S", "string with seconds < min"],
+ [{ seconds: -9007199254740992 }, "property bag with seconds < min"],
+ [{ seconds: -9007199254740991, milliseconds: -1000 }, "property bag where milliseconds balance into seconds < min"],
+ [{ seconds: -9007199254740991, microseconds: -1000000 }, "property bag where microseconds balance into seconds < min"],
+ [{ seconds: -9007199254740991, nanoseconds: -1000000000 }, "property bag where nanoseconds balance into seconds < min"],
+];
+
+for (const [arg, descr] of cases) {
+ assert.throws(RangeError, () => instance.add(arg), `${descr} is out of range`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-invalid-property.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-invalid-property.js
new file mode 100644
index 0000000000..af962db8fb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-invalid-property.js
@@ -0,0 +1,31 @@
+// |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.plaindate.prototype.add
+description: temporalDurationLike object must contain at least one correctly spelled property
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+assert.throws(
+ TypeError,
+ () => instance.add({}),
+ "Throws TypeError if no property is present"
+);
+
+assert.throws(
+ TypeError,
+ () => instance.add({ nonsense: true }),
+ "Throws TypeError if no recognized property is present"
+);
+
+assert.throws(
+ TypeError,
+ () => instance.add({ sign: 1 }),
+ "Sign property is not recognized"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-mixed-sign.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-mixed-sign.js
new file mode 100644
index 0000000000..d5de33beb4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-mixed-sign.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.plaindate.prototype.add
+description: Positive and negative values in the temporalDurationLike argument are not acceptable
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+["constrain", "reject"].forEach((overflow) => {
+ assert.throws(
+ RangeError,
+ () => instance.add({ hours: 1, minutes: -30 }, { overflow }),
+ `mixed positive and negative values always throw (overflow = "${overflow}")`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-not-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-not-object.js
new file mode 100644
index 0000000000..06546bf79e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-not-object.js
@@ -0,0 +1,22 @@
+// |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.plaindate.prototype.add
+description: Passing a primitive other than string to add() throws
+features: [Symbol, Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+assert.throws(TypeError, () => instance.add(undefined), "undefined");
+assert.throws(TypeError, () => instance.add(null), "null");
+assert.throws(TypeError, () => instance.add(true), "boolean");
+assert.throws(RangeError, () => instance.add(""), "empty string");
+assert.throws(TypeError, () => instance.add(Symbol()), "Symbol");
+assert.throws(TypeError, () => instance.add(7), "number");
+assert.throws(TypeError, () => instance.add(7n), "bigint");
+assert.throws(TypeError, () => instance.add([]), "array");
+assert.throws(TypeError, () => instance.add(() => {}), "function");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-singular-properties.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-singular-properties.js
new file mode 100644
index 0000000000..d37504ccc6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-singular-properties.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.plaindate.prototype.add
+description: Singular properties in the property bag are always ignored
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+[
+ { year: 1 },
+ { month: 2 },
+ { week: 3 },
+ { day: 4 },
+ { hour: 5 },
+ { minute: 6 },
+ { second: 7 },
+ { millisecond: 8 },
+ { microsecond: 9 },
+ { nanosecond: 10 },
+].forEach((badObject) => {
+ assert.throws(TypeError, () => instance.add(badObject),
+ "Throw TypeError if temporalDurationLike is not valid");
+});
+
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-string-negative-fractional-units.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-string-negative-fractional-units.js
new file mode 100644
index 0000000000..b0dc6ef7f3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-string-negative-fractional-units.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.plaindate.prototype.add
+description: Strings with fractional duration units are treated with the correct sign
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+const resultHours = instance.add("-PT24.567890123H");
+TemporalHelpers.assertPlainDate(resultHours, 2000, 5, "M05", 1, "negative fractional hours");
+
+const resultMinutes = instance.add("-PT1440.567890123M");
+TemporalHelpers.assertPlainDate(resultMinutes, 2000, 5, "M05", 1, "negative fractional minutes");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-string.js
new file mode 100644
index 0000000000..4155fc39a4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-string.js
@@ -0,0 +1,16 @@
+// |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.plaindate.prototype.add
+description: A string is parsed into the correct object when passed as the argument
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = Temporal.PlainDate.from({ year: 2000, month: 5, day: 2 });
+const result = instance.add("P3D");
+TemporalHelpers.assertPlainDate(result, 2000, 5, "M05", 5);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/balance-smaller-units-basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/balance-smaller-units-basic.js
new file mode 100644
index 0000000000..b5f43668b0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/balance-smaller-units-basic.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.calendar.prototype.dateadd
+description: Durations with units smaller than days are balanced
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(1976, 11, 18);
+
+// lower units that don't balance up to a day
+TemporalHelpers.assertPlainDate(date.add({ hours: 1 }), 1976, 11, "M11", 18);
+TemporalHelpers.assertPlainDate(date.add({ minutes: 1 }), 1976, 11, "M11", 18);
+TemporalHelpers.assertPlainDate(date.add({ seconds: 1 }), 1976, 11, "M11", 18);
+TemporalHelpers.assertPlainDate(date.add({ milliseconds: 1 }), 1976, 11, "M11", 18);
+TemporalHelpers.assertPlainDate(date.add({ microseconds: 1 }), 1976, 11, "M11", 18);
+TemporalHelpers.assertPlainDate(date.add({ nanoseconds: 1 }), 1976, 11, "M11", 18);
+
+// lower units that balance up to a day or more
+TemporalHelpers.assertPlainDate(date.add({ hours: 24 }), 1976, 11, "M11", 19);
+TemporalHelpers.assertPlainDate(date.add({ hours: 36 }), 1976, 11, "M11", 19);
+TemporalHelpers.assertPlainDate(date.add({ hours: 48 }), 1976, 11, "M11", 20);
+TemporalHelpers.assertPlainDate(date.add({ minutes: 1440 }), 1976, 11, "M11", 19);
+TemporalHelpers.assertPlainDate(date.add({ seconds: 86400 }), 1976, 11, "M11", 19);
+TemporalHelpers.assertPlainDate(date.add({ milliseconds: 86400_000 }), 1976, 11, "M11", 19);
+TemporalHelpers.assertPlainDate(date.add({ microseconds: 86400_000_000 }), 1976, 11, "M11", 19);
+TemporalHelpers.assertPlainDate(date.add({ nanoseconds: 86400_000_000_000 }), 1976, 11, "M11", 19);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/balance-smaller-units.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/balance-smaller-units.js
new file mode 100644
index 0000000000..c9d42a3b87
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/balance-smaller-units.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.calendar.prototype.dateadd
+description: Durations with units smaller than days are balanced before adding, in the calendar
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(2000, 5, 2);
+const duration = new Temporal.Duration(0, 0, 0, 1, 24, 1440, 86400, 86400_000, 86400_000_000, 86400_000_000_000);
+
+const result = date.add(duration);
+TemporalHelpers.assertPlainDate(result, 2000, 5, "M05", 9, "units smaller than days are balanced");
+
+const resultString = date.add("P1DT24H1440M86400S");
+TemporalHelpers.assertPlainDate(resultString, 2000, 5, "M05", 6, "units smaller than days are balanced");
+
+const resultPropBag = date.add({ days: 1, hours: 24, minutes: 1440, seconds: 86400, milliseconds: 86400_000, microseconds: 86400_000_000, nanoseconds: 86400_000_000_000 });
+TemporalHelpers.assertPlainDate(resultPropBag, 2000, 5, "M05", 9, "units smaller than days are balanced");
+
+const negativeDuration = new Temporal.Duration(0, 0, 0, -1, -24, -1440, -86400, -86400_000, -86400_000_000, -86400_000_000_000);
+const resultNegative = date.add(negativeDuration);
+TemporalHelpers.assertPlainDate(resultNegative, 2000, 4, "M04", 25, "units smaller than days are balanced");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/basic.js
new file mode 100644
index 0000000000..0d757e7129
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/basic.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.plaindate.prototype.add
+description: Basic tests
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const date = Temporal.PlainDate.from("1976-11-18");
+TemporalHelpers.assertPlainDate(date.add({ years: 43 }),
+ 2019, 11, "M11", 18);
+TemporalHelpers.assertPlainDate(date.add({ months: 3 }),
+ 1977, 2, "M02", 18);
+TemporalHelpers.assertPlainDate(date.add({ days: 20 }),
+ 1976, 12, "M12", 8);
+TemporalHelpers.assertPlainDate(Temporal.PlainDate.from("2019-01-31").add({ months: 1 }),
+ 2019, 2, "M02", 28);
+TemporalHelpers.assertPlainDate(date.add(Temporal.Duration.from('P43Y')),
+ 2019, 11, "M11", 18);
+TemporalHelpers.assertPlainDate(Temporal.PlainDate.from('2019-11-18').add({ years: -43 }),
+ 1976, 11, "M11", 18);
+TemporalHelpers.assertPlainDate(Temporal.PlainDate.from('1977-02-18').add({ months: -3 }),
+ 1976, 11, "M11", 18);
+TemporalHelpers.assertPlainDate(Temporal.PlainDate.from('1976-12-08').add({ days: -20 }),
+ 1976, 11, "M11", 18);
+TemporalHelpers.assertPlainDate(Temporal.PlainDate.from('2019-02-28').add({ months: -1 }),
+ 2019, 1, "M01", 28);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/branding.js
new file mode 100644
index 0000000000..111bc9929c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/branding.js
@@ -0,0 +1,27 @@
+// |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.plaindate.prototype.add
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const add = Temporal.PlainDate.prototype.add;
+
+assert.sameValue(typeof add, "function");
+
+const args = [new Temporal.Duration(5)];
+
+assert.throws(TypeError, () => add.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => add.apply(null, args), "null");
+assert.throws(TypeError, () => add.apply(true, args), "true");
+assert.throws(TypeError, () => add.apply("", args), "empty string");
+assert.throws(TypeError, () => add.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => add.apply(1, args), "1");
+assert.throws(TypeError, () => add.apply({}, args), "plain object");
+assert.throws(TypeError, () => add.apply(Temporal.PlainDate, args), "Temporal.PlainDate");
+assert.throws(TypeError, () => add.apply(Temporal.PlainDate.prototype, args), "Temporal.PlainDate.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..66838eb6e1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plaindate.prototype.add
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dateAddOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dateAdd");
+Object.defineProperty(Temporal.Calendar.prototype, "dateAdd", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("dateAdd should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDate(2000, 5, 2, "iso8601");
+instance.add(new Temporal.Duration(1));
+
+Object.defineProperty(Temporal.Calendar.prototype, "dateAdd", dateAddOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/builtin.js
new file mode 100644
index 0000000000..2ab3bd0c98
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/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.plaindate.prototype.add
+description: >
+ Tests that Temporal.PlainDate.prototype.add
+ 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.PlainDate.prototype.add),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDate.prototype.add),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDate.prototype.add),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDate.prototype.add.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/calendar-invalid-return.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/calendar-invalid-return.js
new file mode 100644
index 0000000000..0cbfb34881
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/calendar-invalid-return.js
@@ -0,0 +1,39 @@
+// |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.plaindate.prototype.add
+description: Throw when the returned value from the calendar's dateAdd method is not a PlainDate.
+features: [Temporal]
+---*/
+
+class CustomCalendar extends Temporal.Calendar {
+ constructor(value) {
+ super("iso8601");
+ this.value = value;
+ }
+ dateAdd() {
+ return this.value;
+ }
+}
+
+const tests = [
+ [undefined],
+ [null, "null"],
+ [true],
+ ["2000-05"],
+ [Symbol()],
+ [200005],
+ [200005n],
+ [{}, "plain object"],
+ [() => {}, "lambda"],
+ [Temporal.PlainDate, "Temporal.PlainDate"],
+ [Temporal.PlainDate.prototype, "Temporal.PlainDate.prototype"],
+];
+for (const [test, description = typeof test] of tests) {
+ const plainDate = new Temporal.PlainDate(2000, 5, 2, new CustomCalendar(test));
+ assert.throws(TypeError, () => plainDate.add({ years: 1 }), `Expected error with ${description}`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/custom.js
new file mode 100644
index 0000000000..f220ad6225
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/custom.js
@@ -0,0 +1,34 @@
+// |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.plaindate.prototype.add
+description: Basic tests with custom calendar
+includes: [compareArray.js,temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const result = new Temporal.PlainDate(1920, 5, 3);
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ dateAdd(...args) {
+ ++calls;
+ assert.sameValue(args.length, 3, "Three arguments");
+ assert.sameValue(args[0], plainDate, "First argument");
+ TemporalHelpers.assertDuration(args[1], 43, 0, 0, 0, 0, 0, 0, 0, 0, 0, "Second argument");
+ assert.sameValue(typeof args[2], "object", "Third argument: type");
+ assert.sameValue(Object.getPrototypeOf(args[2]), null, "Third argument: prototype");
+ assert.compareArray(Object.keys(args[2]), [], "Third argument: keys");
+ return result;
+ }
+}
+const calendar = new CustomCalendar();
+const plainDate = new Temporal.PlainDate(1976, 11, 18, calendar);
+assert.sameValue(plainDate.add({ years: 43 }), result);
+assert.sameValue(calls, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..c68829b5a2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/infinity-throws-rangeerror.js
@@ -0,0 +1,38 @@
+// |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.
+
+/*---
+description: Temporal.PlainDate.prototype.add throws a RangeError if any value in a property bag is Infinity
+esid: sec-temporal.plaindate.prototype.add
+features: [Temporal]
+---*/
+
+const overflows = ["constrain", "reject"];
+const fields = ["years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"];
+
+const instance = Temporal.PlainDate.from({ year: 2000, month: 5, day: 2 });
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.add({ [field]: Infinity }, { overflow }));
+ });
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return Infinity;
+ }
+};
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => instance.add({ [field]: obj }, { overflow }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/length.js
new file mode 100644
index 0000000000..d5031ff92e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/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.plaindate.prototype.add
+description: Temporal.PlainDate.prototype.add.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.PlainDate.prototype.add, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/limits.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/limits.js
new file mode 100644
index 0000000000..dedd779131
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/limits.js
@@ -0,0 +1,18 @@
+// |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.
+
+/*---
+description: Temporal.PlainDate.prototype.add throws a RangeError if the calculation crosses a limit
+esid: sec-temporal.plaindate.prototype.add
+features: [Temporal]
+---*/
+
+const min = Temporal.PlainDate.from("-271821-04-19");
+const max = Temporal.PlainDate.from("+275760-09-13");
+["reject", "constrain"].forEach((overflow) => {
+ assert.throws(RangeError, () => min.add({ days: -1 }, { overflow }), `min with ${overflow}`);
+ assert.throws(RangeError, () => max.add({ days: 1 }, { overflow }), `max with ${overflow}`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/name.js
new file mode 100644
index 0000000000..21e3495429
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/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.plaindate.prototype.add
+description: Temporal.PlainDate.prototype.add.name is "add".
+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.PlainDate.prototype.add, "name", {
+ value: "add",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/negative-infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/negative-infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..44014165ee
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/negative-infinity-throws-rangeerror.js
@@ -0,0 +1,38 @@
+// |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.
+
+/*---
+description: Temporal.PlainDate.prototype.add throws a RangeError if any value in a property bag is -Infinity
+esid: sec-temporal.plaindate.prototype.add
+features: [Temporal]
+---*/
+
+const overflows = ["constrain", "reject"];
+const fields = ["years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"];
+
+const instance = Temporal.PlainDate.from({ year: 2000, month: 5, day: 2 });
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.add({ [field]: -Infinity }, { overflow }));
+ });
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return -Infinity;
+ }
+};
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => instance.add({ [field]: obj }, { overflow }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/non-integer-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/non-integer-throws-rangeerror.js
new file mode 100644
index 0000000000..1dd6d9b79b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/non-integer-throws-rangeerror.js
@@ -0,0 +1,29 @@
+// |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.plaindate.prototype.add
+description: A non-integer value for any recognized property in the property bag, throws a RangeError
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+const fields = [
+ "years",
+ "months",
+ "weeks",
+ "days",
+ "hours",
+ "minutes",
+ "seconds",
+ "milliseconds",
+ "microseconds",
+ "nanoseconds",
+];
+fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.add({ [field]: 1.5 }));
+ assert.throws(RangeError, () => instance.add({ [field]: -1.5 }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/not-a-constructor.js
new file mode 100644
index 0000000000..4234996527
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/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.plaindate.prototype.add
+description: >
+ Temporal.PlainDate.prototype.add 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.PlainDate.prototype.add();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDate.prototype.add), false,
+ "isConstructor(Temporal.PlainDate.prototype.add)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/options-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/options-object.js
new file mode 100644
index 0000000000..a827bcd6e8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/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.plaindate.prototype.add
+description: Empty or a function object may be used as options
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+const result1 = instance.add({ months: 1 }, {});
+TemporalHelpers.assertPlainDate(
+ result1, 2000, 6, "M06", 2,
+ "options may be an empty plain object"
+);
+
+const result2 = instance.add({ months: 1 }, () => {});
+TemporalHelpers.assertPlainDate(
+ result2, 2000, 6, "M06", 2,
+ "options may be a function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/options-undefined.js
new file mode 100644
index 0000000000..914f728206
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/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.plaindate.prototype.add
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(2000, 1, 31);
+const duration = { months: 1 };
+
+const explicit = date.add(duration, undefined);
+assert.sameValue(explicit.month, 2, "default overflow is constrain");
+assert.sameValue(explicit.day, 29, "default overflow is constrain");
+
+const implicit = date.add(duration);
+assert.sameValue(implicit.month, 2, "default overflow is constrain");
+assert.sameValue(implicit.day, 29, "default overflow is constrain");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/options-wrong-type.js
new file mode 100644
index 0000000000..a068e69773
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/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.plaindate.prototype.add
+description: TypeError thrown when options argument is a primitive
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const badOptions = [
+ null,
+ true,
+ "some string",
+ Symbol(),
+ 1,
+ 2n,
+];
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.add({ months: 1 }, value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/order-of-operations.js
new file mode 100644
index 0000000000..d19a06209b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/order-of-operations.js
@@ -0,0 +1,129 @@
+// |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.plaindate.prototype.add
+description: Properties on an object passed to add() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ // ToTemporalDurationRecord
+ "get fields.days",
+ "get fields.days.valueOf",
+ "call fields.days.valueOf",
+ "get fields.hours",
+ "get fields.hours.valueOf",
+ "call fields.hours.valueOf",
+ "get fields.microseconds",
+ "get fields.microseconds.valueOf",
+ "call fields.microseconds.valueOf",
+ "get fields.milliseconds",
+ "get fields.milliseconds.valueOf",
+ "call fields.milliseconds.valueOf",
+ "get fields.minutes",
+ "get fields.minutes.valueOf",
+ "call fields.minutes.valueOf",
+ "get fields.months",
+ "get fields.months.valueOf",
+ "call fields.months.valueOf",
+ "get fields.nanoseconds",
+ "get fields.nanoseconds.valueOf",
+ "call fields.nanoseconds.valueOf",
+ "get fields.seconds",
+ "get fields.seconds.valueOf",
+ "call fields.seconds.valueOf",
+ "get fields.weeks",
+ "get fields.weeks.valueOf",
+ "call fields.weeks.valueOf",
+ "get fields.years",
+ "get fields.years.valueOf",
+ "call fields.years.valueOf",
+ // AddDate
+ "get this.calendar.dateAdd",
+ "call this.calendar.dateAdd",
+ // inside Calendar.p.dateAdd
+ "get options.overflow",
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+];
+const actual = [];
+
+const calendar = TemporalHelpers.calendarObserver(actual, "this.calendar");
+const instance = new Temporal.PlainDate(2000, 5, 2, calendar);
+// clear observable operations that occurred during the constructor call
+actual.splice(0);
+
+const fields = TemporalHelpers.propertyBagObserver(actual, {
+ years: 1,
+ months: 1,
+ weeks: 1,
+ days: 1,
+ hours: 1,
+ minutes: 1,
+ seconds: 1,
+ milliseconds: 1,
+ microseconds: 1,
+ nanoseconds: 1,
+}, "fields");
+
+const options = TemporalHelpers.propertyBagObserver(actual, {
+ overflow: "constrain",
+}, "options");
+
+instance.add(fields, options);
+assert.compareArray(actual, expected, "order of operations");
+
+actual.splice(0); // clear
+
+const noCalendarExpected = [
+ // ToTemporalDurationRecord
+ "get fields.days",
+ "get fields.days.valueOf",
+ "call fields.days.valueOf",
+ "get fields.hours",
+ "get fields.hours.valueOf",
+ "call fields.hours.valueOf",
+ "get fields.microseconds",
+ "get fields.microseconds.valueOf",
+ "call fields.microseconds.valueOf",
+ "get fields.milliseconds",
+ "get fields.milliseconds.valueOf",
+ "call fields.milliseconds.valueOf",
+ "get fields.minutes",
+ "get fields.minutes.valueOf",
+ "call fields.minutes.valueOf",
+ "get fields.months",
+ "get fields.nanoseconds",
+ "get fields.nanoseconds.valueOf",
+ "call fields.nanoseconds.valueOf",
+ "get fields.seconds",
+ "get fields.seconds.valueOf",
+ "call fields.seconds.valueOf",
+ "get fields.weeks",
+ "get fields.years",
+ "get this.calendar.dateAdd",
+ // AddDate
+ "get options.overflow",
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+];
+
+const noCalendarFields = TemporalHelpers.propertyBagObserver(actual, {
+ days: 1,
+ hours: 1,
+ minutes: 1,
+ seconds: 1,
+ milliseconds: 1,
+ microseconds: 1,
+ nanoseconds: 1,
+}, "fields");
+
+instance.add(noCalendarFields, options);
+assert.compareArray(actual, noCalendarExpected, "order of operations with no calendar operation");
+
+actual.splice(0); // clear
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/overflow-constrain.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/overflow-constrain.js
new file mode 100644
index 0000000000..6bf13939f1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/overflow-constrain.js
@@ -0,0 +1,16 @@
+// |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.plaindate.prototype.add
+description: Constrains with overflow constrain
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const jan31 = Temporal.PlainDate.from("2020-01-31");
+TemporalHelpers.assertPlainDate(jan31.add({ months: 1 }, { overflow: "constrain" }),
+ 2020, 2, "M02", 29);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/overflow-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/overflow-invalid-string.js
new file mode 100644
index 0000000000..0853583603
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/overflow-invalid-string.js
@@ -0,0 +1,32 @@
+// |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.plaindate.prototype.add
+description: RangeError thrown when overflow option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal.calendar.prototype.dateadd step 7:
+ 7. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plaindate.prototype.add step 7:
+ 7. Return ? CalendarDateAdd(_temporalDate_.[[Calendar]], _temporalDate_, _balancedDuration_, _options_).
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(2000, 5, 2);
+const duration = new Temporal.Duration(3, 3, 0, 3);
+
+const badOverflows = ["", "CONSTRAIN", "balance", "other string", "constra\u0131n", "reject\0"];
+for (const overflow of badOverflows) {
+ assert.throws(
+ RangeError,
+ () => date.add(duration, { overflow }),
+ `invalid overflow ("${overflow}")`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/overflow-reject.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/overflow-reject.js
new file mode 100644
index 0000000000..aa9672d377
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/overflow-reject.js
@@ -0,0 +1,14 @@
+// |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.plaindate.prototype.add
+description: Throws with overflow reject
+features: [Temporal]
+---*/
+
+const jan31 = Temporal.PlainDate.from("2020-01-31");
+assert.throws(RangeError, () => jan31.add({ months: 1 }, { overflow: "reject" }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/overflow-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/overflow-undefined.js
new file mode 100644
index 0000000000..6f923341f3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/overflow-undefined.js
@@ -0,0 +1,31 @@
+// |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.plaindate.prototype.add
+description: Fallback value for overflow option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal.calendar.prototype.dateadd step 7:
+ 7. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plaindate.prototype.add step 7:
+ 7. Return ? CalendarDateAdd(_temporalDate_.[[Calendar]], _temporalDate_, _balancedDuration_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(2000, 5, 31);
+const duration = new Temporal.Duration(3, 1);
+
+const explicit = date.add(duration, { overflow: undefined });
+TemporalHelpers.assertPlainDate(explicit, 2003, 6, "M06", 30, "default overflow is constrain");
+const implicit = date.add(duration, {});
+TemporalHelpers.assertPlainDate(implicit, 2003, 6, "M06", 30, "default overflow is constrain");
+const lambda = date.add(duration, () => {});
+TemporalHelpers.assertPlainDate(lambda, 2003, 6, "M06", 30, "default overflow is constrain");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/overflow-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/overflow-wrong-type.js
new file mode 100644
index 0000000000..98927d249e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/overflow-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.plaindate.prototype.add
+description: Type conversions for overflow option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal.calendar.prototype.dateadd step 7:
+ 7. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plaindate.prototype.add step 7:
+ 7. Return ? CalendarDateAdd(_temporalDate_.[[Calendar]], _temporalDate_, _balancedDuration_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(2000, 5, 2);
+const duration = new Temporal.Duration(3, 3, 0, 3);
+TemporalHelpers.checkStringOptionWrongType("overflow", "constrain",
+ (overflow) => date.add(duration, { overflow }),
+ (result, descr) => TemporalHelpers.assertPlainDate(result, 2003, 8, "M08", 5, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/prop-desc.js
new file mode 100644
index 0000000000..8bb7789f01
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/prop-desc.js
@@ -0,0 +1,24 @@
+// |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.plaindate.prototype.add
+description: The "add" property of Temporal.PlainDate.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDate.prototype.add,
+ "function",
+ "`typeof PlainDate.prototype.add` is `function`"
+);
+
+verifyProperty(Temporal.PlainDate.prototype, "add", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/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/PlainDate/prototype/add/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/subclassing-ignored.js
new file mode 100644
index 0000000000..abbe09f5df
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/subclassing-ignored.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.plaindate.prototype.add
+description: Objects of a subclass are never created as return values for add()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.PlainDate,
+ [2000, 5, 2],
+ "add",
+ [{ days: 1 }],
+ (result) => TemporalHelpers.assertPlainDate(result, 2000, 5, "M05", 3),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/calendarId/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/calendarId/branding.js
new file mode 100644
index 0000000000..87e61c2db5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/calendarId/branding.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-get-temporal.plaindate.prototype.calendarid
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const calendarId = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "calendarId").get;
+
+assert.sameValue(typeof calendarId, "function");
+
+assert.throws(TypeError, () => calendarId.call(undefined), "undefined");
+assert.throws(TypeError, () => calendarId.call(null), "null");
+assert.throws(TypeError, () => calendarId.call(true), "true");
+assert.throws(TypeError, () => calendarId.call(""), "empty string");
+assert.throws(TypeError, () => calendarId.call(Symbol()), "symbol");
+assert.throws(TypeError, () => calendarId.call(1), "1");
+assert.throws(TypeError, () => calendarId.call({}), "plain object");
+assert.throws(TypeError, () => calendarId.call(Temporal.PlainDate), "Temporal.PlainDate");
+assert.throws(TypeError, () => calendarId.call(Temporal.PlainDate.prototype), "Temporal.PlainDate.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/calendarId/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/calendarId/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/calendarId/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/calendarId/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/calendarId/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..c4c44332c6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/calendarId/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plaindate.prototype.calendarid
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const idOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "id");
+Object.defineProperty(Temporal.Calendar.prototype, "id", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("id should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDate(2000, 5, 2, "iso8601");
+instance.calendarId;
+
+Object.defineProperty(Temporal.Calendar.prototype, "id", idOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/calendarId/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/calendarId/prop-desc.js
new file mode 100644
index 0000000000..bea8a35cce
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/calendarId/prop-desc.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-get-temporal.plaindate.prototype.calendarid
+description: The "calendarId" property of Temporal.PlainDate.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "calendarId");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/calendarId/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/calendarId/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/calendarId/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/constructor.js
new file mode 100644
index 0000000000..3363c592ed
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/constructor.js
@@ -0,0 +1,20 @@
+// |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.plaindate.prototype.constructor
+description: Test for Temporal.PlainDate.prototype.constructor.
+info: The initial value of Temporal.PlainDate.prototype.constructor is %Temporal.PlainDate%.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype, "constructor", {
+ value: Temporal.PlainDate,
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/day/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/day/branding.js
new file mode 100644
index 0000000000..307ed9319c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/day/branding.js
@@ -0,0 +1,25 @@
+// |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-get-temporal.plaindate.prototype.day
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const day = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "day").get;
+
+assert.sameValue(typeof day, "function");
+
+assert.throws(TypeError, () => day.call(undefined), "undefined");
+assert.throws(TypeError, () => day.call(null), "null");
+assert.throws(TypeError, () => day.call(true), "true");
+assert.throws(TypeError, () => day.call(""), "empty string");
+assert.throws(TypeError, () => day.call(Symbol()), "symbol");
+assert.throws(TypeError, () => day.call(1), "1");
+assert.throws(TypeError, () => day.call({}), "plain object");
+assert.throws(TypeError, () => day.call(Temporal.PlainDate), "Temporal.PlainDate");
+assert.throws(TypeError, () => day.call(Temporal.PlainDate.prototype), "Temporal.PlainDate.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/day/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/day/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/day/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/day/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/day/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..f80dbba88e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/day/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plaindate.prototype.day
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dayOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "day");
+Object.defineProperty(Temporal.Calendar.prototype, "day", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("day should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDate(2000, 5, 2, "iso8601");
+instance.day;
+
+Object.defineProperty(Temporal.Calendar.prototype, "day", dayOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/day/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/day/custom.js
new file mode 100644
index 0000000000..c74d2b56c4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/day/custom.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-get-temporal.plaindate.prototype.day
+description: Custom calendar tests for day().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ day(...args) {
+ ++calls;
+ assert.compareArray(args, [pd], "day arguments");
+ return 7;
+ }
+}
+
+const calendar = new CustomCalendar();
+const pd = new Temporal.PlainDate(1830, 8, 25, calendar);
+const result = pd.day;
+assert.sameValue(result, 7, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/day/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/day/prop-desc.js
new file mode 100644
index 0000000000..a51472e4cd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/day/prop-desc.js
@@ -0,0 +1,17 @@
+// |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-get-temporal.plaindate.prototype.day
+description: The "day" property of Temporal.PlainDate.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "day");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/day/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/day/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/day/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/day/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/day/validate-calendar-value.js
new file mode 100644
index 0000000000..3cecbfd3a5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/day/validate-calendar-value.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-get-temporal.plaindate.prototype.day
+description: Validate result returned from calendar day() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [null, TypeError],
+ [false, TypeError],
+ [Infinity, RangeError],
+ [-Infinity, RangeError],
+ [NaN, RangeError],
+ [-7, RangeError],
+ [-0.1, RangeError],
+ ["string", TypeError],
+ [Symbol("foo"), TypeError],
+ [7n, TypeError],
+ [{}, TypeError],
+ [true, TypeError],
+ [7.1, RangeError],
+ ["7", TypeError],
+ ["7.5", TypeError],
+ [{valueOf() { return 7; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ day() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainDate(1981, 12, 15, calendar);
+ assert.throws(error, () => instance.day, `${typeof result} ${String(result)} not converted to positive integer`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfWeek/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfWeek/basic.js
new file mode 100644
index 0000000000..e87712e78a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfWeek/basic.js
@@ -0,0 +1,16 @@
+// |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-get-temporal.plaindate.prototype.dayofweek
+description: Basic tests for dayOfWeek().
+features: [Temporal]
+---*/
+
+for (let i = 1; i <= 7; ++i) {
+ const plainDate = new Temporal.PlainDate(1976, 11, 14 + i);
+ assert.sameValue(plainDate.dayOfWeek, i, `${plainDate} should be on day ${i}`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfWeek/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfWeek/branding.js
new file mode 100644
index 0000000000..24f4f288ad
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfWeek/branding.js
@@ -0,0 +1,25 @@
+// |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-get-temporal.plaindate.prototype.dayofweek
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const dayOfWeek = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "dayOfWeek").get;
+
+assert.sameValue(typeof dayOfWeek, "function");
+
+assert.throws(TypeError, () => dayOfWeek.call(undefined), "undefined");
+assert.throws(TypeError, () => dayOfWeek.call(null), "null");
+assert.throws(TypeError, () => dayOfWeek.call(true), "true");
+assert.throws(TypeError, () => dayOfWeek.call(""), "empty string");
+assert.throws(TypeError, () => dayOfWeek.call(Symbol()), "symbol");
+assert.throws(TypeError, () => dayOfWeek.call(1), "1");
+assert.throws(TypeError, () => dayOfWeek.call({}), "plain object");
+assert.throws(TypeError, () => dayOfWeek.call(Temporal.PlainDate), "Temporal.PlainDate");
+assert.throws(TypeError, () => dayOfWeek.call(Temporal.PlainDate.prototype), "Temporal.PlainDate.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfWeek/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfWeek/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfWeek/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfWeek/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfWeek/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..06c14532d2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfWeek/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plaindate.prototype.dayofweek
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dayOfWeekOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dayOfWeek");
+Object.defineProperty(Temporal.Calendar.prototype, "dayOfWeek", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("dayOfWeek should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDate(2000, 5, 2, "iso8601");
+instance.dayOfWeek;
+
+Object.defineProperty(Temporal.Calendar.prototype, "dayOfWeek", dayOfWeekOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfWeek/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfWeek/custom.js
new file mode 100644
index 0000000000..1f3ad60d72
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfWeek/custom.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-get-temporal.plaindate.prototype.dayofweek
+description: Custom calendar tests for dayOfWeek().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ dayOfWeek(...args) {
+ ++calls;
+ assert.compareArray(args, [pd], "dayOfWeek arguments");
+ return 7;
+ }
+}
+
+const calendar = new CustomCalendar();
+const pd = new Temporal.PlainDate(1830, 8, 25, calendar);
+const result = pd.dayOfWeek;
+assert.sameValue(result, 7, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfWeek/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfWeek/prop-desc.js
new file mode 100644
index 0000000000..51629ce17f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfWeek/prop-desc.js
@@ -0,0 +1,17 @@
+// |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-get-temporal.plaindate.prototype.dayofweek
+description: The "dayOfWeek" property of Temporal.PlainDate.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "dayOfWeek");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfWeek/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfWeek/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfWeek/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfWeek/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfWeek/validate-calendar-value.js
new file mode 100644
index 0000000000..b3a5583270
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfWeek/validate-calendar-value.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-get-temporal.plaindate.prototype.dayofweek
+description: Validate result returned from calendar dayOfWeek() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [null, TypeError],
+ [false, TypeError],
+ [Infinity, RangeError],
+ [-Infinity, RangeError],
+ [NaN, RangeError],
+ [-7, RangeError],
+ [-0.1, RangeError],
+ ["string", TypeError],
+ [Symbol("foo"), TypeError],
+ [7n, TypeError],
+ [{}, TypeError],
+ [true, TypeError],
+ [7.1, RangeError],
+ ["7", TypeError],
+ ["7.5", TypeError],
+ [{valueOf() { return 7; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ dayOfWeek() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainDate(1981, 12, 15, calendar);
+ assert.throws(error, () => instance.dayOfWeek, `${typeof result} ${String(result)} not converted to positive integer`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/basic.js
new file mode 100644
index 0000000000..a0b4fc9967
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/basic.js
@@ -0,0 +1,16 @@
+// |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-get-temporal.plaindate.prototype.dayofyear
+description: Basic tests for dayOfYear().
+features: [Temporal]
+---*/
+
+for (let i = 1; i <= 7; ++i) {
+ const plainDate = new Temporal.PlainDate(1976, 11, 14 + i);
+ assert.sameValue(plainDate.dayOfYear, 319 + i, `${plainDate} should be on day ${319 + i}`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/branding.js
new file mode 100644
index 0000000000..b9de274662
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/branding.js
@@ -0,0 +1,25 @@
+// |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-get-temporal.plaindate.prototype.dayofyear
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const dayOfYear = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "dayOfYear").get;
+
+assert.sameValue(typeof dayOfYear, "function");
+
+assert.throws(TypeError, () => dayOfYear.call(undefined), "undefined");
+assert.throws(TypeError, () => dayOfYear.call(null), "null");
+assert.throws(TypeError, () => dayOfYear.call(true), "true");
+assert.throws(TypeError, () => dayOfYear.call(""), "empty string");
+assert.throws(TypeError, () => dayOfYear.call(Symbol()), "symbol");
+assert.throws(TypeError, () => dayOfYear.call(1), "1");
+assert.throws(TypeError, () => dayOfYear.call({}), "plain object");
+assert.throws(TypeError, () => dayOfYear.call(Temporal.PlainDate), "Temporal.PlainDate");
+assert.throws(TypeError, () => dayOfYear.call(Temporal.PlainDate.prototype), "Temporal.PlainDate.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..b07a147c44
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plaindate.prototype.dayofyear
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dayOfYearOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dayOfYear");
+Object.defineProperty(Temporal.Calendar.prototype, "dayOfYear", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("dayOfYear should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDate(2000, 5, 2, "iso8601");
+instance.dayOfYear;
+
+Object.defineProperty(Temporal.Calendar.prototype, "dayOfYear", dayOfYearOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/custom.js
new file mode 100644
index 0000000000..3dc1efba96
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/custom.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-get-temporal.plaindate.prototype.dayofyear
+description: Custom calendar tests for dayOfYear().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ dayOfYear(...args) {
+ ++calls;
+ assert.compareArray(args, [pd], "dayOfYear arguments");
+ return 7;
+ }
+}
+
+const calendar = new CustomCalendar();
+const pd = new Temporal.PlainDate(1830, 8, 25, calendar);
+const result = pd.dayOfYear;
+assert.sameValue(result, 7, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/prop-desc.js
new file mode 100644
index 0000000000..789531add6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/prop-desc.js
@@ -0,0 +1,17 @@
+// |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-get-temporal.plaindate.prototype.dayofyear
+description: The "dayOfYear" property of Temporal.PlainDate.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "dayOfYear");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/validate-calendar-value.js
new file mode 100644
index 0000000000..025b76dcdd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/validate-calendar-value.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-get-temporal.plaindate.prototype.dayofyear
+description: Validate result returned from calendar dayOfYear() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [null, TypeError],
+ [false, TypeError],
+ [Infinity, RangeError],
+ [-Infinity, RangeError],
+ [NaN, RangeError],
+ [-7, RangeError],
+ [-0.1, RangeError],
+ ["string", TypeError],
+ [Symbol("foo"), TypeError],
+ [7n, TypeError],
+ [{}, TypeError],
+ [true, TypeError],
+ [7.1, RangeError],
+ ["7", TypeError],
+ ["7.5", TypeError],
+ [{valueOf() { return 7; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ dayOfYear() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainDate(1981, 12, 15, calendar);
+ assert.throws(error, () => instance.dayOfYear, `${typeof result} ${String(result)} not converted to positive integer`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/basic.js
new file mode 100644
index 0000000000..022b6d218a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/basic.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-get-temporal.plaindate.prototype.daysinmonth
+description: Checking days in month for a "normal" case (non-undefined, non-boundary case, etc.)
+features: [Temporal]
+---*/
+
+const tests = [
+ [new Temporal.PlainDate(1976, 2, 18), 29],
+ [new Temporal.PlainDate(1976, 11, 18), 30],
+ [new Temporal.PlainDate(1976, 12, 18), 31],
+ [new Temporal.PlainDate(1977, 2, 18), 28],
+];
+for (const [plainDate, expected] of tests) {
+ assert.sameValue(plainDate.daysInMonth, expected, `${expected} days in the month of ${plainDate}`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/branding.js
new file mode 100644
index 0000000000..a3650a8d64
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/branding.js
@@ -0,0 +1,25 @@
+// |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-get-temporal.plaindate.prototype.daysinmonth
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const daysInMonth = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "daysInMonth").get;
+
+assert.sameValue(typeof daysInMonth, "function");
+
+assert.throws(TypeError, () => daysInMonth.call(undefined), "undefined");
+assert.throws(TypeError, () => daysInMonth.call(null), "null");
+assert.throws(TypeError, () => daysInMonth.call(true), "true");
+assert.throws(TypeError, () => daysInMonth.call(""), "empty string");
+assert.throws(TypeError, () => daysInMonth.call(Symbol()), "symbol");
+assert.throws(TypeError, () => daysInMonth.call(1), "1");
+assert.throws(TypeError, () => daysInMonth.call({}), "plain object");
+assert.throws(TypeError, () => daysInMonth.call(Temporal.PlainDate), "Temporal.PlainDate");
+assert.throws(TypeError, () => daysInMonth.call(Temporal.PlainDate.prototype), "Temporal.PlainDate.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..f9b7544a2c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plaindate.prototype.daysinmonth
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const daysInMonthOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "daysInMonth");
+Object.defineProperty(Temporal.Calendar.prototype, "daysInMonth", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("daysInMonth should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDate(2000, 5, 2, "iso8601");
+instance.daysInMonth;
+
+Object.defineProperty(Temporal.Calendar.prototype, "daysInMonth", daysInMonthOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/custom.js
new file mode 100644
index 0000000000..b53aee4e36
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/custom.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-get-temporal.plaindate.prototype.daysinmonth
+description: Custom calendar tests for daysInMonth().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ daysInMonth(...args) {
+ ++calls;
+ assert.compareArray(args, [pd], "daysInMonth arguments");
+ return 7;
+ }
+}
+
+const calendar = new CustomCalendar();
+const pd = new Temporal.PlainDate(1830, 8, 25, calendar);
+const result = pd.daysInMonth;
+assert.sameValue(result, 7, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/prop-desc.js
new file mode 100644
index 0000000000..00f8dc0aa0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/prop-desc.js
@@ -0,0 +1,17 @@
+// |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-get-temporal.plaindate.prototype.daysinmonth
+description: The "daysInMonth" property of Temporal.PlainDate.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "daysInMonth");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/validate-calendar-value.js
new file mode 100644
index 0000000000..2c903a74ce
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/validate-calendar-value.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-get-temporal.plaindate.prototype.daysinmonth
+description: Validate result returned from calendar daysInMonth() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [null, TypeError],
+ [false, TypeError],
+ [Infinity, RangeError],
+ [-Infinity, RangeError],
+ [NaN, RangeError],
+ [-7, RangeError],
+ [-0.1, RangeError],
+ ["string", TypeError],
+ [Symbol("foo"), TypeError],
+ [7n, TypeError],
+ [{}, TypeError],
+ [true, TypeError],
+ [7.1, RangeError],
+ ["7", TypeError],
+ ["7.5", TypeError],
+ [{valueOf() { return 7; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ daysInMonth() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainDate(1981, 12, 15, calendar);
+ assert.throws(error, () => instance.daysInMonth, `${typeof result} ${String(result)} not converted to positive integer`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInWeek/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInWeek/basic.js
new file mode 100644
index 0000000000..8e470e1e42
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInWeek/basic.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-get-temporal.plaindate.prototype.daysinweek
+description: Basic tests for daysInWeek().
+features: [Temporal]
+---*/
+
+const tests = [
+ new Temporal.PlainDate(1976, 1, 1),
+ new Temporal.PlainDate(1976, 11, 18),
+ new Temporal.PlainDate(1976, 12, 31),
+];
+for (const plainDate of tests) {
+ assert.sameValue(plainDate.daysInWeek, 7, `Seven days in the week of ${plainDate}`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInWeek/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInWeek/branding.js
new file mode 100644
index 0000000000..6e010896dd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInWeek/branding.js
@@ -0,0 +1,25 @@
+// |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-get-temporal.plaindate.prototype.daysinweek
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const daysInWeek = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "daysInWeek").get;
+
+assert.sameValue(typeof daysInWeek, "function");
+
+assert.throws(TypeError, () => daysInWeek.call(undefined), "undefined");
+assert.throws(TypeError, () => daysInWeek.call(null), "null");
+assert.throws(TypeError, () => daysInWeek.call(true), "true");
+assert.throws(TypeError, () => daysInWeek.call(""), "empty string");
+assert.throws(TypeError, () => daysInWeek.call(Symbol()), "symbol");
+assert.throws(TypeError, () => daysInWeek.call(1), "1");
+assert.throws(TypeError, () => daysInWeek.call({}), "plain object");
+assert.throws(TypeError, () => daysInWeek.call(Temporal.PlainDate), "Temporal.PlainDate");
+assert.throws(TypeError, () => daysInWeek.call(Temporal.PlainDate.prototype), "Temporal.PlainDate.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInWeek/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInWeek/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInWeek/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInWeek/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInWeek/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..7eb50b35d9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInWeek/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plaindate.prototype.daysinweek
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const daysInWeekOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "daysInWeek");
+Object.defineProperty(Temporal.Calendar.prototype, "daysInWeek", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("daysInWeek should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDate(2000, 5, 2, "iso8601");
+instance.daysInWeek;
+
+Object.defineProperty(Temporal.Calendar.prototype, "daysInWeek", daysInWeekOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInWeek/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInWeek/custom.js
new file mode 100644
index 0000000000..3942f6a3af
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInWeek/custom.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-get-temporal.plaindate.prototype.daysinweek
+description: Custom calendar tests for daysInWeek().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ daysInWeek(...args) {
+ ++calls;
+ assert.compareArray(args, [pd], "daysInWeek arguments");
+ return 7;
+ }
+}
+
+const calendar = new CustomCalendar();
+const pd = new Temporal.PlainDate(1830, 8, 25, calendar);
+const result = pd.daysInWeek;
+assert.sameValue(result, 7, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInWeek/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInWeek/prop-desc.js
new file mode 100644
index 0000000000..8d04f5afe1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInWeek/prop-desc.js
@@ -0,0 +1,17 @@
+// |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-get-temporal.plaindate.prototype.daysinweek
+description: The "daysInWeek" property of Temporal.PlainDate.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "daysInWeek");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInWeek/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInWeek/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInWeek/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInWeek/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInWeek/validate-calendar-value.js
new file mode 100644
index 0000000000..e9eefdba7d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInWeek/validate-calendar-value.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-get-temporal.plaindate.prototype.daysinweek
+description: Validate result returned from calendar daysInWeek() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [null, TypeError],
+ [false, TypeError],
+ [Infinity, RangeError],
+ [-Infinity, RangeError],
+ [NaN, RangeError],
+ [-7, RangeError],
+ [-0.1, RangeError],
+ ["string", TypeError],
+ [Symbol("foo"), TypeError],
+ [7n, TypeError],
+ [{}, TypeError],
+ [true, TypeError],
+ [7.1, RangeError],
+ ["7", TypeError],
+ ["7.5", TypeError],
+ [{valueOf() { return 7; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ daysInWeek() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainDate(1981, 12, 15, calendar);
+ assert.throws(error, () => instance.daysInWeek, `${typeof result} ${String(result)} not converted to positive integer`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/basic.js
new file mode 100644
index 0000000000..719a2fd527
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/basic.js
@@ -0,0 +1,14 @@
+// |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-get-temporal.plaindate.prototype.daysinyear
+description: Basic tests for daysInYear.
+features: [Temporal]
+---*/
+
+assert.sameValue((new Temporal.PlainDate(1976, 11, 18)).daysInYear, 366, "leap year");
+assert.sameValue((new Temporal.PlainDate(1977, 11, 18)).daysInYear, 365, "non-leap year");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/branding.js
new file mode 100644
index 0000000000..c9caa7a2d8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/branding.js
@@ -0,0 +1,25 @@
+// |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-get-temporal.plaindate.prototype.daysinyear
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const daysInYear = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "daysInYear").get;
+
+assert.sameValue(typeof daysInYear, "function");
+
+assert.throws(TypeError, () => daysInYear.call(undefined), "undefined");
+assert.throws(TypeError, () => daysInYear.call(null), "null");
+assert.throws(TypeError, () => daysInYear.call(true), "true");
+assert.throws(TypeError, () => daysInYear.call(""), "empty string");
+assert.throws(TypeError, () => daysInYear.call(Symbol()), "symbol");
+assert.throws(TypeError, () => daysInYear.call(1), "1");
+assert.throws(TypeError, () => daysInYear.call({}), "plain object");
+assert.throws(TypeError, () => daysInYear.call(Temporal.PlainDate), "Temporal.PlainDate");
+assert.throws(TypeError, () => daysInYear.call(Temporal.PlainDate.prototype), "Temporal.PlainDate.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..e38db3ec88
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plaindate.prototype.daysinyear
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const daysInYearOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "daysInYear");
+Object.defineProperty(Temporal.Calendar.prototype, "daysInYear", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("daysInYear should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDate(2000, 5, 2, "iso8601");
+instance.daysInYear;
+
+Object.defineProperty(Temporal.Calendar.prototype, "daysInYear", daysInYearOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/custom.js
new file mode 100644
index 0000000000..e9311cf1db
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/custom.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-get-temporal.plaindate.prototype.daysinyear
+description: Custom calendar tests for daysInYear().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ daysInYear(...args) {
+ ++calls;
+ assert.compareArray(args, [pd], "daysInYear arguments");
+ return 7;
+ }
+}
+
+const calendar = new CustomCalendar();
+const pd = new Temporal.PlainDate(1830, 8, 25, calendar);
+const result = pd.daysInYear;
+assert.sameValue(result, 7, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/prop-desc.js
new file mode 100644
index 0000000000..1e23a84375
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/prop-desc.js
@@ -0,0 +1,17 @@
+// |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-get-temporal.plaindate.prototype.daysinyear
+description: The "daysInYear" property of Temporal.PlainDate.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "daysInYear");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/validate-calendar-value.js
new file mode 100644
index 0000000000..3676842033
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/validate-calendar-value.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-get-temporal.plaindate.prototype.daysinyear
+description: Validate result returned from calendar daysInYear() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [null, TypeError],
+ [false, TypeError],
+ [Infinity, RangeError],
+ [-Infinity, RangeError],
+ [NaN, RangeError],
+ [-7, RangeError],
+ [-0.1, RangeError],
+ ["string", TypeError],
+ [Symbol("foo"), TypeError],
+ [7n, TypeError],
+ [{}, TypeError],
+ [true, TypeError],
+ [7.1, RangeError],
+ ["7", TypeError],
+ ["7.5", TypeError],
+ [{valueOf() { return 7; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ daysInYear() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainDate(1981, 12, 15, calendar);
+ assert.throws(error, () => instance.daysInYear, `${typeof result} ${String(result)} not converted to positive integer`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..4ceb1ce39b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-builtin-calendar-no-array-iteration.js
@@ -0,0 +1,24 @@
+// |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.plaindate.prototype.equals
+description: >
+ Calling the method with a property bag argument with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+const arg = { year: 2000, month: 5, day: 2, calendar: "iso8601" };
+instance.equals(arg);
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-calendar-datefromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..da799c2c7b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-calendar-datefromfields-called-with-null-prototype-fields.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.plaindate.prototype.equals
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.PlainDate(2000, 5, 2);
+const arg = { year: 2000, month: 5, day: 2, calendar };
+instance.equals(arg);
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..190426203f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-constructor-in-calendar-fields.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.plaindate.prototype.equals
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+assert.throws(RangeError, () => instance.equals(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-duplicate-calendar-fields.js
new file mode 100644
index 0000000000..d233f81391
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-duplicate-calendar-fields.js
@@ -0,0 +1,19 @@
+// |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.plaindate.prototype.equals
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['day'], ['month'], ['monthCode'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar };
+ const instance = new Temporal.PlainDate(2000, 5, 2);
+
+ assert.throws(RangeError, () => instance.equals(arg));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-leap-second.js
new file mode 100644
index 0000000000..6a8b47c6f6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-leap-second.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.plaindate.prototype.equals
+description: Leap second is a valid ISO string for PlainDate
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2016, 12, 31);
+
+let arg = "2016-12-31T23:59:60";
+const result1 = instance.equals(arg);
+assert.sameValue(
+ result1,
+ true,
+ "leap second is a valid ISO string for PlainDate"
+);
+
+arg = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+const result2 = instance.equals(arg);
+assert.sameValue(
+ result2,
+ true,
+ "second: 60 is ignored in property bag for PlainDate"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-number.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-number.js
new file mode 100644
index 0000000000..ce8907a765
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/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.plaindate.prototype.equals
+description: A number cannot be used in place of a Temporal.PlainDate
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(1976, 11, 18);
+
+const numbers = [
+ 1,
+ 19761118,
+ -19761118,
+ 1234567890,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => instance.equals(arg),
+ 'Numbers cannot be used in place of an ISO string for PlainDate'
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-object-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-object-invalid.js
new file mode 100644
index 0000000000..3d78637c6d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-object-invalid.js
@@ -0,0 +1,23 @@
+// |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.plaindate.prototype.equals
+description: Appropriate error thrown when object argument is invalid
+features: [Temporal]
+---*/
+
+const instance = Temporal.PlainDate.from({ year: 2000, month: 5, day: 2 });
+
+// End up in ISODateFromFields with missing fields
+assert.throws(TypeError, () => instance.equals({}), "plain object");
+assert.throws(TypeError, () => instance.equals({ year: 1972, month: 7 }), "only year, month");
+assert.throws(TypeError, () => instance.equals({ year: 1972, month: 7 }), "only year, month");
+assert.throws(TypeError, () => instance.equals({ year: 1972, day: 7 }), "only year, day");
+assert.throws(TypeError, () => instance.equals(Temporal.PlainDate), "Temporal.PlainDate");
+
+// Tries to get fields with an invalid receiver
+assert.throws(TypeError, () => instance.equals(Temporal.PlainDate.prototype), "Temporal.PlainDate.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-object-valid.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-object-valid.js
new file mode 100644
index 0000000000..635405aa14
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-object-valid.js
@@ -0,0 +1,41 @@
+// |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.plaindate.prototype.equals
+description: equals with a valid property bag
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+assert.sameValue(instance.equals({ year: 2000, month: 5, day: 2 }), true, "same date");
+assert.sameValue(instance.equals({ year: 2000, month: 5, day: 4 }), false, "different date");
+
+const calendar = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "a",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+assert.sameValue(instance.withCalendar(calendar).equals({ year: 2000, month: 5, day: 2 }),
+ false, "different calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-plaindatetime.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-plaindatetime.js
new file mode 100644
index 0000000000..76e9e3464c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-plaindatetime.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.plaindate.prototype.equals
+description: Fast path for converting Temporal.PlainDateTime to Temporal.PlainDate by reading internal slots
+info: |
+ sec-temporal.plaindate.prototype.equals step 3:
+ 3. Set _other_ to ? ToTemporalDate(_other_).
+ sec-temporal-totemporaldate step 2.b:
+ b. If _item_ has an [[InitializedTemporalDateTime]] internal slot, then
+ i. Return ! CreateTemporalDate(_item_.[[ISOYear]], _item_.[[ISOMonth]], _item_.[[ISODay]], _item_.[[Calendar]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkPlainDateTimeConversionFastPath((datetime) => {
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ assert(date.equals(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-propertybag-calendar-case-insensitive.js
new file mode 100644
index 0000000000..294ce1d006
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-propertybag-calendar-case-insensitive.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.plaindate.prototype.equals
+description: The calendar name is case-insensitive
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(1976, 11, 18);
+
+const calendar = "IsO8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.equals(arg);
+assert.sameValue(result, true, "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-propertybag-calendar-leap-second.js
new file mode 100644
index 0000000000..8182dd7587
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-propertybag-calendar-leap-second.js
@@ -0,0 +1,23 @@
+// |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.plaindate.prototype.equals
+description: Leap second is a valid ISO string for a calendar in a property bag
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(1976, 11, 18);
+
+const calendar = "2016-12-31T23:59:60";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.equals(arg);
+assert.sameValue(
+ result,
+ true,
+ "leap second is a valid ISO string for calendar"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-propertybag-calendar-number.js
new file mode 100644
index 0000000000..9510983c61
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-propertybag-calendar-number.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.plaindate.prototype.equals
+description: A number as calendar in a property bag is not accepted
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(1976, 11, 18);
+
+const numbers = [
+ 1,
+ 19970327,
+ -19970327,
+ 1234567890,
+];
+
+for (const calendar of numbers) {
+ const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+ assert.throws(
+ TypeError,
+ () => instance.equals(arg),
+ "Numbers cannot be used as a calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-propertybag-calendar-string.js
new file mode 100644
index 0000000000..637ccc83eb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-propertybag-calendar-string.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.plaindate.prototype.equals
+description: A calendar ID is valid input for Calendar
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(1976, 11, 18);
+
+const calendar = "iso8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.equals(arg);
+assert.sameValue(result, true, `Calendar created from string "${calendar}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-propertybag-calendar-wrong-type.js
new file mode 100644
index 0000000000..caacd9266a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-propertybag-calendar-wrong-type.js
@@ -0,0 +1,46 @@
+// |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.plaindate.prototype.equals
+description: >
+ Appropriate error thrown when a calendar property from a property bag cannot
+ be converted to a calendar object or string
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [calendar, description] of primitiveTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ typeof calendar === 'string' ? RangeError : TypeError,
+ () => instance.equals(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+ [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields()
+];
+
+for (const [calendar, description] of typeErrorTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(TypeError, () => instance.equals(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/PlainDate/prototype/equals/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-propertybag-calendar-year-zero.js
new file mode 100644
index 0000000000..dc0a12c58a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-propertybag-calendar-year-zero.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.plaindate.prototype.equals
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T17:45",
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+01:00",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+const instance = new Temporal.PlainDate(2000, 5, 2);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-proto-in-calendar-fields.js
new file mode 100644
index 0000000000..5bc1294f59
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-proto-in-calendar-fields.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.plaindate.prototype.equals
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+assert.throws(RangeError, () => instance.equals(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..dfb9082b99
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-calendar-annotation.js
@@ -0,0 +1,33 @@
+// |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.plaindate.prototype.equals
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02[u-ca=iso8601]", "without time or time zone"],
+ ["2000-05-02[UTC][u-ca=iso8601]", "with time zone and no time"],
+ ["2000-05-02T15:23[u-ca=iso8601]", "without time zone"],
+ ["2000-05-02T15:23[UTC][u-ca=iso8601]", "with time zone"],
+ ["2000-05-02T15:23[!u-ca=iso8601]", "with ! and no time zone"],
+ ["2000-05-02T15:23[UTC][!u-ca=iso8601]", "with ! and time zone"],
+ ["2000-05-02T15:23[u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.equals(arg);
+
+ assert.sameValue(
+ result,
+ true,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..a8799d431c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-critical-unknown-annotation.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.plaindate.prototype.equals
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[!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.PlainDate(2000, 5, 2);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..7625140eec
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-date-with-utc-offset.js
@@ -0,0 +1,48 @@
+// |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.plaindate.prototype.equals
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+const validStrings = [
+ "2000-05-02T00+00:00",
+ "2000-05-02T00+00:00[UTC]",
+ "2000-05-02T00+00:00[!UTC]",
+ "2000-05-02T00-02:30[America/St_Johns]",
+];
+
+for (const arg of validStrings) {
+ const result = instance.equals(arg);
+
+ assert.sameValue(
+ result,
+ true,
+ `"${arg}" is a valid UTC offset with time for PlainDate`
+ );
+}
+
+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.equals(arg),
+ `"${arg}" UTC offset without time is not valid for PlainDate`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-invalid.js
new file mode 100644
index 0000000000..f16df08054
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-invalid.js
@@ -0,0 +1,64 @@
+// |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.plaindate.prototype.equals
+description: >
+ RangeError thrown if an invalid ISO string (or syntactically valid ISO string
+ that is not supported) is used as a PlainDate
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ // invalid ISO strings:
+ "",
+ "invalid iso8601",
+ "2020-01-00",
+ "2020-01-32",
+ "2020-02-30",
+ "2021-02-29",
+ "2020-00-01",
+ "2020-13-01",
+ "2020-01-01T",
+ "2020-01-01T25:00:00",
+ "2020-01-01T01:60:00",
+ "2020-01-01T01:60:61",
+ "2020-01-01junk",
+ "2020-01-01T00:00:00junk",
+ "2020-01-01T00:00:00+00:00junk",
+ "2020-01-01T00:00:00+00:00[UTC]junk",
+ "2020-01-01T00:00:00+00:00[UTC][u-ca=iso8601]junk",
+ "02020-01-01",
+ "2020-001-01",
+ "2020-01-001",
+ "2020-01-01T001",
+ "2020-01-01T01:001",
+ "2020-01-01T01:01:001",
+ // valid, but forms not supported in Temporal:
+ "2020-W01-1",
+ "2020-001",
+ "+0002020-01-01",
+ // valid, but this calendar must not exist:
+ "2020-01-01[u-ca=notexist]",
+ // may be valid in other contexts, but insufficient information for PlainDate:
+ "2020-01",
+ "+002020-01",
+ "01-01",
+ "2020-W01",
+ "P1Y",
+ "-P12Y",
+ // valid, but outside the supported range:
+ "-999999-01-01",
+ "+999999-01-01",
+];
+const instance = new Temporal.PlainDate(2000, 5, 2);
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ `"${arg}" should not be a valid ISO string for a PlainDate`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..6d02de0a76
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/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.plaindate.prototype.equals
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[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.PlainDate(2000, 5, 2);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..f444e8bb2d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-multiple-time-zone.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.plaindate.prototype.equals
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[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.PlainDate(2000, 5, 2);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-time-separators.js
new file mode 100644
index 0000000000..cf9bd7cf12
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-time-separators.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.plaindate.prototype.equals
+description: Time separator in string argument can vary
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02T15:23", "uppercase T"],
+ ["2000-05-02t15:23", "lowercase T"],
+ ["2000-05-02 15:23", "space between date and time"],
+];
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.equals(arg);
+
+ assert.sameValue(
+ result,
+ true,
+ `variant time separators (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..621e81d60a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-time-zone-annotation.js
@@ -0,0 +1,38 @@
+// |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.plaindate.prototype.equals
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02[Asia/Kolkata]", "named, with no time"],
+ ["2000-05-02[!Europe/Vienna]", "named, with ! and no time"],
+ ["2000-05-02[+00:00]", "numeric, with no time"],
+ ["2000-05-02[!-02:30]", "numeric, with ! and no time"],
+ ["2000-05-02T15:23[America/Sao_Paulo]", "named, with no offset"],
+ ["2000-05-02T15:23[!Asia/Tokyo]", "named, with ! and no offset"],
+ ["2000-05-02T15:23[-02:30]", "numeric, with no offset"],
+ ["2000-05-02T15:23[!+00:00]", "numeric, with ! and no offset"],
+ ["2000-05-02T15:23+00:00[America/New_York]", "named, with offset"],
+ ["2000-05-02T15:23+00:00[!UTC]", "named, with offset and !"],
+ ["2000-05-02T15:23+00:00[+01:00]", "numeric, with offset"],
+ ["2000-05-02T15:23+00:00[!-08:00]", "numeric, with offset and !"],
+];
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.equals(arg);
+
+ assert.sameValue(
+ result,
+ true,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..9b669a031d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-unknown-annotation.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.plaindate.prototype.equals
+description: Various forms of unknown annotation
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02[foo=bar]", "without time"],
+ ["2000-05-02T15:23[foo=bar]", "alone"],
+ ["2000-05-02T15:23[UTC][foo=bar]", "with time zone"],
+ ["2000-05-02T15:23[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["2000-05-02T15:23[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["2000-05-02T15:23[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.equals(arg);
+
+ assert.sameValue(
+ result,
+ true,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..483bc36245
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-with-utc-designator.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.plaindate.prototype.equals
+description: RangeError thrown if a string with UTC designator is used as a PlainDate
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "2019-10-01T09:00:00Z",
+ "2019-10-01T09:00:00Z[UTC]",
+];
+const instance = new Temporal.PlainDate(2000, 5, 2);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ "String with UTC designator should not be valid as a PlainDate"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string.js
new file mode 100644
index 0000000000..436d598fc1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string.js
@@ -0,0 +1,40 @@
+// |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.plaindate.prototype.equals
+description: equals with a valid string
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+assert.sameValue(instance.equals("2000-05-02"), true, "same date");
+assert.sameValue(instance.equals("2000-05-04"), false, "different date");
+
+const calendar = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "a",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+assert.sameValue(instance.withCalendar(calendar).equals("2000-05-02"), false, "different calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-wrong-type.js
new file mode 100644
index 0000000000..214b01461d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/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.plaindate.prototype.equals
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainDate
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+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.equals(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.PlainDate, "Temporal.PlainDate, object"],
+ [Temporal.PlainDate.prototype, "Temporal.PlainDate.prototype, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.equals(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/PlainDate/prototype/equals/argument-zoneddatetime-convert.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-zoneddatetime-convert.js
new file mode 100644
index 0000000000..5136721a65
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-zoneddatetime-convert.js
@@ -0,0 +1,22 @@
+// |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.plaindate.prototype.equals
+description: An exception from TimeZone#getOffsetNanosecondsFor() is propagated.
+features: [Temporal]
+---*/
+
+class TZ extends Temporal.TimeZone {
+ constructor() { super("UTC") }
+ getOffsetNanosecondsFor() { throw new Test262Error() }
+}
+
+const tz = new TZ();
+const arg = new Temporal.ZonedDateTime(0n, tz);
+const instance = new Temporal.PlainDate(1976, 11, 18);
+
+assert.throws(Test262Error, () => instance.equals(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-zoneddatetime-slots.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-zoneddatetime-slots.js
new file mode 100644
index 0000000000..ffee040bf0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-zoneddatetime-slots.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.plaindate.prototype.equals
+description: Getters are not called when converting a ZonedDateTime to a PlainDate.
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.ZonedDateTime.prototype);
+const getters = ["year", "month", "monthCode", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond", "calendar"];
+
+for (const property of getters) {
+ Object.defineProperty(Temporal.ZonedDateTime.prototype, property, {
+ get() {
+ actual.push(`get ${property}`);
+ const value = prototypeDescrs[property].get.call(this);
+ return {
+ toString() {
+ actual.push(`toString ${property}`);
+ return value.toString();
+ },
+ valueOf() {
+ actual.push(`valueOf ${property}`);
+ return value;
+ },
+ };
+ },
+ });
+}
+
+const arg = new Temporal.ZonedDateTime(0n, "UTC");
+const instance = new Temporal.PlainDate(1976, 11, 18);
+instance.equals(arg);
+assert.compareArray(actual, []);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..bd6dae260c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/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.plaindate.prototype.equals
+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 date = new Temporal.PlainDate(2000, 5, 2);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => date.equals(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..bc3c919fc5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/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.plaindate.prototype.equals
+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 date = new Temporal.PlainDate(2000, 5, 2);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => date.equals(datetime),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..94a7516198
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/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.plaindate.prototype.equals
+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 date = new Temporal.PlainDate(2000, 5, 2);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => date.equals(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..4d63297d7e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/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.plaindate.prototype.equals
+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 date = new Temporal.PlainDate(2000, 5, 2);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => date.equals(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/basic.js
new file mode 100644
index 0000000000..67aabdfb73
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/basic.js
@@ -0,0 +1,18 @@
+// |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.plaindate.protoype.equals
+description: basic tests
+features: [Temporal]
+---*/
+
+const date1 = new Temporal.PlainDate(1976, 11, 18);
+const date2 = new Temporal.PlainDate(1914, 2, 23);
+const date3 = new Temporal.PlainDate(1914, 2, 23);
+assert.sameValue(date1.equals(date2), false, "different dates");
+assert.sameValue(date2.equals(date3), true, "same dates");
+assert.sameValue(date2.equals(date2), true, "same objects");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/branding.js
new file mode 100644
index 0000000000..4cb44e59d3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/branding.js
@@ -0,0 +1,27 @@
+// |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.plaindate.prototype.equals
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const equals = Temporal.PlainDate.prototype.equals;
+
+assert.sameValue(typeof equals, "function");
+
+const args = [new Temporal.PlainDate(2022, 6, 22)];
+
+assert.throws(TypeError, () => equals.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => equals.apply(null, args), "null");
+assert.throws(TypeError, () => equals.apply(true, args), "true");
+assert.throws(TypeError, () => equals.apply("", args), "empty string");
+assert.throws(TypeError, () => equals.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => equals.apply(1, args), "1");
+assert.throws(TypeError, () => equals.apply({}, args), "plain object");
+assert.throws(TypeError, () => equals.apply(Temporal.PlainDate, args), "Temporal.PlainDate");
+assert.throws(TypeError, () => equals.apply(Temporal.PlainDate.prototype, args), "Temporal.PlainDate.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..542b98c5fa
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plaindate.prototype.equals
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const idOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "id");
+Object.defineProperty(Temporal.Calendar.prototype, "id", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("id should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDate(2000, 5, 2, "iso8601");
+instance.equals(new Temporal.PlainDate(2000, 5, 2));
+
+Object.defineProperty(Temporal.Calendar.prototype, "id", idOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/builtin.js
new file mode 100644
index 0000000000..84abf7bc2e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/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.plaindate.prototype.equals
+description: >
+ Tests that Temporal.PlainDate.prototype.equals
+ 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.PlainDate.prototype.equals),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDate.prototype.equals),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDate.prototype.equals),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDate.prototype.equals.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/calendar-call-different.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/calendar-call-different.js
new file mode 100644
index 0000000000..52352e6e43
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/calendar-call-different.js
@@ -0,0 +1,37 @@
+// |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.plaindate.protoype.equals
+description: test if the calendar is compared
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+class CalendarTraceId extends Temporal.Calendar {
+ constructor(id) {
+ super("iso8601");
+ this.id_ = id;
+ this.calls = 0;
+ }
+ get id() {
+ ++this.calls;
+ return this.id_;
+ }
+ toString() {
+ TemporalHelpers.assertUnreachable('toString should not be called');
+ }
+};
+
+const calendar1 = new CalendarTraceId("a");
+const date1 = new Temporal.PlainDate(1914, 2, 23, calendar1);
+
+const calendar2 = new CalendarTraceId("b");
+const date2 = new Temporal.PlainDate(1914, 2, 23, calendar2);
+
+assert.sameValue(date1.equals(date2), false, "different calendars");
+assert.sameValue(calendar1.calls, 1, "calendar1 id getter calls");
+assert.sameValue(calendar2.calls, 1, "calendar2 id getter calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/calendar-call-same.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/calendar-call-same.js
new file mode 100644
index 0000000000..15f32174c4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/calendar-call-same.js
@@ -0,0 +1,37 @@
+// |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.plaindate.protoype.equals
+description: test if the calendar is compared
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+class CalendarTraceToString extends Temporal.Calendar {
+ constructor(id) {
+ super("iso8601");
+ this.id_ = id;
+ this.calls = 0;
+ }
+ get id() {
+ ++this.calls;
+ return this.id_;
+ }
+ toString() {
+ TemporalHelpers.assertUnreachable('toString should not be called');
+ }
+};
+
+const calendar1 = new CalendarTraceToString("a");
+const date1 = new Temporal.PlainDate(1914, 2, 23, calendar1);
+
+const calendar2 = new CalendarTraceToString("a");
+const date2 = new Temporal.PlainDate(1914, 2, 23, calendar2);
+
+assert.sameValue(date1.equals(date2), true, "same calendar id");
+assert.sameValue(calendar1.calls, 1, "calendar1 id getter calls");
+assert.sameValue(calendar2.calls, 1, "calendar2 id getter calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/calendar-datefromfields-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/calendar-datefromfields-called-with-options-undefined.js
new file mode 100644
index 0000000000..bb9ddd5be6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/calendar-datefromfields-called-with-options-undefined.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.plaindate.prototype.equals
+description: >
+ Calendar.dateFromFields method is called with undefined as the options value
+ when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
+const instance = new Temporal.PlainDate(2000, 5, 2, calendar);
+instance.equals({ year: 2000, month: 5, day: 3, calendar });
+assert.sameValue(calendar.dateFromFieldsCallCount, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/calendar-fields-iterable.js
new file mode 100644
index 0000000000..71b0cbeb7b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/calendar-fields-iterable.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.plaindate.prototype.equals
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plaindate.prototype.equals step 3:
+ 3. Set _other_ to ? ToTemporalDate(_other_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const date = new Temporal.PlainDate(2000, 5, 2, calendar1);
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+date.equals({ year: 2005, month: 6, day: 2, calendar: calendar2 });
+
+assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/calendar-no-call.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/calendar-no-call.js
new file mode 100644
index 0000000000..533feb35e4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/calendar-no-call.js
@@ -0,0 +1,33 @@
+// |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.plaindate.protoype.equals
+description: test if the calendar is compared
+features: [Temporal]
+---*/
+
+class CalendarTraceToString extends Temporal.Calendar {
+ constructor(id) {
+ super("iso8601");
+ this.id_ = id;
+ this.calls = 0;
+ }
+ toString() {
+ ++this.calls;
+ return this.id_;
+ }
+};
+
+const calendar1 = new CalendarTraceToString("a");
+const date1 = new Temporal.PlainDate(1914, 2, 23, calendar1);
+
+const calendar2 = new CalendarTraceToString("b");
+const date2 = new Temporal.PlainDate(1914, 2, 22, calendar2);
+
+assert.sameValue(date1.equals(date2), false, "different ISO dates");
+assert.sameValue(calendar1.calls, 0, "calendar1 toString() calls");
+assert.sameValue(calendar2.calls, 0, "calendar2 toString() calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/calendar-temporal-object.js
new file mode 100644
index 0000000000..4ea36b8a70
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/calendar-temporal-object.js
@@ -0,0 +1,29 @@
+// |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.plaindate.prototype.equals
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plaindate.prototype.equals step 3:
+ 3. Set _other_ to ? ToTemporalDate(_other_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const date = new Temporal.PlainDate(2000, 5, 2, temporalObject);
+ date.equals({ year: 2005, month: 6, day: 2, calendar: temporalObject });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..fd8f9bcb1c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/infinity-throws-rangeerror.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.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plaindate.prototype.equals
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+const base = { year: 2000, month: 5, day: 2 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.equals({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.equals({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/length.js
new file mode 100644
index 0000000000..ba9ffa7e32
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/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.plaindate.prototype.equals
+description: Temporal.PlainDate.prototype.equals.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.PlainDate.prototype.equals, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/name.js
new file mode 100644
index 0000000000..2e4a79abc7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/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.plaindate.prototype.equals
+description: Temporal.PlainDate.prototype.equals.name is "equals".
+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.PlainDate.prototype.equals, "name", {
+ value: "equals",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/not-a-constructor.js
new file mode 100644
index 0000000000..c7fd5403e8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/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.plaindate.prototype.equals
+description: >
+ Temporal.PlainDate.prototype.equals 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.PlainDate.prototype.equals();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDate.prototype.equals), false,
+ "isConstructor(Temporal.PlainDate.prototype.equals)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/prop-desc.js
new file mode 100644
index 0000000000..690c834d65
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/prop-desc.js
@@ -0,0 +1,24 @@
+// |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.plaindate.prototype.equals
+description: The "equals" property of Temporal.PlainDate.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDate.prototype.equals,
+ "function",
+ "`typeof PlainDate.prototype.equals` is `function`"
+);
+
+verifyProperty(Temporal.PlainDate.prototype, "equals", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/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/PlainDate/prototype/equals/year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/year-zero.js
new file mode 100644
index 0000000000..d62248d870
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/year-zero.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.plaindate.prototype.equals
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T00:45",
+ "-000000-10-31T00:45+01:00",
+ "-000000-10-31T00:45+00:00[UTC]",
+];
+const instance = new Temporal.PlainDate(2000, 5, 2);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getCalendar/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getCalendar/branding.js
new file mode 100644
index 0000000000..d941757460
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getCalendar/branding.js
@@ -0,0 +1,25 @@
+// |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.plaindate.prototype.getcalendar
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const getCalendar = Temporal.PlainDate.prototype.getCalendar;
+
+assert.sameValue(typeof getCalendar, "function");
+
+assert.throws(TypeError, () => getCalendar.call(undefined), "undefined");
+assert.throws(TypeError, () => getCalendar.call(null), "null");
+assert.throws(TypeError, () => getCalendar.call(true), "true");
+assert.throws(TypeError, () => getCalendar.call(""), "empty string");
+assert.throws(TypeError, () => getCalendar.call(Symbol()), "symbol");
+assert.throws(TypeError, () => getCalendar.call(1), "1");
+assert.throws(TypeError, () => getCalendar.call({}), "plain object");
+assert.throws(TypeError, () => getCalendar.call(Temporal.PlainDate), "Temporal.PlainDate");
+assert.throws(TypeError, () => getCalendar.call(Temporal.PlainDate.prototype), "Temporal.PlainDate.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getCalendar/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getCalendar/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getCalendar/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getCalendar/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getCalendar/builtin.js
new file mode 100644
index 0000000000..31432c79ae
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getCalendar/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.plaindate.prototype.getcalendar
+description: >
+ Tests that Temporal.PlainDate.prototype.getCalendar
+ 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.PlainDate.prototype.getCalendar),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDate.prototype.getCalendar),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDate.prototype.getCalendar),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDate.prototype.getCalendar.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getCalendar/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getCalendar/length.js
new file mode 100644
index 0000000000..7ed0f11c4e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getCalendar/length.js
@@ -0,0 +1,28 @@
+// |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.plaindate.prototype.getcalendar
+description: Temporal.PlainDate.prototype.getCalendar.length is 0
+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.PlainDate.prototype.getCalendar, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getCalendar/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getCalendar/name.js
new file mode 100644
index 0000000000..7a508ff62e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getCalendar/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.plaindate.prototype.getcalendar
+description: Temporal.PlainDate.prototype.getCalendar.name is "getCalendar".
+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.PlainDate.prototype.getCalendar, "name", {
+ value: "getCalendar",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getCalendar/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getCalendar/not-a-constructor.js
new file mode 100644
index 0000000000..2cd96bf644
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getCalendar/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.plaindate.prototype.getcalendar
+description: >
+ Temporal.PlainDate.prototype.getCalendar 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.PlainDate.prototype.getCalendar();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDate.prototype.getCalendar), false,
+ "isConstructor(Temporal.PlainDate.prototype.getCalendar)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getCalendar/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getCalendar/prop-desc.js
new file mode 100644
index 0000000000..7087f4a214
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getCalendar/prop-desc.js
@@ -0,0 +1,24 @@
+// |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.plaindate.prototype.getcalendar
+description: The "getCalendar" property of Temporal.PlainDate.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDate.prototype.getCalendar,
+ "function",
+ "`typeof PlainDate.prototype.getCalendar` is `function`"
+);
+
+verifyProperty(Temporal.PlainDate.prototype, "getCalendar", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getCalendar/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getCalendar/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getCalendar/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/PlainDate/prototype/getISOFields/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/branding.js
new file mode 100644
index 0000000000..54eb2801ca
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/branding.js
@@ -0,0 +1,25 @@
+// |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.plaindate.prototype.getisofields
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const getISOFields = Temporal.PlainDate.prototype.getISOFields;
+
+assert.sameValue(typeof getISOFields, "function");
+
+assert.throws(TypeError, () => getISOFields.call(undefined), "undefined");
+assert.throws(TypeError, () => getISOFields.call(null), "null");
+assert.throws(TypeError, () => getISOFields.call(true), "true");
+assert.throws(TypeError, () => getISOFields.call(""), "empty string");
+assert.throws(TypeError, () => getISOFields.call(Symbol()), "symbol");
+assert.throws(TypeError, () => getISOFields.call(1), "1");
+assert.throws(TypeError, () => getISOFields.call({}), "plain object");
+assert.throws(TypeError, () => getISOFields.call(Temporal.PlainDate), "Temporal.PlainDate");
+assert.throws(TypeError, () => getISOFields.call(Temporal.PlainDate.prototype), "Temporal.PlainDate.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/builtin.js
new file mode 100644
index 0000000000..6e413062fe
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/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.plaindate.prototype.getisofields
+description: >
+ Tests that Temporal.PlainDate.prototype.getISOFields
+ 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.PlainDate.prototype.getISOFields),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDate.prototype.getISOFields),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDate.prototype.getISOFields),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDate.prototype.getISOFields.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/custom.js
new file mode 100644
index 0000000000..9af214f61e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/custom.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.plaindate.prototype.getisofields
+description: getISOFields does not call into user code.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarThrowEverything();
+const instance = new Temporal.PlainDate(2000, 5, 2, calendar);
+const result = instance.getISOFields();
+
+assert.sameValue(result.isoYear, 2000, "isoYear result");
+assert.sameValue(result.isoMonth, 5, "isoMonth result");
+assert.sameValue(result.isoDay, 2, "isoDay result");
+assert.sameValue(result.calendar, calendar, "calendar result");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/field-names.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/field-names.js
new file mode 100644
index 0000000000..e9fea72883
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/field-names.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.plaindate.prototype.getisofields
+description: Correct field names on the object returned from getISOFields
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(2000, 5, 2);
+
+const result = date.getISOFields();
+assert.sameValue(result.isoYear, 2000, "isoYear result");
+assert.sameValue(result.isoMonth, 5, "isoMonth result");
+assert.sameValue(result.isoDay, 2, "isoDay result");
+assert.sameValue(result.calendar, "iso8601", "calendar result");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/field-prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/field-prop-desc.js
new file mode 100644
index 0000000000..3f033cb633
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/field-prop-desc.js
@@ -0,0 +1,30 @@
+// |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.plaindate.prototype.getisofields
+description: Properties on the returned object have the correct descriptor
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "calendar",
+ "isoDay",
+ "isoMonth",
+ "isoYear",
+];
+
+const date = new Temporal.PlainDate(2000, 5, 2);
+const result = date.getISOFields();
+
+for (const property of expected) {
+ verifyProperty(result, property, {
+ writable: true,
+ enumerable: true,
+ configurable: true,
+ });
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/field-traversal-order.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/field-traversal-order.js
new file mode 100644
index 0000000000..6417f1d3fd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/field-traversal-order.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.plaindate.prototype.getisofields
+description: Properties added in correct order to object returned from getISOFields
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "calendar",
+ "isoDay",
+ "isoMonth",
+ "isoYear",
+];
+
+const date = new Temporal.PlainDate(2000, 5, 2);
+const result = date.getISOFields();
+
+assert.compareArray(Object.keys(result), expected);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/length.js
new file mode 100644
index 0000000000..34dd97f568
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/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.plaindate.prototype.getisofields
+description: Temporal.PlainDate.prototype.getISOFields.length is 0
+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.PlainDate.prototype.getISOFields, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/name.js
new file mode 100644
index 0000000000..c4bb4b942c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/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.plaindate.prototype.getisofields
+description: Temporal.PlainDate.prototype.getISOFields.name is "getISOFields".
+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.PlainDate.prototype.getISOFields, "name", {
+ value: "getISOFields",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/not-a-constructor.js
new file mode 100644
index 0000000000..3484530b71
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/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.plaindate.prototype.getisofields
+description: >
+ Temporal.PlainDate.prototype.getISOFields 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.PlainDate.prototype.getISOFields();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDate.prototype.getISOFields), false,
+ "isConstructor(Temporal.PlainDate.prototype.getISOFields)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/prop-desc.js
new file mode 100644
index 0000000000..49af4b6386
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/prop-desc.js
@@ -0,0 +1,24 @@
+// |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.plaindate.prototype.getisofields
+description: The "getISOFields" property of Temporal.PlainDate.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDate.prototype.getISOFields,
+ "function",
+ "`typeof PlainDate.prototype.getISOFields` is `function`"
+);
+
+verifyProperty(Temporal.PlainDate.prototype, "getISOFields", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/prototype.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/prototype.js
new file mode 100644
index 0000000000..4dd3c9ebb6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/prototype.js
@@ -0,0 +1,15 @@
+// |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.plaindate.prototype.getisofields
+description: Correct prototype on the object returned from getISOFields
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+const result = instance.getISOFields();
+assert.sameValue(Object.getPrototypeOf(result), Object.prototype, "prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/getISOFields/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/PlainDate/prototype/inLeapYear/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/basic.js
new file mode 100644
index 0000000000..8a7aabb4e8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/basic.js
@@ -0,0 +1,16 @@
+// |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-get-temporal.plaindate.prototype.inleapyear
+description: Basic test for inLeapYear
+features: [Temporal]
+---*/
+
+assert.sameValue((new Temporal.PlainDate(1976, 11, 18)).inLeapYear,
+ true, "leap year");
+assert.sameValue((new Temporal.PlainDate(1977, 11, 18)).inLeapYear,
+ false, "non-leap year");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/branding.js
new file mode 100644
index 0000000000..ef7e7fb073
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/branding.js
@@ -0,0 +1,25 @@
+// |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-get-temporal.plaindate.prototype.inleapyear
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const inLeapYear = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "inLeapYear").get;
+
+assert.sameValue(typeof inLeapYear, "function");
+
+assert.throws(TypeError, () => inLeapYear.call(undefined), "undefined");
+assert.throws(TypeError, () => inLeapYear.call(null), "null");
+assert.throws(TypeError, () => inLeapYear.call(true), "true");
+assert.throws(TypeError, () => inLeapYear.call(""), "empty string");
+assert.throws(TypeError, () => inLeapYear.call(Symbol()), "symbol");
+assert.throws(TypeError, () => inLeapYear.call(1), "1");
+assert.throws(TypeError, () => inLeapYear.call({}), "plain object");
+assert.throws(TypeError, () => inLeapYear.call(Temporal.PlainDate), "Temporal.PlainDate");
+assert.throws(TypeError, () => inLeapYear.call(Temporal.PlainDate.prototype), "Temporal.PlainDate.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..690016923a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plaindate.prototype.inleapyear
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const inLeapYearOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "inLeapYear");
+Object.defineProperty(Temporal.Calendar.prototype, "inLeapYear", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("inLeapYear should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDate(2000, 5, 2, "iso8601");
+instance.inLeapYear;
+
+Object.defineProperty(Temporal.Calendar.prototype, "inLeapYear", inLeapYearOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/custom.js
new file mode 100644
index 0000000000..5c7b285116
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/custom.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-get-temporal.plaindate.prototype.inleapyear
+description: Custom calendar tests for inLeapYear().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ inLeapYear(...args) {
+ ++calls;
+ assert.compareArray(args, [pd], "inLeapYear arguments");
+ return true;
+ }
+}
+
+const calendar = new CustomCalendar();
+const pd = new Temporal.PlainDate(1830, 8, 25, calendar);
+const result = pd.inLeapYear;
+assert.sameValue(result, true, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/prop-desc.js
new file mode 100644
index 0000000000..9a1a9b4e48
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/prop-desc.js
@@ -0,0 +1,17 @@
+// |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-get-temporal.plaindate.prototype.inleapyear
+description: The "inLeapYear" property of Temporal.PlainDate.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "inLeapYear");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/validate-calendar-value.js
new file mode 100644
index 0000000000..a21e641c24
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/validate-calendar-value.js
@@ -0,0 +1,56 @@
+// |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-get-temporal.plaindate.prototype.inleapyear
+description: Validate result returned from calendar inLeapYear() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [null, TypeError],
+ [0, TypeError],
+ [-0, TypeError],
+ [42, TypeError],
+ [7.1, TypeError],
+ [NaN, TypeError],
+ [Infinity, TypeError],
+ [-Infinity, TypeError],
+ ["", TypeError],
+ ["a string", TypeError],
+ ["0", TypeError],
+ [Symbol("foo"), TypeError],
+ [0n, TypeError],
+ [42n, TypeError],
+ [{}, TypeError],
+ [{valueOf() { return false; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ inLeapYear() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainDate(1981, 12, 15, calendar);
+ assert.throws(error, () => instance.inLeapYear, `${typeof result} ${String(result)} not converted to boolean`);
+});
+
+const preservedResults = [
+ true,
+ false,
+];
+
+preservedResults.forEach(result => {
+ const calendar = new class extends Temporal.Calendar {
+ inLeapYear() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainDate(1981, 12, 15, calendar);
+ assert.sameValue(instance.inLeapYear, result, `${typeof result} ${String(result)} preserved`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/month/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/month/branding.js
new file mode 100644
index 0000000000..c43783d1e9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/month/branding.js
@@ -0,0 +1,25 @@
+// |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-get-temporal.plaindate.prototype.month
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const month = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "month").get;
+
+assert.sameValue(typeof month, "function");
+
+assert.throws(TypeError, () => month.call(undefined), "undefined");
+assert.throws(TypeError, () => month.call(null), "null");
+assert.throws(TypeError, () => month.call(true), "true");
+assert.throws(TypeError, () => month.call(""), "empty string");
+assert.throws(TypeError, () => month.call(Symbol()), "symbol");
+assert.throws(TypeError, () => month.call(1), "1");
+assert.throws(TypeError, () => month.call({}), "plain object");
+assert.throws(TypeError, () => month.call(Temporal.PlainDate), "Temporal.PlainDate");
+assert.throws(TypeError, () => month.call(Temporal.PlainDate.prototype), "Temporal.PlainDate.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/month/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/month/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/month/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/month/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/month/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..267c9f16a7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/month/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plaindate.prototype.month
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const monthOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "month");
+Object.defineProperty(Temporal.Calendar.prototype, "month", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("month should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDate(2000, 5, 2, "iso8601");
+instance.month;
+
+Object.defineProperty(Temporal.Calendar.prototype, "month", monthOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/month/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/month/custom.js
new file mode 100644
index 0000000000..bec164e029
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/month/custom.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-get-temporal.plaindate.prototype.month
+description: Custom calendar tests for month().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ month(...args) {
+ ++calls;
+ assert.compareArray(args, [pd], "month arguments");
+ return 7;
+ }
+}
+
+const calendar = new CustomCalendar();
+const pd = new Temporal.PlainDate(1830, 8, 25, calendar);
+const result = pd.month;
+assert.sameValue(result, 7, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/month/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/month/prop-desc.js
new file mode 100644
index 0000000000..185197f175
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/month/prop-desc.js
@@ -0,0 +1,17 @@
+// |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-get-temporal.plaindate.prototype.month
+description: The "month" property of Temporal.PlainDate.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "month");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/month/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/month/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/month/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/month/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/month/validate-calendar-value.js
new file mode 100644
index 0000000000..cf18755457
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/month/validate-calendar-value.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-get-temporal.plaindate.prototype.month
+description: Validate result returned from calendar month() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [null, TypeError],
+ [false, TypeError],
+ [Infinity, RangeError],
+ [-Infinity, RangeError],
+ [NaN, RangeError],
+ [-7, RangeError],
+ [-0.1, RangeError],
+ ["string", TypeError],
+ [Symbol("foo"), TypeError],
+ [7n, TypeError],
+ [{}, TypeError],
+ [true, TypeError],
+ [7.1, RangeError],
+ ["7", TypeError],
+ ["7.5", TypeError],
+ [{valueOf() { return 7; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ month() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainDate(1981, 12, 15, calendar);
+ assert.throws(error, () => instance.month, `${typeof result} ${String(result)} not converted to positive integer`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthCode/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthCode/branding.js
new file mode 100644
index 0000000000..3c863bb2e7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthCode/branding.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-get-temporal.plaindate.prototype.monthcode
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const monthCode = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "monthCode").get;
+
+assert.sameValue(typeof monthCode, "function");
+
+assert.throws(TypeError, () => monthCode.call(undefined), "undefined");
+assert.throws(TypeError, () => monthCode.call(null), "null");
+assert.throws(TypeError, () => monthCode.call(true), "true");
+assert.throws(TypeError, () => monthCode.call(""), "empty string");
+assert.throws(TypeError, () => monthCode.call(Symbol()), "symbol");
+assert.throws(TypeError, () => monthCode.call(1), "1");
+assert.throws(TypeError, () => monthCode.call({}), "plain object");
+assert.throws(TypeError, () => monthCode.call(Temporal.PlainDate), "Temporal.PlainDate");
+assert.throws(TypeError, () => monthCode.call(Temporal.PlainDate.prototype), "Temporal.PlainDate.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthCode/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthCode/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthCode/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthCode/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthCode/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..6d5f28b4e5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthCode/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plaindate.prototype.monthcode
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const monthCodeOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "monthCode");
+Object.defineProperty(Temporal.Calendar.prototype, "monthCode", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("monthCode should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDate(2000, 5, 2, "iso8601");
+instance.monthCode;
+
+Object.defineProperty(Temporal.Calendar.prototype, "monthCode", monthCodeOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthCode/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthCode/custom.js
new file mode 100644
index 0000000000..922cb28b48
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthCode/custom.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-get-temporal.plaindate.prototype.monthcode
+description: Custom calendar tests for monthCode().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ monthCode(...args) {
+ ++calls;
+ assert.compareArray(args, [pd], "monthCode arguments");
+ return "M01";
+ }
+}
+
+const calendar = new CustomCalendar();
+const pd = new Temporal.PlainDate(1830, 8, 25, calendar);
+const result = pd.monthCode;
+assert.sameValue(result, "M01", "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthCode/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthCode/prop-desc.js
new file mode 100644
index 0000000000..5200447989
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthCode/prop-desc.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-get-temporal.plaindate.prototype.monthcode
+description: The "monthCode" property of Temporal.PlainDate.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "monthCode");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthCode/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthCode/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthCode/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthCode/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthCode/validate-calendar-value.js
new file mode 100644
index 0000000000..0de050312b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthCode/validate-calendar-value.js
@@ -0,0 +1,31 @@
+// |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-get-temporal.plaindate.prototype.monthcode
+description: Validate result returned from calendar monthCode() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [Symbol("foo"), TypeError],
+ [null, TypeError],
+ [true, TypeError],
+ [false, TypeError],
+ [7.1, TypeError],
+ [{toString() { return "M01"; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ monthCode() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainDate(1981, 12, 15, calendar);
+ assert.throws(error, () => instance.monthCode, `${typeof result} ${String(result)} not converted to string`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/basic.js
new file mode 100644
index 0000000000..e87dbbc43b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/basic.js
@@ -0,0 +1,14 @@
+// |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-get-temporal.plaindate.prototype.monthsinyear
+description: Basic tests for monthsInYear().
+features: [Temporal]
+---*/
+
+const plainDate = new Temporal.PlainDate(1976, 11, 18);
+assert.sameValue(plainDate.monthsInYear, 12);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/branding.js
new file mode 100644
index 0000000000..02ce82dd31
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/branding.js
@@ -0,0 +1,25 @@
+// |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-get-temporal.plaindate.prototype.monthsinyear
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const monthsInYear = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "monthsInYear").get;
+
+assert.sameValue(typeof monthsInYear, "function");
+
+assert.throws(TypeError, () => monthsInYear.call(undefined), "undefined");
+assert.throws(TypeError, () => monthsInYear.call(null), "null");
+assert.throws(TypeError, () => monthsInYear.call(true), "true");
+assert.throws(TypeError, () => monthsInYear.call(""), "empty string");
+assert.throws(TypeError, () => monthsInYear.call(Symbol()), "symbol");
+assert.throws(TypeError, () => monthsInYear.call(1), "1");
+assert.throws(TypeError, () => monthsInYear.call({}), "plain object");
+assert.throws(TypeError, () => monthsInYear.call(Temporal.PlainDate), "Temporal.PlainDate");
+assert.throws(TypeError, () => monthsInYear.call(Temporal.PlainDate.prototype), "Temporal.PlainDate.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..10606a3ab6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plaindate.prototype.monthsinyear
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const monthsInYearOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "monthsInYear");
+Object.defineProperty(Temporal.Calendar.prototype, "monthsInYear", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("monthsInYear should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDate(2000, 5, 2, "iso8601");
+instance.monthsInYear;
+
+Object.defineProperty(Temporal.Calendar.prototype, "monthsInYear", monthsInYearOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/custom.js
new file mode 100644
index 0000000000..aa3d856629
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/custom.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-get-temporal.plaindate.prototype.monthsinyear
+description: Custom calendar tests for monthsInYear().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ monthsInYear(...args) {
+ ++calls;
+ assert.compareArray(args, [pd], "monthsInYear arguments");
+ return 7;
+ }
+}
+
+const calendar = new CustomCalendar();
+const pd = new Temporal.PlainDate(1830, 8, 25, calendar);
+const result = pd.monthsInYear;
+assert.sameValue(result, 7, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/prop-desc.js
new file mode 100644
index 0000000000..a93ed88867
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/prop-desc.js
@@ -0,0 +1,17 @@
+// |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-get-temporal.plaindate.prototype.monthsinyear
+description: The "monthsInYear" property of Temporal.PlainDate.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "monthsInYear");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/validate-calendar-value.js
new file mode 100644
index 0000000000..d0cf9588bf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/validate-calendar-value.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-get-temporal.plaindate.prototype.monthsinyear
+description: Validate result returned from calendar monthsInYear() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [null, TypeError],
+ [false, TypeError],
+ [Infinity, RangeError],
+ [-Infinity, RangeError],
+ [NaN, RangeError],
+ [-7, RangeError],
+ [-0.1, RangeError],
+ ["string", TypeError],
+ [Symbol("foo"), TypeError],
+ [7n, TypeError],
+ [{}, TypeError],
+ [true, TypeError],
+ [7.1, RangeError],
+ ["7", TypeError],
+ ["7.5", TypeError],
+ [{valueOf() { return 7; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ monthsInYear() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainDate(1981, 12, 15, calendar);
+ assert.throws(error, () => instance.monthsInYear, `${typeof result} ${String(result)} not converted to positive integer`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/prop-desc.js
new file mode 100644
index 0000000000..9f00e5b72c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/prop-desc.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-plaindate-prototype
+description: The "prototype" property of Temporal.PlainDate
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(typeof Temporal.PlainDate.prototype, "object");
+assert.notSameValue(Temporal.PlainDate.prototype, null);
+
+verifyProperty(Temporal.PlainDate, "prototype", {
+ writable: false,
+ enumerable: false,
+ configurable: false,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..39f7b6b3fc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-builtin-calendar-no-array-iteration.js
@@ -0,0 +1,24 @@
+// |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.plaindate.prototype.since
+description: >
+ Calling the method with a property bag argument with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+const arg = { year: 2000, month: 5, day: 2, calendar: "iso8601" };
+instance.since(arg);
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-calendar-datefromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..c9a2c2753b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-calendar-datefromfields-called-with-null-prototype-fields.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.plaindate.prototype.since
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.PlainDate(2000, 5, 2);
+const arg = { year: 2000, month: 5, day: 2, calendar };
+instance.since(arg);
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..08aa6aebf8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-constructor-in-calendar-fields.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.plaindate.prototype.since
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+assert.throws(RangeError, () => instance.since(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-duplicate-calendar-fields.js
new file mode 100644
index 0000000000..366ef14b08
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-duplicate-calendar-fields.js
@@ -0,0 +1,19 @@
+// |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.plaindate.prototype.since
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['day'], ['month'], ['monthCode'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar };
+ const instance = new Temporal.PlainDate(2000, 5, 2);
+
+ assert.throws(RangeError, () => instance.since(arg));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-leap-second.js
new file mode 100644
index 0000000000..37acb5ed55
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-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.plaindate.prototype.since
+description: Leap second is a valid ISO string for PlainDate
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2016, 12, 31);
+
+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 PlainDate"
+);
+
+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 PlainDate"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-number.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-number.js
new file mode 100644
index 0000000000..b7d52930c9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/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.plaindate.prototype.since
+description: A number cannot be used in place of a Temporal.PlainDate
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(1976, 11, 18);
+
+const numbers = [
+ 1,
+ 19761118,
+ -19761118,
+ 1234567890,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => instance.since(arg),
+ 'Numbers cannot be used in place of an ISO string for PlainDate'
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-plaindatetime.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-plaindatetime.js
new file mode 100644
index 0000000000..0524d02ca7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-plaindatetime.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.plaindate.since
+description: Fast path for converting Temporal.PlainDateTime to Temporal.PlainDate by reading internal slots
+info: |
+ sec-temporal.plaindate.prototype.since step 3:
+ 3. Set _other_ to ? ToTemporalDate(_other_).
+ sec-temporal-totemporaldate step 2.b:
+ b. If _item_ has an [[InitializedTemporalDateTime]] internal slot, then
+ i. Return ! CreateTemporalDate(_item_.[[ISOYear]], _item_.[[ISOMonth]], _item_.[[ISODay]], _item_.[[Calendar]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkPlainDateTimeConversionFastPath((datetime) => {
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ const result = date.since(datetime);
+ assert.sameValue(result.total({ unit: "nanoseconds" }), 0, "time part dropped");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-propertybag-calendar-case-insensitive.js
new file mode 100644
index 0000000000..f625f82cff
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-propertybag-calendar-case-insensitive.js
@@ -0,0 +1,20 @@
+// |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.plaindate.prototype.since
+description: The calendar name is case-insensitive
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(1976, 11, 18);
+
+const calendar = "IsO8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.since(arg);
+TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-propertybag-calendar-leap-second.js
new file mode 100644
index 0000000000..b028699f45
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-propertybag-calendar-leap-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.plaindate.prototype.since
+description: Leap second is a valid ISO string for a calendar in a property bag
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(1976, 11, 18);
+
+const calendar = "2016-12-31T23:59:60";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.since(arg);
+TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "leap second is a valid ISO string for calendar"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-propertybag-calendar-number.js
new file mode 100644
index 0000000000..ef118b29ec
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-propertybag-calendar-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.plaindate.prototype.since
+description: A number as calendar in a property bag is not accepted
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(1976, 11, 18);
+
+const numbers = [
+ 1,
+ 19970327,
+ -19970327,
+ 1234567890,
+];
+for (const calendar of numbers) {
+ const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+ assert.throws(
+ TypeError,
+ () => instance.since(arg),
+ "Numbers cannot be used as a calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-propertybag-calendar-string.js
new file mode 100644
index 0000000000..78077d1cb5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-propertybag-calendar-string.js
@@ -0,0 +1,20 @@
+// |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.plaindate.prototype.since
+description: A calendar ID is valid input for Calendar
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(1976, 11, 18);
+
+const calendar = "iso8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.since(arg);
+TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, `Calendar created from string "${calendar}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-propertybag-calendar-wrong-type.js
new file mode 100644
index 0000000000..4f7bc89d08
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-propertybag-calendar-wrong-type.js
@@ -0,0 +1,46 @@
+// |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.plaindate.prototype.since
+description: >
+ Appropriate error thrown when a calendar property from a property bag cannot
+ be converted to a calendar object or string
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [calendar, description] of primitiveTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ typeof calendar === 'string' ? RangeError : TypeError,
+ () => instance.since(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+ [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields()
+];
+
+for (const [calendar, description] of typeErrorTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ 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/PlainDate/prototype/since/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-propertybag-calendar-year-zero.js
new file mode 100644
index 0000000000..13b4a25387
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-propertybag-calendar-year-zero.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.plaindate.prototype.since
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T17:45",
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+01:00",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+const instance = new Temporal.PlainDate(2000, 5, 2);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.since(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-proto-in-calendar-fields.js
new file mode 100644
index 0000000000..21dd6f91da
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-proto-in-calendar-fields.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.plaindate.prototype.since
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+assert.throws(RangeError, () => instance.since(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..c24c587a15
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-calendar-annotation.js
@@ -0,0 +1,34 @@
+// |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.plaindate.prototype.since
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2000-05-02[u-ca=iso8601]", "without time or time zone"],
+ ["2000-05-02[UTC][u-ca=iso8601]", "with time zone and no time"],
+ ["2000-05-02T15:23[u-ca=iso8601]", "without time zone"],
+ ["2000-05-02T15:23[UTC][u-ca=iso8601]", "with time zone"],
+ ["2000-05-02T15:23[!u-ca=iso8601]", "with ! and no time zone"],
+ ["2000-05-02T15:23[UTC][!u-ca=iso8601]", "with ! and time zone"],
+ ["2000-05-02T15:23[u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+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/PlainDate/prototype/since/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..d3ff358725
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-critical-unknown-annotation.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.plaindate.prototype.since
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[!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.PlainDate(2000, 5, 2);
+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/PlainDate/prototype/since/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..577856f88a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-date-with-utc-offset.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.plaindate.prototype.since
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+const validStrings = [
+ "2000-05-02T00+00:00",
+ "2000-05-02T00+00:00[UTC]",
+ "2000-05-02T00+00:00[!UTC]",
+ "2000-05-02T00-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 PlainDate`
+ );
+}
+
+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 PlainDate`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-invalid.js
new file mode 100644
index 0000000000..9767b0ef63
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-invalid.js
@@ -0,0 +1,64 @@
+// |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.plaindate.prototype.since
+description: >
+ RangeError thrown if an invalid ISO string (or syntactically valid ISO string
+ that is not supported) is used as a PlainDate
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ // invalid ISO strings:
+ "",
+ "invalid iso8601",
+ "2020-01-00",
+ "2020-01-32",
+ "2020-02-30",
+ "2021-02-29",
+ "2020-00-01",
+ "2020-13-01",
+ "2020-01-01T",
+ "2020-01-01T25:00:00",
+ "2020-01-01T01:60:00",
+ "2020-01-01T01:60:61",
+ "2020-01-01junk",
+ "2020-01-01T00:00:00junk",
+ "2020-01-01T00:00:00+00:00junk",
+ "2020-01-01T00:00:00+00:00[UTC]junk",
+ "2020-01-01T00:00:00+00:00[UTC][u-ca=iso8601]junk",
+ "02020-01-01",
+ "2020-001-01",
+ "2020-01-001",
+ "2020-01-01T001",
+ "2020-01-01T01:001",
+ "2020-01-01T01:01:001",
+ // valid, but forms not supported in Temporal:
+ "2020-W01-1",
+ "2020-001",
+ "+0002020-01-01",
+ // valid, but this calendar must not exist:
+ "2020-01-01[u-ca=notexist]",
+ // may be valid in other contexts, but insufficient information for PlainDate:
+ "2020-01",
+ "+002020-01",
+ "01-01",
+ "2020-W01",
+ "P1Y",
+ "-P12Y",
+ // valid, but outside the supported range:
+ "-999999-01-01",
+ "+999999-01-01",
+];
+const instance = new Temporal.PlainDate(2000, 5, 2);
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => instance.since(arg),
+ `"${arg}" should not be a valid ISO string for a PlainDate`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..5bb7388900
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/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.plaindate.prototype.since
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[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.PlainDate(2000, 5, 2);
+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/PlainDate/prototype/since/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..9f87b4dc02
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-multiple-time-zone.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.plaindate.prototype.since
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[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.PlainDate(2000, 5, 2);
+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/PlainDate/prototype/since/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-time-separators.js
new file mode 100644
index 0000000000..f12c529db2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-time-separators.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.plaindate.prototype.since
+description: Time separator in string argument can vary
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2000-05-02T15:23", "uppercase T"],
+ ["2000-05-02t15:23", "lowercase T"],
+ ["2000-05-02 15:23", "space between date and time"],
+];
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+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/PlainDate/prototype/since/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..acf7c2972f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-time-zone-annotation.js
@@ -0,0 +1,39 @@
+// |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.plaindate.prototype.since
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2000-05-02[Asia/Kolkata]", "named, with no time"],
+ ["2000-05-02[!Europe/Vienna]", "named, with ! and no time"],
+ ["2000-05-02[+00:00]", "numeric, with no time"],
+ ["2000-05-02[!-02:30]", "numeric, with ! and no time"],
+ ["2000-05-02T15:23[America/Sao_Paulo]", "named, with no offset"],
+ ["2000-05-02T15:23[!Asia/Tokyo]", "named, with ! and no offset"],
+ ["2000-05-02T15:23[-02:30]", "numeric, with no offset"],
+ ["2000-05-02T15:23[!+00:00]", "numeric, with ! and no offset"],
+ ["2000-05-02T15:23+00:00[America/New_York]", "named, with offset"],
+ ["2000-05-02T15:23+00:00[!UTC]", "named, with offset and !"],
+ ["2000-05-02T15:23+00:00[+01:00]", "numeric, with offset"],
+ ["2000-05-02T15:23+00:00[!-08:00]", "numeric, with offset and !"],
+];
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+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/PlainDate/prototype/since/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..ba543410e2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-unknown-annotation.js
@@ -0,0 +1,33 @@
+// |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.plaindate.prototype.since
+description: Various forms of unknown annotation
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2000-05-02[foo=bar]", "without time"],
+ ["2000-05-02T15:23[foo=bar]", "alone"],
+ ["2000-05-02T15:23[UTC][foo=bar]", "with time zone"],
+ ["2000-05-02T15:23[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["2000-05-02T15:23[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["2000-05-02T15:23[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+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/PlainDate/prototype/since/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..1d6700d894
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-with-utc-designator.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.plaindate.prototype.since
+description: RangeError thrown if a string with UTC designator is used as a PlainDate
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "2019-10-01T09:00:00Z",
+ "2019-10-01T09:00:00Z[UTC]",
+];
+const instance = new Temporal.PlainDate(2000, 5, 2);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.since(arg),
+ "String with UTC designator should not be valid as a PlainDate"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-wrong-type.js
new file mode 100644
index 0000000000..8fcddedfba
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/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.plaindate.prototype.since
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainDate
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+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.PlainDate, "Temporal.PlainDate, object"],
+ [Temporal.PlainDate.prototype, "Temporal.PlainDate.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/PlainDate/prototype/since/argument-zoneddatetime-convert.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-zoneddatetime-convert.js
new file mode 100644
index 0000000000..5f1ee07422
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-zoneddatetime-convert.js
@@ -0,0 +1,22 @@
+// |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.plaindate.prototype.since
+description: An exception from TimeZone#getOffsetNanosecondsFor() is propagated.
+features: [Temporal]
+---*/
+
+class TZ extends Temporal.TimeZone {
+ constructor() { super("UTC") }
+ getOffsetNanosecondsFor() { throw new Test262Error() }
+}
+
+const tz = new TZ();
+const arg = new Temporal.ZonedDateTime(0n, tz);
+const instance = new Temporal.PlainDate(1976, 11, 18);
+
+assert.throws(Test262Error, () => instance.since(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-zoneddatetime-slots.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-zoneddatetime-slots.js
new file mode 100644
index 0000000000..1270b6d99f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-zoneddatetime-slots.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.plaindate.prototype.since
+description: Getters are not called when converting a ZonedDateTime to a PlainDate.
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.ZonedDateTime.prototype);
+const getters = ["year", "month", "monthCode", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond", "calendar"];
+
+for (const property of getters) {
+ Object.defineProperty(Temporal.ZonedDateTime.prototype, property, {
+ get() {
+ actual.push(`get ${property}`);
+ const value = prototypeDescrs[property].get.call(this);
+ return {
+ toString() {
+ actual.push(`toString ${property}`);
+ return value.toString();
+ },
+ valueOf() {
+ actual.push(`valueOf ${property}`);
+ return value;
+ },
+ };
+ },
+ });
+}
+
+const arg = new Temporal.ZonedDateTime(0n, "UTC");
+const instance = new Temporal.PlainDate(1976, 11, 18);
+instance.since(arg);
+assert.compareArray(actual, []);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..8c06d06e06
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/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.plaindate.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 date = new Temporal.PlainDate(2000, 5, 2);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => date.since(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..2fd89d1bc2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/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.plaindate.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 date = new Temporal.PlainDate(2000, 5, 2);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => date.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/PlainDate/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..41e03c66bf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/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.plaindate.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 date = new Temporal.PlainDate(2000, 5, 2);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => date.since(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..46d51c9016
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/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.plaindate.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 date = new Temporal.PlainDate(2000, 5, 2);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => date.since(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/basic.js
new file mode 100644
index 0000000000..0f7e7221d4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/basic.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.plaindate.prototype.since
+description: Basic tests for since().
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainDate = new Temporal.PlainDate(1969, 7, 24);
+const plainDate2 = Temporal.PlainDate.from({ year: 1969, month: 10, day: 5 });
+TemporalHelpers.assertDuration(plainDate2.since(plainDate), 0, 0, 0, /* days = */ 73, 0, 0, 0, 0, 0, 0, "same year");
+
+const earlier = new Temporal.PlainDate(1969, 7, 24);
+const later = new Temporal.PlainDate(1996, 3, 3);
+const duration = later.since(earlier);
+TemporalHelpers.assertDuration(duration, 0, 0, 0, /* days = */ 9719, 0, 0, 0, 0, 0, 0, "different year");
+
+TemporalHelpers.assertDuration(plainDate.since({ year: 2019, month: 7, day: 24 }), 0, 0, 0, /* days = */ -18262, 0, 0, 0, 0, 0, 0, "option bag");
+TemporalHelpers.assertDuration(plainDate.since("2019-07-24"), 0, 0, 0, /* days = */ -18262, 0, 0, 0, 0, 0, 0, "string");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/branding.js
new file mode 100644
index 0000000000..b6b33a0ed1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/branding.js
@@ -0,0 +1,27 @@
+// |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.plaindate.prototype.since
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const since = Temporal.PlainDate.prototype.since;
+
+assert.sameValue(typeof since, "function");
+
+const args = [new Temporal.PlainDate(2022, 6, 22)];
+
+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.PlainDate, args), "Temporal.PlainDate");
+assert.throws(TypeError, () => since.apply(Temporal.PlainDate.prototype, args), "Temporal.PlainDate.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..48abc72656
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plaindate.prototype.since
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dateUntilOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dateUntil");
+Object.defineProperty(Temporal.Calendar.prototype, "dateUntil", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("dateUntil should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDate(2000, 5, 2, "iso8601");
+instance.since(new Temporal.PlainDate(1999, 4, 1));
+
+Object.defineProperty(Temporal.Calendar.prototype, "dateUntil", dateUntilOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/builtin.js
new file mode 100644
index 0000000000..2a0e1056df
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/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.plaindate.prototype.since
+description: >
+ Tests that Temporal.PlainDate.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.PlainDate.prototype.since),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDate.prototype.since),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDate.prototype.since),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDate.prototype.since.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-dateadd-called-with-plaindate-instance.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-dateadd-called-with-plaindate-instance.js
new file mode 100644
index 0000000000..ccbfe65104
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-dateadd-called-with-plaindate-instance.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.plaindate.prototype.since
+description: >
+ relativeTo parameters that are not ZonedDateTime or undefined, are always
+ converted to PlainDate for observable calendar calls
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarDateAddPlainDateInstance();
+const instance = new Temporal.PlainDate(1970, 1, 1, calendar);
+calendar.specificPlainDate = instance;
+instance.since(new Temporal.PlainDate(2000, 5, 2, calendar), { smallestUnit: "month" });
+assert(calendar.dateAddCallCount > 0, "assertions in calendar.dateAdd() should have been tested");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-datefromfields-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-datefromfields-called-with-options-undefined.js
new file mode 100644
index 0000000000..72eb3ada42
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-datefromfields-called-with-options-undefined.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.plaindate.prototype.since
+description: >
+ Calendar.dateFromFields method is called with undefined as the options value
+ when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
+const instance = new Temporal.PlainDate(2000, 5, 2, calendar);
+instance.since({ year: 2000, month: 5, day: 3, calendar });
+assert.sameValue(calendar.dateFromFieldsCallCount, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-dateuntil-called-with-null-prototype-options.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-dateuntil-called-with-null-prototype-options.js
new file mode 100644
index 0000000000..68079f9d32
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-dateuntil-called-with-null-prototype-options.js
@@ -0,0 +1,20 @@
+// |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.plaindate.prototype.since
+description: >
+ Calendar.dateUntil method is called with a null-prototype object as the
+ options value when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckOptionsPrototypePollution();
+const instance = new Temporal.PlainDate(2000, 5, 2, calendar);
+const argument = new Temporal.PlainDate(2022, 6, 14, calendar);
+instance.since(argument, { largestUnit: "months" });
+assert.sameValue(calendar.dateUntilCallCount, 1, "dateUntil should have been called on the calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-dateuntil-called-with-singular-largestunit.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-dateuntil-called-with-singular-largestunit.js
new file mode 100644
index 0000000000..96382a3764
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-dateuntil-called-with-singular-largestunit.js
@@ -0,0 +1,30 @@
+// |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.plaindate.prototype.since
+description: The options object passed to calendar.dateUntil has a largestUnit property with its value in the singular form
+info: |
+ sec-temporal.plaindate.prototype.since steps 13–14:
+ 13. Let _untilOptions_ be ? MergeLargestUnitOption(_options_, _largestUnit_).
+ 14. Let _result_ be ? CalendarDateUntil(_temporalDate_.[[Calendar]], _other_, _temporalDate_, _untilOptions_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkCalendarDateUntilLargestUnitSingular(
+ (calendar, largestUnit) => {
+ const earlier = new Temporal.PlainDate(2000, 5, 2, calendar);
+ const later = new Temporal.PlainDate(2001, 6, 3, calendar);
+ later.since(earlier, { largestUnit });
+ },
+ {
+ years: ["year"],
+ months: ["month"],
+ weeks: ["week"],
+ days: []
+ }
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-fields-iterable.js
new file mode 100644
index 0000000000..73db3c85dd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-fields-iterable.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.plaindate.prototype.since
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plaindate.prototype.since step 3:
+ 3. Set _other_ to ? ToTemporalDate(_other_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const date = new Temporal.PlainDate(2000, 5, 2, calendar1);
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+date.since({ year: 2005, month: 6, day: 2, calendar: calendar2 });
+
+assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-id-match.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-id-match.js
new file mode 100644
index 0000000000..9191a3635f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-id-match.js
@@ -0,0 +1,33 @@
+// |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.plaindate.prototype.since
+description: Calculation is performed if calendars' toString results match
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+class Calendar1 extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ toString() {
+ return "A";
+ }
+}
+class Calendar2 extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ toString() {
+ return "A";
+ }
+}
+
+const plainDate1 = new Temporal.PlainDate(2000, 1, 1, new Calendar1());
+const plainDate2 = new Temporal.PlainDate(2000, 1, 2, new Calendar2());
+TemporalHelpers.assertDuration(plainDate2.since(plainDate1), 0, 0, 0, /* days = */ 1, 0, 0, 0, 0, 0, 0);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-invalid-return.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-invalid-return.js
new file mode 100644
index 0000000000..f008696b77
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-invalid-return.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.plaindate.prototype.since
+description: Throw when the returned value from the calendar's dateUntil method is not a Duration.
+features: [Temporal]
+---*/
+
+class CustomCalendar extends Temporal.Calendar {
+ constructor(value) {
+ super("iso8601");
+ this.value = value;
+ }
+ dateUntil() {
+ return this.value;
+ }
+}
+
+const tests = [
+ [undefined],
+ [null, "null"],
+ [true],
+ ["2000-05"],
+ [Symbol()],
+ [200005],
+ [200005n],
+ [{}, "plain object"],
+ [() => {}, "lambda"],
+ [Temporal.Duration, "Temporal.Duration"],
+ [Temporal.Duration.prototype, "Temporal.Duration.prototype"],
+];
+for (const [test, description = typeof test] of tests) {
+ const plainDate = new Temporal.PlainDate(2000, 5, 2, new CustomCalendar(test));
+ assert.throws(
+ TypeError,
+ () => plainDate.since("2022-06-20", { largestUnit: "years" }),
+ `Expected error with ${description}`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-mismatch.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-mismatch.js
new file mode 100644
index 0000000000..1537dda6ee
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-mismatch.js
@@ -0,0 +1,62 @@
+// |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.plaindate.prototype.since
+description: RangeError thrown if calendars' id properties do not match
+features: [Temporal]
+---*/
+
+const calendar1 = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "A",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+const calendar2 = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "B",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+
+const plainDate1 = new Temporal.PlainDate(2000, 1, 1, calendar1);
+const plainDate2 = new Temporal.PlainDate(2000, 1, 1, calendar2);
+assert.throws(RangeError, () => plainDate1.since(plainDate2));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-temporal-object.js
new file mode 100644
index 0000000000..c99733f2d7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/calendar-temporal-object.js
@@ -0,0 +1,29 @@
+// |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.plaindate.prototype.since
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plaindate.prototype.since step 3:
+ 3. Set _other_ to ? ToTemporalDate(_other_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const date = new Temporal.PlainDate(2000, 5, 2, temporalObject);
+ date.since({ year: 2005, month: 6, day: 2, calendar: temporalObject });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/custom.js
new file mode 100644
index 0000000000..7aa8c4de2e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/custom.js
@@ -0,0 +1,35 @@
+// |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.plaindate.prototype.since
+description: Basic tests with custom calendar
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const result = new Temporal.Duration(1, 3, 5, 7, 9);
+const options = { largestUnit: "years" };
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ dateUntil(...args) {
+ ++calls;
+ assert.sameValue(args.length, 3, "Three arguments");
+ assert.sameValue(args[0], plainDate, "First argument");
+ assert.sameValue(args[1], other, "Second argument");
+ assert.sameValue(args[2].largestUnit, "year", "Third argument: largestUnit");
+ return result;
+ }
+}
+const calendar = new CustomCalendar();
+const plainDate = new Temporal.PlainDate(1976, 11, 18, calendar);
+const other = new Temporal.PlainDate(2022, 6, 20, calendar);
+TemporalHelpers.assertDuration(plainDate.since(other, options),
+ -1, -3, -5, -7, 0, 0, 0, 0, 0, 0, "result");
+assert.sameValue(calls, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/days-in-month.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/days-in-month.js
new file mode 100644
index 0000000000..4bce5ba5d5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/days-in-month.js
@@ -0,0 +1,22 @@
+// |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.plaindate.prototype.since
+description: since() should take length of month into account.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainDate1 = Temporal.PlainDate.from("2019-01-01");
+const plainDate2 = Temporal.PlainDate.from("2019-02-01");
+const plainDate3 = Temporal.PlainDate.from("2019-03-01");
+TemporalHelpers.assertDuration(plainDate2.since(plainDate1), 0, 0, 0, /* days = */ 31, 0, 0, 0, 0, 0, 0, "January 2019");
+TemporalHelpers.assertDuration(plainDate3.since(plainDate2), 0, 0, 0, /* days = */ 28, 0, 0, 0, 0, 0, 0, "February 2019");
+
+const plainDate4 = Temporal.PlainDate.from("2020-02-01");
+const plainDate5 = Temporal.PlainDate.from("2020-03-01");
+TemporalHelpers.assertDuration(plainDate5.since(plainDate4), 0, 0, 0, /* days = */ 29, 0, 0, 0, 0, 0, 0, "February 2020");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/days-in-year.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/days-in-year.js
new file mode 100644
index 0000000000..852c3062f6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/days-in-year.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.plaindate.prototype.since
+description: since() should take length of year into account.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainDate1 = Temporal.PlainDate.from("2019-01-01");
+const plainDate2 = Temporal.PlainDate.from("2020-01-01");
+const plainDate3 = Temporal.PlainDate.from("2021-01-01");
+TemporalHelpers.assertDuration(plainDate2.since(plainDate1), 0, 0, 0, /* days = */ 365, 0, 0, 0, 0, 0, 0, "From January 2019");
+TemporalHelpers.assertDuration(plainDate3.since(plainDate2), 0, 0, 0, /* days = */ 366, 0, 0, 0, 0, 0, 0, "From January 2020");
+
+const plainDate4 = Temporal.PlainDate.from("2019-06-01");
+const plainDate5 = Temporal.PlainDate.from("2020-06-01");
+const plainDate6 = Temporal.PlainDate.from("2021-06-01");
+TemporalHelpers.assertDuration(plainDate5.since(plainDate4), 0, 0, 0, /* days = */ 366, 0, 0, 0, 0, 0, 0, "From June 2019");
+TemporalHelpers.assertDuration(plainDate6.since(plainDate5), 0, 0, 0, /* days = */ 365, 0, 0, 0, 0, 0, 0, "From June 2020");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..33d5c6c536
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/infinity-throws-rangeerror.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.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plaindate.prototype.since
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+const base = { year: 2000, month: 5, day: 2 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.since({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.since({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/largestunit-default.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/largestunit-default.js
new file mode 100644
index 0000000000..347254e0ab
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/largestunit-default.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.plaindate.prototype.since
+description: Default value for largestUnit option is days
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const feb20 = Temporal.PlainDate.from("2020-02-01");
+const feb21 = Temporal.PlainDate.from("2021-02-01");
+TemporalHelpers.assertDuration(feb21.since(feb20), 0, 0, 0, /* days = */ 366, 0, 0, 0, 0, 0, 0, "no options");
+TemporalHelpers.assertDuration(feb21.since(feb20, undefined), 0, 0, 0, /* days = */ 366, 0, 0, 0, 0, 0, 0, "undefined options");
+TemporalHelpers.assertDuration(feb21.since(feb20, {}), 0, 0, 0, /* days = */ 366, 0, 0, 0, 0, 0, 0, "no largestUnit");
+TemporalHelpers.assertDuration(feb21.since(feb20, { largestUnit: undefined }), 0, 0, 0, /* days = */ 366, 0, 0, 0, 0, 0, 0, "undefined largestUnit");
+TemporalHelpers.assertDuration(feb21.since(feb20, { largestUnit: "days" }), 0, 0, 0, /* days = */ 366, 0, 0, 0, 0, 0, 0, "days");
+TemporalHelpers.assertDuration(feb21.since(feb20, { largestUnit: "auto" }), 0, 0, 0, /* days = */ 366, 0, 0, 0, 0, 0, 0, "auto");
+TemporalHelpers.assertDuration(feb21.since(feb20, () => {}), 0, 0, 0, /* days = */ 366, 0, 0, 0, 0, 0, 0, "no largestUnit (function)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/largestunit-higher-units.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/largestunit-higher-units.js
new file mode 100644
index 0000000000..89fbfe8c6f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/largestunit-higher-units.js
@@ -0,0 +1,29 @@
+// |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.plaindate.prototype.since
+description: Tests calculations with higher largestUnit than the default of 'days'
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(1969, 7, 24);
+const later = Temporal.PlainDate.from({ year: 2019, month: 7, day: 24 });
+const duration = later.since(date, { largestUnit: "years" });
+TemporalHelpers.assertDuration(duration, /* years = */ 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, "crossing epoch");
+
+const feb20 = Temporal.PlainDate.from("2020-02-01");
+const feb21 = Temporal.PlainDate.from("2021-02-01");
+TemporalHelpers.assertDuration(feb21.since(feb20, { largestUnit: "years" }), /* years = */ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "start of February, years");
+TemporalHelpers.assertDuration(feb21.since(feb20, { largestUnit: "months" }), 0, /* months = */ 12, 0, 0, 0, 0, 0, 0, 0, 0, "start of February, months");
+TemporalHelpers.assertDuration(feb21.since(feb20, { largestUnit: "weeks" }), 0, 0, /* weeks = */ 52, /* days = */ 2, 0, 0, 0, 0, 0, 0, "start of February, weeks");
+
+const lastFeb20 = Temporal.PlainDate.from("2020-02-29");
+const lastFeb21 = Temporal.PlainDate.from("2021-02-28");
+TemporalHelpers.assertDuration(lastFeb21.since(lastFeb20, { largestUnit: "years" }), 0, /* months = */ 11, 0, /* days = */ 28, 0, 0, 0, 0, 0, 0, "end of February, years");
+TemporalHelpers.assertDuration(lastFeb21.since(lastFeb20, { largestUnit: "months" }), 0, /* months = */ 11, 0, /* days = */ 28, 0, 0, 0, 0, 0, 0, "end of February, months");
+TemporalHelpers.assertDuration(lastFeb21.since(lastFeb20, { largestUnit: "weeks" }), 0, 0, /* weeks = */ 52, /* days = */ 1, 0, 0, 0, 0, 0, 0, "end of February, weeks");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/largestunit-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/largestunit-invalid-string.js
new file mode 100644
index 0000000000..f62f2effe5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/largestunit-invalid-string.js
@@ -0,0 +1,41 @@
+// |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.plaindate.prototype.since
+description: RangeError thrown when largestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2001, 6, 3);
+const badValues = [
+ "era",
+ "eraYear",
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+ "month\0",
+ "YEAR",
+ "eras",
+ "eraYears",
+ "hours",
+ "minutes",
+ "seconds",
+ "milliseconds",
+ "microseconds",
+ "nanoseconds",
+ "months\0",
+ "YEARS",
+ "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/PlainDate/prototype/since/largestunit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/largestunit-plurals-accepted.js
new file mode 100644
index 0000000000..cd93f33fd9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/largestunit-plurals-accepted.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.plaindate.prototype.since
+description: Plural units are accepted as well for the largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2001, 6, 12);
+const validUnits = [
+ "year",
+ "month",
+ "week",
+ "day",
+];
+TemporalHelpers.checkPluralUnitsAccepted((largestUnit) => later.since(earlier, { largestUnit }), validUnits);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/largestunit-smallestunit-mismatch.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/largestunit-smallestunit-mismatch.js
new file mode 100644
index 0000000000..f7ab10f829
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/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.plaindate.prototype.since
+description: RangeError thrown when smallestUnit is larger than largestUnit
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2001, 6, 3);
+const units = ["years", "months", "weeks", "days"];
+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/PlainDate/prototype/since/largestunit-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/largestunit-undefined.js
new file mode 100644
index 0000000000..f12b94d2d9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/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.plaindate.prototype.since
+description: Fallback value for largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2001, 6, 3);
+
+const explicit = later.since(earlier, { largestUnit: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 397, 0, 0, 0, 0, 0, 0, "default largestUnit is day");
+const implicit = later.since(earlier, {});
+TemporalHelpers.assertDuration(implicit, 0, 0, 0, 397, 0, 0, 0, 0, 0, 0, "default largestUnit is day");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/largestunit-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/largestunit-wrong-type.js
new file mode 100644
index 0000000000..6fc693637d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/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.plaindate.prototype.since
+description: Type conversions for largestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2001, 6, 3);
+TemporalHelpers.checkStringOptionWrongType("largestUnit", "year",
+ (largestUnit) => later.since(earlier, { largestUnit }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/largestunit.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/largestunit.js
new file mode 100644
index 0000000000..1f78521659
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/largestunit.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.plaindate.prototype.since
+description: Specify behavior of PlainDate.since when largest specified unit is years or months.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+const pd1 = new Temporal.PlainDate(2020, 11, 1);
+const pd2 = new Temporal.PlainDate(2021, 11, 30);
+TemporalHelpers.assertDuration(pd2.since(pd1), 0, 0, 0, 394, 0, 0, 0, 0, 0, 0, 'does not include higher units than necessary (largest unit unspecified)');
+TemporalHelpers.assertDuration(pd2.since(pd1, { largestUnit: 'months' }), 0, 12, 0, 29, 0, 0, 0, 0, 0, 0, 'does not include higher units than necessary (largest unit is months)');
+TemporalHelpers.assertDuration(pd2.since(pd1, { largestUnit: 'years' }), 1, 0, 0, 29, 0, 0, 0, 0, 0, 0, 'does not include higher units than necessary (largest unit is years)');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/length.js
new file mode 100644
index 0000000000..32fd4e2a83
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/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.plaindate.prototype.since
+description: Temporal.PlainDate.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.PlainDate.prototype.since, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/name.js
new file mode 100644
index 0000000000..70e86fb9e9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/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.plaindate.prototype.since
+description: Temporal.PlainDate.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.PlainDate.prototype.since, "name", {
+ value: "since",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/not-a-constructor.js
new file mode 100644
index 0000000000..addff2bce7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/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.plaindate.prototype.since
+description: >
+ Temporal.PlainDate.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.PlainDate.prototype.since();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDate.prototype.since), false,
+ "isConstructor(Temporal.PlainDate.prototype.since)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/options-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/options-object.js
new file mode 100644
index 0000000000..e3cfe462a8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/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.plaindate.prototype.since
+description: Empty or a function object may be used as options
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+const result1 = instance.since(new Temporal.PlainDate(1976, 11, 18), {});
+TemporalHelpers.assertDuration(
+ result1, 0, 0, 0, 8566, 0, 0, 0, 0, 0, 0,
+ "options may be an empty plain object"
+);
+
+const result2 = instance.since(new Temporal.PlainDate(1976, 11, 18), () => {});
+TemporalHelpers.assertDuration(
+ result2, 0, 0, 0, 8566, 0, 0, 0, 0, 0, 0,
+ "options may be a function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/options-wrong-type.js
new file mode 100644
index 0000000000..6c95b9436c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/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.plaindate.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.PlainDate(2000, 5, 2);
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.since(new Temporal.PlainDate(1976, 11, 18), value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/order-of-operations.js
new file mode 100644
index 0000000000..1214a1cd49
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/order-of-operations.js
@@ -0,0 +1,213 @@
+// |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.plaindate.prototype.since
+description: Properties on objects passed to since() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ // ToTemporalDate
+ "get other.calendar",
+ "has other.calendar.dateAdd",
+ "has other.calendar.dateFromFields",
+ "has other.calendar.dateUntil",
+ "has other.calendar.day",
+ "has other.calendar.dayOfWeek",
+ "has other.calendar.dayOfYear",
+ "has other.calendar.daysInMonth",
+ "has other.calendar.daysInWeek",
+ "has other.calendar.daysInYear",
+ "has other.calendar.fields",
+ "has other.calendar.id",
+ "has other.calendar.inLeapYear",
+ "has other.calendar.mergeFields",
+ "has other.calendar.month",
+ "has other.calendar.monthCode",
+ "has other.calendar.monthDayFromFields",
+ "has other.calendar.monthsInYear",
+ "has other.calendar.weekOfYear",
+ "has other.calendar.year",
+ "has other.calendar.yearMonthFromFields",
+ "has other.calendar.yearOfWeek",
+ "get other.calendar.dateFromFields",
+ "get other.calendar.fields",
+ "call other.calendar.fields",
+ "get other.day",
+ "get other.day.valueOf",
+ "call other.day.valueOf",
+ "get other.month",
+ "get other.month.valueOf",
+ "call other.month.valueOf",
+ "get other.monthCode",
+ "get other.monthCode.toString",
+ "call other.monthCode.toString",
+ "get other.year",
+ "get other.year.valueOf",
+ "call other.year.valueOf",
+ "call other.calendar.dateFromFields",
+ // CalendarEquals
+ "get this.calendar.id",
+ "get other.calendar.id",
+ // 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 ownCalendar = TemporalHelpers.calendarObserver(actual, "this.calendar");
+const instance = new Temporal.PlainDate(2000, 5, 2, ownCalendar);
+
+const otherDatePropertyBag = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2001,
+ month: 6,
+ monthCode: "M06",
+ day: 2,
+ calendar: TemporalHelpers.calendarObserver(actual, "other.calendar"),
+}, "other");
+
+function createOptionsObserver({ smallestUnit = "days", largestUnit = "auto", roundingMode = "halfExpand", roundingIncrement = 1 } = {}) {
+ return TemporalHelpers.propertyBagObserver(actual, {
+ // order is significant, due to iterating through properties in order to
+ // copy them to an internal null-prototype object:
+ roundingIncrement,
+ roundingMode,
+ largestUnit,
+ smallestUnit,
+ additional: "property",
+ }, "options");
+}
+
+// clear any observable things that happened while constructing the objects
+actual.splice(0);
+
+// basic order of observable operations with calendar call, without rounding:
+instance.since(otherDatePropertyBag, createOptionsObserver({ largestUnit: "years" }));
+assert.compareArray(actual, expected.concat([
+ // lookup
+ "get this.calendar.dateAdd",
+ "get this.calendar.dateUntil",
+ // CalendarDateUntil
+ "call this.calendar.dateUntil",
+]), "order of operations");
+actual.splice(0); // clear
+
+// short-circuit for identical objects:
+const identicalPropertyBag = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2000,
+ month: 5,
+ monthCode: "M05",
+ day: 2,
+ calendar: TemporalHelpers.calendarObserver(actual, "other.calendar"),
+}, "other");
+
+instance.since(identicalPropertyBag, createOptionsObserver());
+assert.compareArray(actual, expected, "order of operations with identical dates");
+actual.splice(0); // clear
+
+// code path through RoundDuration that rounds to the nearest year:
+const expectedOpsForYearRounding = expected.concat([
+ // lookup
+ "get this.calendar.dateAdd",
+ "get this.calendar.dateUntil",
+ // CalendarDateUntil
+ "call this.calendar.dateUntil",
+ // RoundDuration
+ "call this.calendar.dateAdd", // 12.d
+ "call this.calendar.dateAdd", // 12.f
+ "call this.calendar.dateUntil", // 12.n
+ "call this.calendar.dateAdd", // 12.x MoveRelativeDate
+ // (12.r not called because other units can't add up to >1 year at this point)
+ // BalanceDateDurationRelative
+ "call this.calendar.dateAdd", // 9.c
+ "call this.calendar.dateUntil" // 9.d
+]);
+instance.since(otherDatePropertyBag, createOptionsObserver({ smallestUnit: "years" }));
+assert.compareArray(actual, expectedOpsForYearRounding, "order of operations with smallestUnit = years");
+actual.splice(0); // clear
+
+// code path through RoundDuration that rounds to the nearest year and skips a DateUntil call:
+const otherDatePropertyBagSameMonth = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2001,
+ month: 5,
+ monthCode: "M05",
+ day: 2,
+ calendar: TemporalHelpers.calendarObserver(actual, "other.calendar"),
+}, "other");
+const expectedOpsForYearRoundingSameMonth = expected.concat([
+ // lookup
+ "get this.calendar.dateAdd",
+ "get this.calendar.dateUntil",
+ // CalendarDateUntil
+ "call this.calendar.dateUntil",
+ // RoundDuration
+ "call this.calendar.dateAdd", // 12.d
+ "call this.calendar.dateAdd", // 12.f
+ "call this.calendar.dateAdd", // 12.x MoveRelativeDate
+ // (12.n not called because months and weeks == 0)
+ // BalanceDateDurationRelative
+ "call this.calendar.dateAdd", // 9.c
+ "call this.calendar.dateUntil" // 9.d
+]);
+instance.since(otherDatePropertyBagSameMonth, createOptionsObserver({ smallestUnit: "years" }));
+assert.compareArray(actual, expectedOpsForYearRoundingSameMonth, "order of operations with smallestUnit = years and no excess months/weeks");
+actual.splice(0); // clear
+
+// code path through RoundDuration that rounds to the nearest month:
+const expectedOpsForMonthRounding = expected.concat([
+ // lookup
+ "get this.calendar.dateAdd",
+ "get this.calendar.dateUntil",
+ // CalendarDateUntil
+ "call this.calendar.dateUntil",
+ // RoundDuration
+ "call this.calendar.dateAdd", // 13.c
+ "call this.calendar.dateAdd", // 13.e
+ "call this.calendar.dateAdd", // 13.w MoveRelativeDate
+ // BalanceDateDurationRelative
+ "call this.calendar.dateAdd", // 10.d
+ "call this.calendar.dateUntil" // 10.e
+]);
+instance.since(otherDatePropertyBag, createOptionsObserver({ smallestUnit: "months" }));
+assert.compareArray(actual, expectedOpsForMonthRounding, "order of operations with smallestUnit = months");
+actual.splice(0); // clear
+
+// code path through RoundDuration that rounds to the nearest week:
+const expectedOpsForWeekRounding = expected.concat([
+ // lookup
+ "get this.calendar.dateAdd",
+ "get this.calendar.dateUntil",
+ // CalendarDateUntil
+ "call this.calendar.dateUntil",
+ // RoundDuration
+ "call this.calendar.dateUntil", // 14.f
+ "call this.calendar.dateAdd", // 14.p MoveRelativeDate
+ // BalanceDateDurationRelative
+ "call this.calendar.dateAdd", // 16
+ "call this.calendar.dateUntil" // 17
+]);
+instance.since(otherDatePropertyBag, createOptionsObserver({ smallestUnit: "weeks" }));
+assert.compareArray(actual, expectedOpsForWeekRounding, "order of operations with smallestUnit = weeks");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/prop-desc.js
new file mode 100644
index 0000000000..cbebd49dad
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/prop-desc.js
@@ -0,0 +1,24 @@
+// |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.plaindate.prototype.since
+description: The "since" property of Temporal.PlainDate.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDate.prototype.since,
+ "function",
+ "`typeof PlainDate.prototype.since` is `function`"
+);
+
+verifyProperty(Temporal.PlainDate.prototype, "since", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/round-cross-unit-boundary.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/round-cross-unit-boundary.js
new file mode 100644
index 0000000000..48f8b822c4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/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.plaindate.prototype.since
+description: Rounding can cross unit boundaries up to largestUnit
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2022, 1, 1);
+const later = new Temporal.PlainDate(2023, 12, 25);
+const duration = earlier.since(later, { largestUnit: "years", smallestUnit: "months", roundingMode: "expand" });
+TemporalHelpers.assertDuration(duration, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, "-1 year -11 months balances to -2 years");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/rounding-relative.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/rounding-relative.js
new file mode 100644
index 0000000000..fe26ed0e44
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/rounding-relative.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.plaindate.prototype.since
+description: Should round relative to the receiver.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const date1 = Temporal.PlainDate.from("2019-01-01");
+const date2 = Temporal.PlainDate.from("2019-02-15");
+
+TemporalHelpers.assertDuration(
+ date2.since(date1, { smallestUnit: "months", roundingMode: "halfExpand" }),
+ 0, /* months = */ 1, 0, 0, 0, 0, 0, 0, 0, 0);
+TemporalHelpers.assertDuration(
+ date1.since(date2, { smallestUnit: "months", roundingMode: "halfExpand" }),
+ 0, /* months = */ -2, 0, 0, 0, 0, 0, 0, 0, 0);
+
+const cases = [
+ ["2019-03-01", "2019-01-29", 1, 3],
+ ["2019-01-29", "2019-03-01", -1, -1],
+ ["2019-03-29", "2019-01-30", 1, 29],
+ ["2019-01-30", "2019-03-29", -1, -29],
+ ["2019-03-30", "2019-01-31", 1, 28],
+ ["2019-01-31", "2019-03-30", -1, -30],
+ ["2019-03-31", "2019-01-31", 2, 0],
+ ["2019-01-31", "2019-03-31", -2, 0]
+];
+for (const [end, start, months, days] of cases) {
+ const result = Temporal.PlainDate.from(end).since(start, { largestUnit: "months" });
+ TemporalHelpers.assertDuration(result, 0, months, 0, days, 0, 0, 0, 0, 0, 0, `${end} - ${start}`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/rounding-zero-year-month-week-length.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/rounding-zero-year-month-week-length.js
new file mode 100644
index 0000000000..0815cd30a8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/rounding-zero-year-month-week-length.js
@@ -0,0 +1,34 @@
+// |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.plaindate.prototype.since
+description: >
+ A malicious calendar resulting in a year, month, or week length of zero is
+ handled correctly
+info: |
+ RoundDuration
+ 10.z. If _oneYearDays_ = 0, throw a *RangeError* exception.
+ ...
+ 11.z. If _oneMonthDays_ = 0, throw a *RangeError* exception.
+ ...
+ 12.s. If _oneWeekDays_ = 0, throw a *RangeError* exception.
+features: [Temporal]
+---*/
+
+const cal = new class extends Temporal.Calendar {
+ dateAdd(date, duration, options) {
+ // Called several times, last call sets oneYear/Month/WeekDays to 0
+ return new Temporal.PlainDate(1970, 1, 1);
+ }
+}("iso8601");
+
+const d1 = new Temporal.PlainDate(1970, 1, 1, cal);
+const d2 = new Temporal.PlainDate(1971, 1, 1, cal);
+
+assert.throws(RangeError, () => d1.since(d2, { smallestUnit: "years" }), "zero year length handled correctly");
+assert.throws(RangeError, () => d1.since(d2, { smallestUnit: "months" }), "zero month length handled correctly");
+assert.throws(RangeError, () => d1.since(d2, { smallestUnit: "weeks" }), "zero week length handled correctly");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingincrement-nan.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingincrement-nan.js
new file mode 100644
index 0000000000..147002ea9d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/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.plaindate.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.plaindate.prototype.since step 12:
+ 12. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, *undefined*, *false*).
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2000, 5, 7);
+assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: NaN }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingincrement-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingincrement-non-integer.js
new file mode 100644
index 0000000000..350d8aea2e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingincrement-non-integer.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.plaindate.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.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2000, 5, 7);
+const result = later.since(earlier, { roundingIncrement: 2.5, roundingMode: "trunc" });
+TemporalHelpers.assertDuration(result, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, "roundingIncrement 2.5 truncates to 2");
+const result2 = later.since(earlier, { smallestUnit: "days", roundingIncrement: 1e9 + 0.5, roundingMode: "expand" });
+TemporalHelpers.assertDuration(result2, 0, 0, 0, 1e9, 0, 0, 0, 0, 0, 0, "roundingIncrement 1e9 + 0.5 truncates to 1e9");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingincrement-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingincrement-out-of-range.js
new file mode 100644
index 0000000000..b08717d226
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/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.plaindate.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.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2000, 5, 7);
+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/PlainDate/prototype/since/roundingincrement-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingincrement-undefined.js
new file mode 100644
index 0000000000..929967235b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingincrement-undefined.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.plaindate.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.plaindate.prototype.since step 12:
+ 12. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, *undefined*, *false*).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2000, 5, 7);
+
+const explicit = later.since(earlier, { roundingIncrement: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, "default roundingIncrement is 1");
+
+// See options-undefined.js for {}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingincrement-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingincrement-wrong-type.js
new file mode 100644
index 0000000000..c7ed2fee9d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/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.plaindate.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.plaindate.prototype.since step 12:
+ 12. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, *undefined*, *false*).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2000, 5, 7);
+
+TemporalHelpers.checkRoundingIncrementOptionWrongType(
+ (roundingIncrement) => later.since(earlier, { roundingIncrement }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, descr),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingincrement.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingincrement.js
new file mode 100644
index 0000000000..5873e3b25f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingincrement.js
@@ -0,0 +1,31 @@
+// |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.plaindate.prototype.since
+description: Tests calculations with roundingMode "trunc".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = Temporal.PlainDate.from("2019-01-08");
+const later = Temporal.PlainDate.from("2021-09-07");
+
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "years", roundingIncrement: 4, roundingMode: "halfExpand" }),
+ /* years = */ 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, "years");
+
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "months", roundingIncrement: 10, roundingMode: "halfExpand" }),
+ 0, /* months = */ 30, 0, 0, 0, 0, 0, 0, 0, 0, "months");
+
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "weeks", roundingIncrement: 12, roundingMode: "halfExpand" }),
+ 0, 0, /* weeks = */ 144, 0, 0, 0, 0, 0, 0, 0, "weeks");
+
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "days", roundingIncrement: 100, roundingMode: "halfExpand" }),
+ 0, 0, 0, /* days = */ 1000, 0, 0, 0, 0, 0, 0, "days");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-ceil.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-ceil.js
new file mode 100644
index 0000000000..8a64c7afe3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-ceil.js
@@ -0,0 +1,39 @@
+// |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.plaindate.prototype.since
+description: Tests calculations with roundingMode "ceil".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2019, 1, 8);
+const later = new Temporal.PlainDate(2021, 9, 7);
+
+const expected = [
+ ["years", [3], [-2]],
+ ["months", [0, 32], [0, -31]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+];
+
+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/PlainDate/prototype/since/roundingmode-expand.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-expand.js
new file mode 100644
index 0000000000..a869c012ad
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-expand.js
@@ -0,0 +1,39 @@
+// |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.plaindate.prototype.since
+description: Tests calculations with roundingMode "expand".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2019, 1, 8);
+const later = new Temporal.PlainDate(2021, 9, 7);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [0, 32], [0, -32]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+];
+
+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/PlainDate/prototype/since/roundingmode-floor.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-floor.js
new file mode 100644
index 0000000000..6048242bbf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-floor.js
@@ -0,0 +1,39 @@
+// |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.plaindate.prototype.since
+description: Tests calculations with roundingMode "floor".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2019, 1, 8);
+const later = new Temporal.PlainDate(2021, 9, 7);
+
+const expected = [
+ ["years", [2], [-3]],
+ ["months", [0, 31], [0, -32]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+];
+
+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/PlainDate/prototype/since/roundingmode-halfCeil.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-halfCeil.js
new file mode 100644
index 0000000000..7bdf5db27f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-halfCeil.js
@@ -0,0 +1,39 @@
+// |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.plaindate.prototype.since
+description: Tests calculations with roundingMode "halfCeil".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2019, 1, 8);
+const later = new Temporal.PlainDate(2021, 9, 7);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [0, 32], [0, -32]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+];
+
+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/PlainDate/prototype/since/roundingmode-halfEven.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-halfEven.js
new file mode 100644
index 0000000000..f95cb286eb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-halfEven.js
@@ -0,0 +1,39 @@
+// |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.plaindate.prototype.since
+description: Tests calculations with roundingMode "halfEven".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2019, 1, 8);
+const later = new Temporal.PlainDate(2021, 9, 7);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [0, 32], [0, -32]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+];
+
+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/PlainDate/prototype/since/roundingmode-halfExpand.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-halfExpand.js
new file mode 100644
index 0000000000..1b137fa714
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-halfExpand.js
@@ -0,0 +1,39 @@
+// |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.plaindate.prototype.since
+description: Tests calculations with roundingMode "halfExpand".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2019, 1, 8);
+const later = new Temporal.PlainDate(2021, 9, 7);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [0, 32], [0, -32]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+];
+
+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/PlainDate/prototype/since/roundingmode-halfFloor.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-halfFloor.js
new file mode 100644
index 0000000000..fd2112d032
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-halfFloor.js
@@ -0,0 +1,39 @@
+// |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.plaindate.prototype.since
+description: Tests calculations with roundingMode "halfFloor".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2019, 1, 8);
+const later = new Temporal.PlainDate(2021, 9, 7);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [0, 32], [0, -32]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+];
+
+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/PlainDate/prototype/since/roundingmode-halfTrunc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-halfTrunc.js
new file mode 100644
index 0000000000..e7e36d28f0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-halfTrunc.js
@@ -0,0 +1,39 @@
+// |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.plaindate.prototype.since
+description: Tests calculations with roundingMode "halfTrunc".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2019, 1, 8);
+const later = new Temporal.PlainDate(2021, 9, 7);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [0, 32], [0, -32]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+];
+
+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/PlainDate/prototype/since/roundingmode-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-invalid-string.js
new file mode 100644
index 0000000000..fe4245ee3b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/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.plaindate.prototype.since
+description: RangeError thrown when roundingMode option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2001, 6, 3);
+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/PlainDate/prototype/since/roundingmode-trunc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-trunc.js
new file mode 100644
index 0000000000..6b2deedab4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-trunc.js
@@ -0,0 +1,39 @@
+// |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.plaindate.prototype.since
+description: Tests calculations with roundingMode "trunc".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2019, 1, 8);
+const later = new Temporal.PlainDate(2021, 9, 7);
+
+const expected = [
+ ["years", [2], [-2]],
+ ["months", [0, 31], [0, -31]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+];
+
+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/PlainDate/prototype/since/roundingmode-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-undefined.js
new file mode 100644
index 0000000000..d0fe47a652
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-undefined.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.plaindate.prototype.since
+description: Fallback value for roundingMode option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 1, 1);
+
+const later1 = new Temporal.PlainDate(2005, 2, 20);
+const explicit1 = later1.since(earlier, { smallestUnit: "year", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit1, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, "default roundingMode is trunc");
+const implicit1 = later1.since(earlier, { smallestUnit: "year" });
+TemporalHelpers.assertDuration(implicit1, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, "default roundingMode is trunc");
+
+const later2 = new Temporal.PlainDate(2005, 12, 15);
+const explicit2 = later2.since(earlier, { smallestUnit: "year", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit2, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, "default roundingMode is trunc");
+const implicit2 = later2.since(earlier, { smallestUnit: "year" });
+TemporalHelpers.assertDuration(implicit2, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, "default roundingMode is trunc");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/roundingmode-wrong-type.js
new file mode 100644
index 0000000000..9f69923d72
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/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.plaindate.prototype.since
+description: Type conversions for roundingMode option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2001, 6, 3);
+TemporalHelpers.checkStringOptionWrongType("roundingMode", "trunc",
+ (roundingMode) => later.since(earlier, { smallestUnit: "year", roundingMode }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/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/PlainDate/prototype/since/smallestunit-higher-units.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/smallestunit-higher-units.js
new file mode 100644
index 0000000000..906fa73ecc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/smallestunit-higher-units.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.plaindate.prototype.since
+description: Tests calculations with higher smallestUnit than the default of "days"
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = Temporal.PlainDate.from("2019-01-08");
+const later = Temporal.PlainDate.from("2021-09-07");
+
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "years", roundingMode: "halfExpand" }),
+ /* years = */ 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, "years");
+
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "months", roundingMode: "halfExpand" }),
+ 0, /* months = */ 32, 0, 0, 0, 0, 0, 0, 0, 0, "months");
+
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "weeks", roundingMode: "halfExpand" }),
+ 0, 0, /* weeks = */ 139, 0, 0, 0, 0, 0, 0, 0, "weeks");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/smallestunit-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/smallestunit-invalid-string.js
new file mode 100644
index 0000000000..40b84325d3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/smallestunit-invalid-string.js
@@ -0,0 +1,41 @@
+// |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.plaindate.prototype.since
+description: RangeError thrown when smallestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2001, 6, 3);
+const badValues = [
+ "era",
+ "eraYear",
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+ "month\0",
+ "YEAR",
+ "eras",
+ "eraYears",
+ "hours",
+ "minutes",
+ "seconds",
+ "milliseconds",
+ "microseconds",
+ "nanoseconds",
+ "months\0",
+ "YEARS",
+ "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/PlainDate/prototype/since/smallestunit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/smallestunit-plurals-accepted.js
new file mode 100644
index 0000000000..e60ddefa73
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/smallestunit-plurals-accepted.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.plaindate.prototype.since
+description: Plural units are accepted as well for the smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2001, 6, 12);
+const validUnits = [
+ "year",
+ "month",
+ "week",
+ "day",
+];
+TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => later.since(earlier, { smallestUnit }), validUnits);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/smallestunit-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/smallestunit-undefined.js
new file mode 100644
index 0000000000..a27bf87ec4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/smallestunit-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.plaindate.prototype.since
+description: Fallback value for smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2001, 6, 3);
+
+const explicit = later.since(earlier, { smallestUnit: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 397, 0, 0, 0, 0, 0, 0, "default smallestUnit is day");
+const implicit = later.since(earlier, {});
+TemporalHelpers.assertDuration(implicit, 0, 0, 0, 397, 0, 0, 0, 0, 0, 0, "default smallestUnit is day");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/smallestunit-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/smallestunit-wrong-type.js
new file mode 100644
index 0000000000..c55f5caf9a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/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.plaindate.prototype.since
+description: Type conversions for smallestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2001, 6, 3);
+TemporalHelpers.checkStringOptionWrongType("smallestUnit", "year",
+ (smallestUnit) => later.since(earlier, { smallestUnit }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/weeks-months.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/weeks-months.js
new file mode 100644
index 0000000000..d22b6914ea
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/weeks-months.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.plaindate.prototype.since
+description: since() should not return weeks and months together.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(1969, 7, 24);
+const laterDate = new Temporal.PlainDate(1969, 9, 4);
+TemporalHelpers.assertDuration(laterDate.since(date, { largestUnit: "weeks" }),
+ 0, 0, /* weeks = */ 6, 0, 0, 0, 0, 0, 0, 0, "weeks");
+TemporalHelpers.assertDuration(laterDate.since(date, { largestUnit: "months" }),
+ 0, /* months = */ 1, 0, 11, 0, 0, 0, 0, 0, 0, "months");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/year-zero.js
new file mode 100644
index 0000000000..d894c5036c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/year-zero.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.plaindate.prototype.since
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T00:45",
+ "-000000-10-31T00:45+01:00",
+ "-000000-10-31T00:45+00:00[UTC]",
+];
+const instance = new Temporal.PlainDate(2000, 5, 2);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.since(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-duration-max.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-duration-max.js
new file mode 100644
index 0000000000..09d307861f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-duration-max.js
@@ -0,0 +1,58 @@
+// |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.plaindate.prototype.subtract
+description: Maximum allowed duration
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(1970, 1, 1);
+
+const maxCases = [
+ ["P273790Y8M12DT23H59M59.999999999S", "string with max years"],
+ [{ years: 273790, months: 8, days: 12, nanoseconds: 86399999999999 }, "property bag with max years"],
+ ["P3285488M12DT23H59M59.999999999S", "string with max months"],
+ [{ months: 3285488, days: 12, nanoseconds: 86399999999999 }, "property bag with max months"],
+ ["P14285714W3DT23H59M59.999999999S", "string with max weeks"],
+ [{ weeks: 14285714, days: 3, nanoseconds: 86399999999999 }, "property bag with max weeks"],
+ ["P100000001DT23H59M59.999999999S", "string with max days"],
+ [{ days: 100000001, nanoseconds: 86399999999999 }, "property bag with max days"],
+ ["PT2400000047H59M59.999999999S", "string with max hours"],
+ [{ hours: 2400000047, nanoseconds: 3599999999999 }, "property bag with max hours"],
+ ["PT144000002879M59.999999999S", "string with max minutes"],
+ [{ minutes: 144000002879, nanoseconds: 59999999999 }, "property bag with max minutes"],
+ ["PT8640000172799.999999999S", "string with max seconds"],
+ [{ seconds: 8640000172799, nanoseconds: 999999999 }, "property bag with max seconds"],
+];
+
+for (const [arg, descr] of maxCases) {
+ const result = instance.subtract(arg);
+ TemporalHelpers.assertPlainDate(result, -271821, 4, "M04", 19, `operation succeeds with ${descr}`);
+}
+
+const minCases = [
+ ["-P273790Y8M12DT23H59M59.999999999S", "string with min years"],
+ [{ years: -273790, months: -8, days: -12, nanoseconds: -86399999999999 }, "property bag with min years"],
+ ["-P3285488M12DT23H59M59.999999999S", "string with min months"],
+ [{ months: -3285488, days: -12, nanoseconds: -86399999999999 }, "property bag with min months"],
+ ["-P14285714W2DT23H59M59.999999999S", "string with min weeks"],
+ [{ weeks: -14285714, days: -2, nanoseconds: -86399999999999 }, "property bag with min weeks"],
+ ["-P100000000DT23H59M59.999999999S", "string with min days"],
+ [{ days: -100000000, nanoseconds: -86399999999999 }, "property bag with min days"],
+ ["-PT2400000023H59M59.999999999S", "string with min hours"],
+ [{ hours: -2400000023, nanoseconds: -3599999999999 }, "property bag with min hours"],
+ ["-PT144000001439M59.999999999S", "string with min minutes"],
+ [{ minutes: -144000001439, nanoseconds: -59999999999 }, "property bag with min minutes"],
+ ["-PT8640000086399.999999999S", "string with min seconds"],
+ [{ seconds: -8640000086399, nanoseconds: -999999999 }, "property bag with min seconds"],
+];
+
+for (const [arg, descr] of minCases) {
+ const result = instance.subtract(arg);
+ TemporalHelpers.assertPlainDate(result, 275760, 9, "M09", 13, `operation succeeds with ${descr}`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-duration-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-duration-out-of-range.js
new file mode 100644
index 0000000000..5dbcc8de3c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-duration-out-of-range.js
@@ -0,0 +1,75 @@
+// |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.plaindate.prototype.subtract
+description: Duration-like argument that is out of range
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(1970, 1, 1);
+
+const cases = [
+ // 2^32 = 4294967296
+ ["P4294967296Y", "string with years > max"],
+ [{ years: 4294967296 }, "property bag with years > max"],
+ ["-P4294967296Y", "string with years < min"],
+ [{ years: -4294967296 }, "property bag with years < min"],
+ ["P4294967296M", "string with months > max"],
+ [{ months: 4294967296 }, "property bag with months > max"],
+ ["-P4294967296M", "string with months < min"],
+ [{ months: -4294967296 }, "property bag with months < min"],
+ ["P4294967296W", "string with weeks > max"],
+ [{ weeks: 4294967296 }, "property bag with weeks > max"],
+ ["-P4294967296W", "string with weeks < min"],
+ [{ weeks: -4294967296 }, "property bag with weeks < min"],
+
+ // ceil(max safe integer / 86400) = 104249991375
+ ["P104249991375D", "string with days > max"],
+ [{ days: 104249991375 }, "property bag with days > max"],
+ ["P104249991374DT24H", "string where hours balance into days > max"],
+ [{ days: 104249991374, hours: 24 }, "property bag where hours balance into days > max"],
+ ["-P104249991375D", "string with days < min"],
+ [{ days: -104249991375 }, "property bag with days < min"],
+ ["-P104249991374DT24H", "string where hours balance into days < min"],
+ [{ days: -104249991374, hours: -24 }, "property bag where hours balance into days < min"],
+
+ // ceil(max safe integer / 3600) = 2501999792984
+ ["PT2501999792984H", "string with hours > max"],
+ [{ hours: 2501999792984 }, "property bag with hours > max"],
+ ["PT2501999792983H60M", "string where minutes balance into hours > max"],
+ [{ hours: 2501999792983, minutes: 60 }, "property bag where minutes balance into hours > max"],
+ ["-PT2501999792984H", "string with hours < min"],
+ [{ hours: -2501999792984 }, "property bag with hours < min"],
+ ["-PT2501999792983H60M", "string where minutes balance into hours < min"],
+ [{ hours: -2501999792983, minutes: -60 }, "property bag where minutes balance into hours < min"],
+
+ // ceil(max safe integer / 60) = 150119987579017
+ ["PT150119987579017M", "string with minutes > max"],
+ [{ minutes: 150119987579017 }, "property bag with minutes > max"],
+ ["PT150119987579016M60S", "string where seconds balance into minutes > max"],
+ [{ minutes: 150119987579016, seconds: 60 }, "property bag where seconds balance into minutes > max"],
+ ["-PT150119987579017M", "string with minutes < min"],
+ [{ minutes: -150119987579017 }, "property bag with minutes < min"],
+ ["-PT150119987579016M60S", "string where seconds balance into minutes < min"],
+ [{ minutes: -150119987579016, seconds: -60 }, "property bag where seconds balance into minutes < min"],
+
+ // 2^53 = 9007199254740992
+ ["PT9007199254740992S", "string with seconds > max"],
+ [{ seconds: 9007199254740992 }, "property bag with seconds > max"],
+ [{ seconds: 9007199254740991, milliseconds: 1000 }, "property bag where milliseconds balance into seconds > max"],
+ [{ seconds: 9007199254740991, microseconds: 1000000 }, "property bag where microseconds balance into seconds > max"],
+ [{ seconds: 9007199254740991, nanoseconds: 1000000000 }, "property bag where nanoseconds balance into seconds > max"],
+ ["-PT9007199254740992S", "string with seconds < min"],
+ [{ seconds: -9007199254740992 }, "property bag with seconds < min"],
+ [{ seconds: -9007199254740991, milliseconds: -1000 }, "property bag where milliseconds balance into seconds < min"],
+ [{ seconds: -9007199254740991, microseconds: -1000000 }, "property bag where microseconds balance into seconds < min"],
+ [{ seconds: -9007199254740991, nanoseconds: -1000000000 }, "property bag where nanoseconds balance into seconds < min"],
+];
+
+for (const [arg, descr] of cases) {
+ assert.throws(RangeError, () => instance.subtract(arg), `${descr} is out of range`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-invalid-property.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-invalid-property.js
new file mode 100644
index 0000000000..85deb4d369
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-invalid-property.js
@@ -0,0 +1,31 @@
+// |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.plaindate.prototype.subtract
+description: temporalDurationLike object must contain at least one correctly spelled property
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+assert.throws(
+ TypeError,
+ () => instance.subtract({}),
+ "Throws TypeError if no property is present"
+);
+
+assert.throws(
+ TypeError,
+ () => instance.subtract({ nonsense: true }),
+ "Throws TypeError if no recognized property is present"
+);
+
+assert.throws(
+ TypeError,
+ () => instance.subtract({ sign: 1 }),
+ "Sign property is not recognized"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-mixed-sign.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-mixed-sign.js
new file mode 100644
index 0000000000..82ce5272a8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-mixed-sign.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.plaindate.prototype.subtract
+description: Positive and negative values in the temporalDurationLike argument are not acceptable
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+["constrain", "reject"].forEach((overflow) => {
+ assert.throws(
+ RangeError,
+ () => instance.subtract({ hours: 1, minutes: -30 }, { overflow }),
+ `mixed positive and negative values always throw (overflow = "${overflow}")`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-not-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-not-object.js
new file mode 100644
index 0000000000..7bf90c463d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-not-object.js
@@ -0,0 +1,22 @@
+// |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.plaindate.prototype.subtract
+description: Passing a primitive other than string to subtract() throws
+features: [Symbol, Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+assert.throws(TypeError, () => instance.subtract(undefined), "undefined");
+assert.throws(TypeError, () => instance.subtract(null), "null");
+assert.throws(TypeError, () => instance.subtract(true), "boolean");
+assert.throws(RangeError, () => instance.subtract(""), "empty string");
+assert.throws(TypeError, () => instance.subtract(Symbol()), "Symbol");
+assert.throws(TypeError, () => instance.subtract(7), "number");
+assert.throws(TypeError, () => instance.subtract(7n), "bigint");
+assert.throws(TypeError, () => instance.subtract([]), "array");
+assert.throws(TypeError, () => instance.subtract(() => {}), "function");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-singular-properties.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-singular-properties.js
new file mode 100644
index 0000000000..a9c76a26de
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-singular-properties.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.plaindate.prototype.subtract
+description: Singular properties in the property bag are always ignored
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+[
+ { year: 1 },
+ { month: 2 },
+ { week: 3 },
+ { day: 4 },
+ { hour: 5 },
+ { minute: 6 },
+ { second: 7 },
+ { millisecond: 8 },
+ { microsecond: 9 },
+ { nanosecond: 10 },
+].forEach((badObject) => {
+ assert.throws(TypeError, () => instance.subtract(badObject),
+ "Throw TypeError if temporalDurationLike is not valid");
+});
+
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-string-negative-fractional-units.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-string-negative-fractional-units.js
new file mode 100644
index 0000000000..21fe56e530
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-string-negative-fractional-units.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.plaindate.prototype.subtract
+description: Strings with fractional duration units are treated with the correct sign
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+const resultHours = instance.subtract("-PT24.567890123H");
+TemporalHelpers.assertPlainDate(resultHours, 2000, 5, "M05", 3, "negative fractional hours");
+
+const resultMinutes = instance.subtract("-PT1440.567890123M");
+TemporalHelpers.assertPlainDate(resultMinutes, 2000, 5, "M05", 3, "negative fractional minutes");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-string.js
new file mode 100644
index 0000000000..168af12c46
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-string.js
@@ -0,0 +1,16 @@
+// |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.plaindate.prototype.subtract
+description: A string is parsed into the correct object when passed as the argument
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = Temporal.PlainDate.from({ year: 2000, month: 5, day: 2 });
+const result = instance.subtract("P3D");
+TemporalHelpers.assertPlainDate(result, 2000, 4, "M04", 29);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/balance-smaller-units-basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/balance-smaller-units-basic.js
new file mode 100644
index 0000000000..9c3556b488
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/balance-smaller-units-basic.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.calendar.prototype.dateadd
+description: Durations with units smaller than days are balanced
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(1976, 11, 18);
+
+// lower units that don't balance up to a day
+TemporalHelpers.assertPlainDate(date.subtract({ hours: 1 }), 1976, 11, "M11", 18);
+TemporalHelpers.assertPlainDate(date.subtract({ minutes: 1 }), 1976, 11, "M11", 18);
+TemporalHelpers.assertPlainDate(date.subtract({ seconds: 1 }), 1976, 11, "M11", 18);
+TemporalHelpers.assertPlainDate(date.subtract({ milliseconds: 1 }), 1976, 11, "M11", 18);
+TemporalHelpers.assertPlainDate(date.subtract({ microseconds: 1 }), 1976, 11, "M11", 18);
+TemporalHelpers.assertPlainDate(date.subtract({ nanoseconds: 1 }), 1976, 11, "M11", 18);
+
+// lower units that balance up to a day or more
+TemporalHelpers.assertPlainDate(date.subtract({ hours: 24 }), 1976, 11, "M11", 17);
+TemporalHelpers.assertPlainDate(date.subtract({ hours: 36 }), 1976, 11, "M11", 17);
+TemporalHelpers.assertPlainDate(date.subtract({ hours: 48 }), 1976, 11, "M11", 16);
+TemporalHelpers.assertPlainDate(date.subtract({ minutes: 1440 }), 1976, 11, "M11", 17);
+TemporalHelpers.assertPlainDate(date.subtract({ seconds: 86400 }), 1976, 11, "M11", 17);
+TemporalHelpers.assertPlainDate(date.subtract({ milliseconds: 86400_000 }), 1976, 11, "M11", 17);
+TemporalHelpers.assertPlainDate(date.subtract({ microseconds: 86400_000_000 }), 1976, 11, "M11", 17);
+TemporalHelpers.assertPlainDate(date.subtract({ nanoseconds: 86400_000_000_000 }), 1976, 11, "M11", 17);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/balance-smaller-units.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/balance-smaller-units.js
new file mode 100644
index 0000000000..5c04b17496
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/balance-smaller-units.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.calendar.prototype.dateadd
+description: Durations with units smaller than days are balanced before adding, in the calendar
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(2000, 5, 2);
+const duration = new Temporal.Duration(0, 0, 0, 1, 24, 1440, 86400, 86400_000, 86400_000_000, 86400_000_000_000);
+
+const result = date.subtract(duration);
+TemporalHelpers.assertPlainDate(result, 2000, 4, "M04", 25, "units smaller than days are balanced");
+
+const resultString = date.subtract("P1DT24H1440M86400S");
+TemporalHelpers.assertPlainDate(resultString, 2000, 4, "M04", 28, "units smaller than days are balanced");
+
+const resultPropBag = date.subtract({ days: 1, hours: 24, minutes: 1440, seconds: 86400, milliseconds: 86400_000, microseconds: 86400_000_000, nanoseconds: 86400_000_000_000 });
+TemporalHelpers.assertPlainDate(resultPropBag, 2000, 4, "M04", 25, "units smaller than days are balanced");
+
+const negativeDuration = new Temporal.Duration(0, 0, 0, -1, -24, -1440, -86400, -86400_000, -86400_000_000, -86400_000_000_000);
+const resultNegative = date.subtract(negativeDuration);
+TemporalHelpers.assertPlainDate(resultNegative, 2000, 5, "M05", 9, "units smaller than days are balanced");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/basic.js
new file mode 100644
index 0000000000..a7a928366b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/basic.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.plaindate.prototype.subtract
+description: Basic tests
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const date = Temporal.PlainDate.from("2019-11-18");
+TemporalHelpers.assertPlainDate(date.subtract({ years: 43 }),
+ 1976, 11, "M11", 18);
+TemporalHelpers.assertPlainDate(date.subtract({ months: 11 }),
+ 2018, 12, "M12", 18);
+TemporalHelpers.assertPlainDate(date.subtract({ days: 20 }),
+ 2019, 10, "M10", 29);
+TemporalHelpers.assertPlainDate(Temporal.PlainDate.from('2019-02-28').subtract({ months: 1 }),
+ 2019, 1, "M01", 28);
+TemporalHelpers.assertPlainDate(date.subtract(Temporal.Duration.from('P43Y')),
+ 1976, 11, "M11", 18);
+TemporalHelpers.assertPlainDate(Temporal.PlainDate.from('1976-11-18').subtract({ years: -43 }),
+ 2019, 11, "M11", 18);
+TemporalHelpers.assertPlainDate(Temporal.PlainDate.from('2018-12-18').subtract({ months: -11 }),
+ 2019, 11, "M11", 18);
+TemporalHelpers.assertPlainDate(Temporal.PlainDate.from('2019-10-29').subtract({ days: -20 }),
+ 2019, 11, "M11", 18);
+TemporalHelpers.assertPlainDate(Temporal.PlainDate.from('2019-01-28').subtract({ months: -1 }),
+ 2019, 2, "M02", 28);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/branding.js
new file mode 100644
index 0000000000..86a63ad288
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/branding.js
@@ -0,0 +1,27 @@
+// |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.plaindate.prototype.subtract
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const subtract = Temporal.PlainDate.prototype.subtract;
+
+assert.sameValue(typeof subtract, "function");
+
+const args = [new Temporal.Duration(5)];
+
+assert.throws(TypeError, () => subtract.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => subtract.apply(null, args), "null");
+assert.throws(TypeError, () => subtract.apply(true, args), "true");
+assert.throws(TypeError, () => subtract.apply("", args), "empty string");
+assert.throws(TypeError, () => subtract.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => subtract.apply(1, args), "1");
+assert.throws(TypeError, () => subtract.apply({}, args), "plain object");
+assert.throws(TypeError, () => subtract.apply(Temporal.PlainDate, args), "Temporal.PlainDate");
+assert.throws(TypeError, () => subtract.apply(Temporal.PlainDate.prototype, args), "Temporal.PlainDate.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..3eacf11a43
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plaindate.prototype.subtract
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dateAddOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dateAdd");
+Object.defineProperty(Temporal.Calendar.prototype, "dateAdd", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("dateAdd should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDate(2000, 5, 2, "iso8601");
+instance.subtract(new Temporal.Duration(1));
+
+Object.defineProperty(Temporal.Calendar.prototype, "dateAdd", dateAddOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/builtin.js
new file mode 100644
index 0000000000..b0eeade73a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/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.plaindate.prototype.subtract
+description: >
+ Tests that Temporal.PlainDate.prototype.subtract
+ 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.PlainDate.prototype.subtract),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDate.prototype.subtract),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDate.prototype.subtract),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDate.prototype.subtract.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/calendar-invalid-return.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/calendar-invalid-return.js
new file mode 100644
index 0000000000..57c5dfcee1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/calendar-invalid-return.js
@@ -0,0 +1,39 @@
+// |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.plaindate.prototype.subtract
+description: Throw when the returned value from the calendar's dateAdd method is not a PlainDate.
+features: [Temporal]
+---*/
+
+class CustomCalendar extends Temporal.Calendar {
+ constructor(value) {
+ super("iso8601");
+ this.value = value;
+ }
+ dateAdd() {
+ return this.value;
+ }
+}
+
+const tests = [
+ [undefined],
+ [null, "null"],
+ [true],
+ ["2000-05"],
+ [Symbol()],
+ [200005],
+ [200005n],
+ [{}, "plain object"],
+ [() => {}, "lambda"],
+ [Temporal.PlainDate, "Temporal.PlainDate"],
+ [Temporal.PlainDate.prototype, "Temporal.PlainDate.prototype"],
+];
+for (const [test, description = typeof test] of tests) {
+ const plainDate = new Temporal.PlainDate(2000, 5, 2, new CustomCalendar(test));
+ assert.throws(TypeError, () => plainDate.subtract({ years: 1 }), `Expected error with ${description}`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/custom.js
new file mode 100644
index 0000000000..06ac7f743d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/custom.js
@@ -0,0 +1,34 @@
+// |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.plaindate.prototype.subtract
+description: Basic tests with custom calendar
+includes: [compareArray.js,temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const result = new Temporal.PlainDate(1920, 5, 3);
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ dateAdd(...args) {
+ ++calls;
+ assert.sameValue(args.length, 3, "Three arguments");
+ assert.sameValue(args[0], plainDate, "First argument");
+ TemporalHelpers.assertDuration(args[1], -43, 0, 0, 0, 0, 0, 0, 0, 0, 0, "Second argument");
+ assert.sameValue(typeof args[2], "object", "Third argument: type");
+ assert.sameValue(Object.getPrototypeOf(args[2]), null, "Third argument: prototype");
+ assert.compareArray(Object.keys(args[2]), [], "Third argument: keys");
+ return result;
+ }
+}
+const calendar = new CustomCalendar();
+const plainDate = new Temporal.PlainDate(1976, 11, 18, calendar);
+assert.sameValue(plainDate.subtract({ years: 43 }), result);
+assert.sameValue(calls, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..be3aed422b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/infinity-throws-rangeerror.js
@@ -0,0 +1,38 @@
+// |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.
+
+/*---
+description: Temporal.PlainDate.prototype.subtract throws a RangeError if any value in a property bag is Infinity
+esid: sec-temporal.plaindate.prototype.subtract
+features: [Temporal]
+---*/
+
+const overflows = ["constrain", "reject"];
+const fields = ["years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"];
+
+const instance = Temporal.PlainDate.from({ year: 2000, month: 5, day: 2 });
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.subtract({ [field]: Infinity }, { overflow }));
+ });
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return Infinity;
+ }
+};
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => instance.subtract({ [field]: obj }, { overflow }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/length.js
new file mode 100644
index 0000000000..e430147b69
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/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.plaindate.prototype.subtract
+description: Temporal.PlainDate.prototype.subtract.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.PlainDate.prototype.subtract, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/limits.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/limits.js
new file mode 100644
index 0000000000..ec4378ed8f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/limits.js
@@ -0,0 +1,18 @@
+// |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.
+
+/*---
+description: Temporal.PlainDate.prototype.subtract throws a RangeError if the calculation crosses a limit
+esid: sec-temporal.plaindate.prototype.subtract
+features: [Temporal]
+---*/
+
+const min = Temporal.PlainDate.from("-271821-04-19");
+const max = Temporal.PlainDate.from("+275760-09-13");
+["reject", "constrain"].forEach((overflow) => {
+ assert.throws(RangeError, () => min.subtract({ days: 1 }, { overflow }), `min with ${overflow}`);
+ assert.throws(RangeError, () => max.subtract({ days: -1 }, { overflow }), `max with ${overflow}`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/name.js
new file mode 100644
index 0000000000..59ca6ea774
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/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.plaindate.prototype.subtract
+description: Temporal.PlainDate.prototype.subtract.name is "subtract".
+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.PlainDate.prototype.subtract, "name", {
+ value: "subtract",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/negative-infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/negative-infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..7f67ed0839
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/negative-infinity-throws-rangeerror.js
@@ -0,0 +1,38 @@
+// |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.
+
+/*---
+description: Temporal.PlainDate.prototype.subtract throws a RangeError if any value in a property bag is -Infinity
+esid: sec-temporal.plaindate.prototype.subtract
+features: [Temporal]
+---*/
+
+const overflows = ["constrain", "reject"];
+const fields = ["years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"];
+
+const instance = Temporal.PlainDate.from({ year: 2000, month: 5, day: 2 });
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.subtract({ [field]: -Infinity }, { overflow }));
+ });
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return -Infinity;
+ }
+};
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => instance.subtract({ [field]: obj }, { overflow }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/non-integer-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/non-integer-throws-rangeerror.js
new file mode 100644
index 0000000000..a6a93fbf73
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/non-integer-throws-rangeerror.js
@@ -0,0 +1,29 @@
+// |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.plaindate.prototype.subtract
+description: A non-integer value for any recognized property in the property bag, throws a RangeError
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+const fields = [
+ "years",
+ "months",
+ "weeks",
+ "days",
+ "hours",
+ "minutes",
+ "seconds",
+ "milliseconds",
+ "microseconds",
+ "nanoseconds",
+];
+fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.subtract({ [field]: 1.5 }));
+ assert.throws(RangeError, () => instance.subtract({ [field]: -1.5 }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/not-a-constructor.js
new file mode 100644
index 0000000000..47123f35d1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/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.plaindate.prototype.subtract
+description: >
+ Temporal.PlainDate.prototype.subtract 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.PlainDate.prototype.subtract();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDate.prototype.subtract), false,
+ "isConstructor(Temporal.PlainDate.prototype.subtract)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/options-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/options-object.js
new file mode 100644
index 0000000000..e365c0edd0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/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.plaindate.prototype.subtract
+description: Empty or a function object may be used as options
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+const result1 = instance.subtract({ months: 1 }, {});
+TemporalHelpers.assertPlainDate(
+ result1, 2000, 4, "M04", 2,
+ "options may be an empty plain object"
+);
+
+const result2 = instance.subtract({ months: 1 }, () => {});
+TemporalHelpers.assertPlainDate(
+ result2, 2000, 4, "M04", 2,
+ "options may be a function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/options-undefined.js
new file mode 100644
index 0000000000..ecc573fe5b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/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.plaindate.prototype.subtract
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(2000, 3, 31);
+const duration = { months: 1 };
+
+const explicit = date.subtract(duration, undefined);
+assert.sameValue(explicit.month, 2, "default overflow is constrain");
+assert.sameValue(explicit.day, 29, "default overflow is constrain");
+
+const implicit = date.subtract(duration);
+assert.sameValue(implicit.month, 2, "default overflow is constrain");
+assert.sameValue(implicit.day, 29, "default overflow is constrain");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/options-wrong-type.js
new file mode 100644
index 0000000000..ae2d8a5e1f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/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.plaindate.prototype.subtract
+description: TypeError thrown when options argument is a primitive
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const badOptions = [
+ null,
+ true,
+ "some string",
+ Symbol(),
+ 1,
+ 2n,
+];
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.subtract({ months: 1 }, value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/order-of-operations.js
new file mode 100644
index 0000000000..b3bcb378aa
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/order-of-operations.js
@@ -0,0 +1,129 @@
+// |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.plaindate.prototype.subtract
+description: Properties on an object passed to subtract() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ // ToTemporalDurationRecord
+ "get fields.days",
+ "get fields.days.valueOf",
+ "call fields.days.valueOf",
+ "get fields.hours",
+ "get fields.hours.valueOf",
+ "call fields.hours.valueOf",
+ "get fields.microseconds",
+ "get fields.microseconds.valueOf",
+ "call fields.microseconds.valueOf",
+ "get fields.milliseconds",
+ "get fields.milliseconds.valueOf",
+ "call fields.milliseconds.valueOf",
+ "get fields.minutes",
+ "get fields.minutes.valueOf",
+ "call fields.minutes.valueOf",
+ "get fields.months",
+ "get fields.months.valueOf",
+ "call fields.months.valueOf",
+ "get fields.nanoseconds",
+ "get fields.nanoseconds.valueOf",
+ "call fields.nanoseconds.valueOf",
+ "get fields.seconds",
+ "get fields.seconds.valueOf",
+ "call fields.seconds.valueOf",
+ "get fields.weeks",
+ "get fields.weeks.valueOf",
+ "call fields.weeks.valueOf",
+ "get fields.years",
+ "get fields.years.valueOf",
+ "call fields.years.valueOf",
+ // AddDate
+ "get this.calendar.dateAdd",
+ "call this.calendar.dateAdd",
+ // inside Calendar.p.dateAdd
+ "get options.overflow",
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+];
+const actual = [];
+
+const calendar = TemporalHelpers.calendarObserver(actual, "this.calendar");
+const instance = new Temporal.PlainDate(2000, 5, 2, calendar);
+// clear observable operations that occurred during the constructor call
+actual.splice(0);
+
+const fields = TemporalHelpers.propertyBagObserver(actual, {
+ years: 1,
+ months: 1,
+ weeks: 1,
+ days: 1,
+ hours: 1,
+ minutes: 1,
+ seconds: 1,
+ milliseconds: 1,
+ microseconds: 1,
+ nanoseconds: 1,
+}, "fields");
+
+const options = TemporalHelpers.propertyBagObserver(actual, {
+ overflow: "constrain",
+}, "options");
+
+instance.subtract(fields, options);
+assert.compareArray(actual, expected, "order of operations");
+
+actual.splice(0); // clear
+
+const noCalendarExpected = [
+ // ToTemporalDurationRecord
+ "get fields.days",
+ "get fields.days.valueOf",
+ "call fields.days.valueOf",
+ "get fields.hours",
+ "get fields.hours.valueOf",
+ "call fields.hours.valueOf",
+ "get fields.microseconds",
+ "get fields.microseconds.valueOf",
+ "call fields.microseconds.valueOf",
+ "get fields.milliseconds",
+ "get fields.milliseconds.valueOf",
+ "call fields.milliseconds.valueOf",
+ "get fields.minutes",
+ "get fields.minutes.valueOf",
+ "call fields.minutes.valueOf",
+ "get fields.months",
+ "get fields.nanoseconds",
+ "get fields.nanoseconds.valueOf",
+ "call fields.nanoseconds.valueOf",
+ "get fields.seconds",
+ "get fields.seconds.valueOf",
+ "call fields.seconds.valueOf",
+ "get fields.weeks",
+ "get fields.years",
+ "get this.calendar.dateAdd",
+ // AddDate
+ "get options.overflow",
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+];
+
+const noCalendarFields = TemporalHelpers.propertyBagObserver(actual, {
+ days: 1,
+ hours: 1,
+ minutes: 1,
+ seconds: 1,
+ milliseconds: 1,
+ microseconds: 1,
+ nanoseconds: 1,
+}, "fields");
+
+instance.subtract(noCalendarFields, options);
+assert.compareArray(actual, noCalendarExpected, "order of operations with no calendar operation");
+
+actual.splice(0); // clear
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/overflow-constrain.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/overflow-constrain.js
new file mode 100644
index 0000000000..b95f00c8a6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/overflow-constrain.js
@@ -0,0 +1,16 @@
+// |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.plaindate.prototype.subtract
+description: Constrains with overflow constrain
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const mar31 = Temporal.PlainDate.from("2020-03-31");
+TemporalHelpers.assertPlainDate(mar31.subtract({ months: 1 }, { overflow: "constrain" }),
+ 2020, 2, "M02", 29);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/overflow-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/overflow-invalid-string.js
new file mode 100644
index 0000000000..625e8b02be
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/overflow-invalid-string.js
@@ -0,0 +1,32 @@
+// |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.plaindate.prototype.subtract
+description: RangeError thrown when overflow option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal.calendar.prototype.dateadd step 7:
+ 7. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plaindate.prototype.subtract step 7:
+ 7. Return ? CalendarDateAdd(_temporalDate_.[[Calendar]], _temporalDate_, _negatedDuration_, _options_).
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(2000, 5, 2);
+const duration = new Temporal.Duration(3, 3, 0, 3);
+
+const badOverflows = ["", "CONSTRAIN", "balance", "other string", "constra\u0131n", "reject\0"];
+for (const overflow of badOverflows) {
+ assert.throws(
+ RangeError,
+ () => date.subtract(duration, { overflow }),
+ `invalid overflow ("${overflow}")`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/overflow-reject.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/overflow-reject.js
new file mode 100644
index 0000000000..e64d34f8ab
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/overflow-reject.js
@@ -0,0 +1,14 @@
+// |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.plaindate.prototype.subtract
+description: Throws with overflow reject
+features: [Temporal]
+---*/
+
+const mar31 = Temporal.PlainDate.from("2020-03-31");
+assert.throws(RangeError, () => mar31.subtract({ months: 1 }, { overflow: "reject" }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/overflow-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/overflow-undefined.js
new file mode 100644
index 0000000000..ae5caf3df9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/overflow-undefined.js
@@ -0,0 +1,31 @@
+// |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.plaindate.prototype.subtract
+description: Fallback value for overflow option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal.calendar.prototype.dateadd step 7:
+ 7. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plaindate.prototype.subtract step 7:
+ 7. Return ? CalendarDateAdd(_temporalDate_.[[Calendar]], _temporalDate_, _negatedDuration_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(2000, 5, 31);
+const duration = new Temporal.Duration(3, 1);
+
+const explicit = date.subtract(duration, { overflow: undefined });
+TemporalHelpers.assertPlainDate(explicit, 1997, 4, "M04", 30, "default overflow is constrain");
+const implicit = date.subtract(duration, {});
+TemporalHelpers.assertPlainDate(implicit, 1997, 4, "M04", 30, "default overflow is constrain");
+const lambda = date.subtract(duration, {});
+TemporalHelpers.assertPlainDate(lambda, 1997, 4, "M04", 30, "default overflow is constrain");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/overflow-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/overflow-wrong-type.js
new file mode 100644
index 0000000000..fc1222b4f4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/overflow-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.plaindate.prototype.subtract
+description: Type conversions for overflow option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal.calendar.prototype.dateadd step 7:
+ 7. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plaindate.prototype.subtract step 7:
+ 7. Return ? CalendarDateAdd(_temporalDate_.[[Calendar]], _temporalDate_, _negatedDuration_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(2000, 5, 2);
+const duration = new Temporal.Duration(3, 3, 0, 3);
+TemporalHelpers.checkStringOptionWrongType("overflow", "constrain",
+ (overflow) => date.subtract(duration, { overflow }),
+ (result, descr) => TemporalHelpers.assertPlainDate(result, 1997, 1, "M01", 30, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/prop-desc.js
new file mode 100644
index 0000000000..8cf73cfc43
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/prop-desc.js
@@ -0,0 +1,24 @@
+// |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.plaindate.prototype.subtract
+description: The "subtract" property of Temporal.PlainDate.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDate.prototype.subtract,
+ "function",
+ "`typeof PlainDate.prototype.subtract` is `function`"
+);
+
+verifyProperty(Temporal.PlainDate.prototype, "subtract", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/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/PlainDate/prototype/subtract/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/subclassing-ignored.js
new file mode 100644
index 0000000000..70dcbfaa75
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/subclassing-ignored.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.plaindate.prototype.subtract
+description: Objects of a subclass are never created as return values for subtract()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.PlainDate,
+ [2000, 5, 2],
+ "subtract",
+ [{ days: 1 }],
+ (result) => TemporalHelpers.assertPlainDate(result, 2000, 5, "M05", 1),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/basic.js
new file mode 100644
index 0000000000..b81332353d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/basic.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.plaindate.prototype.tojson
+description: Basic behavior for toJSON
+features: [Temporal]
+---*/
+
+const tests = [
+ [new Temporal.PlainDate(1976, 2, 4), "1976-02-04"],
+ [new Temporal.PlainDate(1976, 11, 18), "1976-11-18"],
+];
+
+const options = new Proxy({}, {
+ get() { throw new Test262Error("should not get properties off argument") }
+});
+for (const [datetime, expected] of tests) {
+ assert.sameValue(datetime.toJSON(), expected, "toJSON without argument");
+ assert.sameValue(datetime.toJSON(options), expected, "toJSON with argument");
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/branding.js
new file mode 100644
index 0000000000..74cd8d11a3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/branding.js
@@ -0,0 +1,25 @@
+// |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.plaindate.prototype.tojson
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toJSON = Temporal.PlainDate.prototype.toJSON;
+
+assert.sameValue(typeof toJSON, "function");
+
+assert.throws(TypeError, () => toJSON.call(undefined), "undefined");
+assert.throws(TypeError, () => toJSON.call(null), "null");
+assert.throws(TypeError, () => toJSON.call(true), "true");
+assert.throws(TypeError, () => toJSON.call(""), "empty string");
+assert.throws(TypeError, () => toJSON.call(Symbol()), "symbol");
+assert.throws(TypeError, () => toJSON.call(1), "1");
+assert.throws(TypeError, () => toJSON.call({}), "plain object");
+assert.throws(TypeError, () => toJSON.call(Temporal.PlainDate), "Temporal.PlainDate");
+assert.throws(TypeError, () => toJSON.call(Temporal.PlainDate.prototype), "Temporal.PlainDate.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..967f21f909
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plaindate.prototype.tojson
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const idOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "id");
+Object.defineProperty(Temporal.Calendar.prototype, "id", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("id should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDate(2000, 5, 2, "iso8601");
+instance.toJSON();
+
+Object.defineProperty(Temporal.Calendar.prototype, "id", idOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/builtin.js
new file mode 100644
index 0000000000..24e1001083
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/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.plaindate.prototype.tojson
+description: >
+ Tests that Temporal.PlainDate.prototype.toJSON
+ 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.PlainDate.prototype.toJSON),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDate.prototype.toJSON),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDate.prototype.toJSON),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDate.prototype.toJSON.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/length.js
new file mode 100644
index 0000000000..dd1d659d44
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/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.plaindate.prototype.tojson
+description: Temporal.PlainDate.prototype.toJSON.length is 0
+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.PlainDate.prototype.toJSON, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/name.js
new file mode 100644
index 0000000000..e60424bd10
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/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.plaindate.prototype.tojson
+description: Temporal.PlainDate.prototype.toJSON.name is "toJSON".
+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.PlainDate.prototype.toJSON, "name", {
+ value: "toJSON",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/not-a-constructor.js
new file mode 100644
index 0000000000..53116ce170
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/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.plaindate.prototype.tojson
+description: >
+ Temporal.PlainDate.prototype.toJSON 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.PlainDate.prototype.toJSON();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDate.prototype.toJSON), false,
+ "isConstructor(Temporal.PlainDate.prototype.toJSON)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/prop-desc.js
new file mode 100644
index 0000000000..ba879c3a2a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/prop-desc.js
@@ -0,0 +1,24 @@
+// |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.plaindate.prototype.tojson
+description: The "toJSON" property of Temporal.PlainDate.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDate.prototype.toJSON,
+ "function",
+ "`typeof PlainDate.prototype.toJSON` is `function`"
+);
+
+verifyProperty(Temporal.PlainDate.prototype, "toJSON", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/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/PlainDate/prototype/toJSON/year-format.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/year-format.js
new file mode 100644
index 0000000000..4d2d63938d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toJSON/year-format.js
@@ -0,0 +1,50 @@
+// |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.plaindate.prototype.tojson
+description: Verify that the year is appropriately formatted as 4 or 6 digits
+features: [Temporal]
+---*/
+
+let instance = new Temporal.PlainDate(-100000, 12, 3);
+assert.sameValue(instance.toJSON(), "-100000-12-03", "large negative year formatted as 6-digit");
+
+instance = new Temporal.PlainDate(-10000, 4, 5);
+assert.sameValue(instance.toJSON(), "-010000-04-05", "smallest 5-digit negative year formatted as 6-digit");
+
+instance = new Temporal.PlainDate(-9999, 6, 7);
+assert.sameValue(instance.toJSON(), "-009999-06-07", "largest 4-digit negative year formatted as 6-digit");
+
+instance = new Temporal.PlainDate(-1000, 8, 9);
+assert.sameValue(instance.toJSON(), "-001000-08-09", "smallest 4-digit negative year formatted as 6-digit");
+
+instance = new Temporal.PlainDate(-999, 10, 9);
+assert.sameValue(instance.toJSON(), "-000999-10-09", "largest 3-digit negative year formatted as 6-digit");
+
+instance = new Temporal.PlainDate(-1, 8, 7);
+assert.sameValue(instance.toJSON(), "-000001-08-07", "year -1 formatted as 6-digit");
+
+instance = new Temporal.PlainDate(0, 6, 5);
+assert.sameValue(instance.toJSON(), "0000-06-05", "year 0 formatted as 4-digit");
+
+instance = new Temporal.PlainDate(1, 4, 3);
+assert.sameValue(instance.toJSON(), "0001-04-03", "year 1 formatted as 4-digit");
+
+instance = new Temporal.PlainDate(999, 2, 10);
+assert.sameValue(instance.toJSON(), "0999-02-10", "largest 3-digit positive year formatted as 4-digit");
+
+instance = new Temporal.PlainDate(1000, 1, 23);
+assert.sameValue(instance.toJSON(), "1000-01-23", "smallest 4-digit positive year formatted as 4-digit");
+
+instance = new Temporal.PlainDate(9999, 4, 5);
+assert.sameValue(instance.toJSON(), "9999-04-05", "largest 4-digit positive year formatted as 4-digit");
+
+instance = new Temporal.PlainDate(10000, 6, 7);
+assert.sameValue(instance.toJSON(), "+010000-06-07", "smallest 5-digit positive year formatted as 6-digit");
+
+instance = new Temporal.PlainDate(100000, 8, 9);
+assert.sameValue(instance.toJSON(), "+100000-08-09", "large positive year formatted as 6-digit");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/branding.js
new file mode 100644
index 0000000000..11881e250e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/branding.js
@@ -0,0 +1,25 @@
+// |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.plaindate.prototype.tolocalestring
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toLocaleString = Temporal.PlainDate.prototype.toLocaleString;
+
+assert.sameValue(typeof toLocaleString, "function");
+
+assert.throws(TypeError, () => toLocaleString.call(undefined), "undefined");
+assert.throws(TypeError, () => toLocaleString.call(null), "null");
+assert.throws(TypeError, () => toLocaleString.call(true), "true");
+assert.throws(TypeError, () => toLocaleString.call(""), "empty string");
+assert.throws(TypeError, () => toLocaleString.call(Symbol()), "symbol");
+assert.throws(TypeError, () => toLocaleString.call(1), "1");
+assert.throws(TypeError, () => toLocaleString.call({}), "plain object");
+assert.throws(TypeError, () => toLocaleString.call(Temporal.PlainDate), "Temporal.PlainDate");
+assert.throws(TypeError, () => toLocaleString.call(Temporal.PlainDate.prototype), "Temporal.PlainDate.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..990b1c7470
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plaindate.prototype.tolocalestring
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const idOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "id");
+Object.defineProperty(Temporal.Calendar.prototype, "id", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("id should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDate(2000, 5, 2, "iso8601");
+instance.toLocaleString();
+
+Object.defineProperty(Temporal.Calendar.prototype, "id", idOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/builtin.js
new file mode 100644
index 0000000000..0d5e2c8308
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/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.plaindate.prototype.tolocalestring
+description: >
+ Tests that Temporal.PlainDate.prototype.toLocaleString
+ 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.PlainDate.prototype.toLocaleString),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDate.prototype.toLocaleString),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDate.prototype.toLocaleString),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDate.prototype.toLocaleString.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/length.js
new file mode 100644
index 0000000000..560284d39a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/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.plaindate.prototype.tolocalestring
+description: Temporal.PlainDate.prototype.toLocaleString.length is 0
+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.PlainDate.prototype.toLocaleString, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/name.js
new file mode 100644
index 0000000000..3c37d8082f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/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.plaindate.prototype.tolocalestring
+description: Temporal.PlainDate.prototype.toLocaleString.name is "toLocaleString".
+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.PlainDate.prototype.toLocaleString, "name", {
+ value: "toLocaleString",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/not-a-constructor.js
new file mode 100644
index 0000000000..0e2ff764d7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/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.plaindate.prototype.tolocalestring
+description: >
+ Temporal.PlainDate.prototype.toLocaleString 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.PlainDate.prototype.toLocaleString();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDate.prototype.toLocaleString), false,
+ "isConstructor(Temporal.PlainDate.prototype.toLocaleString)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/prop-desc.js
new file mode 100644
index 0000000000..ae8d974087
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/prop-desc.js
@@ -0,0 +1,24 @@
+// |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.plaindate.prototype.tolocalestring
+description: The "toLocaleString" property of Temporal.PlainDate.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDate.prototype.toLocaleString,
+ "function",
+ "`typeof PlainDate.prototype.toLocaleString` is `function`"
+);
+
+verifyProperty(Temporal.PlainDate.prototype, "toLocaleString", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/return-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/return-string.js
new file mode 100644
index 0000000000..3843752842
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/return-string.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Kate Miháliková. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tolocalestring
+description: >
+ toLocaleString return a string.
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(2000, 5, 2);
+
+assert.sameValue(typeof date.toLocaleString("en", { dateStyle: "short" }), "string");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toLocaleString/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/PlainDate/prototype/toPlainDateTime/argument-number.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-number.js
new file mode 100644
index 0000000000..8c0d46c48f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/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.plaindate.prototype.toplaindatetime
+description: A number is invalid in place of an ISO string for Temporal.PlainTime
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+const numbers = [
+ 1,
+ -123456.987654321,
+ 1234567,
+ 123456.9876543219,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => instance.toPlainDateTime(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/PlainDate/prototype/toPlainDateTime/argument-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-object.js
new file mode 100644
index 0000000000..950b2b0e49
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-object.js
@@ -0,0 +1,22 @@
+// |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.plaindate.prototype.toplaindatetime
+description: Tests for toPlainDateTime() with an object argument.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainDate = new Temporal.PlainDate(2000, 5, 2);
+const calendar = { toString() { return "iso8601" } };
+const withOverflow = plainDate.toPlainDateTime({ hour: 25, minute: 70, second: 23 });
+TemporalHelpers.assertPlainDateTime(withOverflow, 2000, 5, "M05", 2, 23, 59, 23, 0, 0, 0, "with overflow");
+assert.sameValue(withOverflow.calendar, plainDate.calendar, "with overflow calendar");
+
+const withCalendar = plainDate.toPlainDateTime({ hour: 13, calendar });
+TemporalHelpers.assertPlainDateTime(withCalendar, 2000, 5, "M05", 2, 13, 0, 0, 0, 0, 0, "with calendar");
+assert.sameValue(withCalendar.calendar, plainDate.calendar, "with calendar calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..7b7b86161b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/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.plaindate.prototype.toplaindatetime
+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.PlainDate(2000, 5, 2);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.toPlainDateTime(arg);
+
+ TemporalHelpers.assertPlainDateTime(
+ result,
+ 2000, 5, "M05", 2, 12, 34, 56, 987, 654, 321,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..7c3603fb69
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/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.plaindate.prototype.toplaindatetime
+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.PlainDate(2000, 5, 2);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.toPlainDateTime(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..a9af6a6da0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/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.plaindate.prototype.toplaindatetime
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+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.toPlainDateTime(arg);
+
+ TemporalHelpers.assertPlainDateTime(
+ result,
+ 2000, 5, "M05", 2, 12, 34, 56, 987, 654, 321,
+ `"${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.toPlainDateTime(arg),
+ `"${arg}" UTC offset without time is not valid for PlainTime`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..320c02eb57
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/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.plaindate.prototype.toplaindatetime
+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.PlainDate(2000, 5, 2);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.toPlainDateTime(arg),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..ab6b4d7001
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/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.plaindate.prototype.toplaindatetime
+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.PlainDate(2000, 5, 2);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.toPlainDateTime(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-no-implicit-midnight.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-no-implicit-midnight.js
new file mode 100644
index 0000000000..b07a0fe3a6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/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.plaindate.prototype.toplaindatetime
+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.PlainDate(2000, 5, 2);
+assert.throws(
+ RangeError,
+ () => instance.toPlainDateTime(arg),
+ "Date-only string throws, does not implicitly convert to midnight"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-time-designator-required-for-disambiguation.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-time-designator-required-for-disambiguation.js
new file mode 100644
index 0000000000..eaecbfeb56
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/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.plaindate.prototype.toplaindatetime
+description: ISO 8601 time designator "T" required in cases of ambiguity
+includes: [temporalHelpers.js]
+features: [Temporal, arrow-function]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+TemporalHelpers.ISO.plainTimeStringsAmbiguous().forEach((string) => {
+ let arg = string;
+ assert.throws(
+ RangeError,
+ () => instance.toPlainDateTime(arg),
+ `'${arg}' is ambiguous and requires T prefix`
+ );
+ // The same string with a T prefix should not throw:
+ arg = `T${string}`;
+ instance.toPlainDateTime(arg);
+
+ arg = ` ${string}`;
+ assert.throws(
+ RangeError,
+ () => instance.toPlainDateTime(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.toPlainDateTime(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-time-separators.js
new file mode 100644
index 0000000000..6819a99c6c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/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.plaindate.prototype.toplaindatetime
+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.PlainDate(2000, 5, 2);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.toPlainDateTime(arg);
+
+ TemporalHelpers.assertPlainDateTime(
+ result,
+ 2000, 5, "M05", 2, 12, 34, 56, 987, 654, 321,
+ `variant time separators (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..fea520d08d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/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.plaindate.prototype.toplaindatetime
+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.PlainDate(2000, 5, 2);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.toPlainDateTime(arg);
+
+ TemporalHelpers.assertPlainDateTime(
+ result,
+ 2000, 5, "M05", 2, 12, 34, 56, 987, 654, 321,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..229b3c496a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/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.plaindate.prototype.toplaindatetime
+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.PlainDate(2000, 5, 2);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.toPlainDateTime(arg);
+
+ TemporalHelpers.assertPlainDateTime(
+ result,
+ 2000, 5, "M05", 2, 12, 34, 56, 987, 654, 321,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-with-time-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-with-time-designator.js
new file mode 100644
index 0000000000..b4911b666e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/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.plaindate.prototype.toplaindatetime
+description: ISO 8601 time designator "T" allowed at the start of PlainTime strings
+includes: [temporalHelpers.js]
+features: [Temporal, arrow-function]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 1, 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.toPlainDateTime(arg);
+ TemporalHelpers.assertPlainDateTime(result, 2000, 1, "M01", 1, 0, 30, 0, 0, 0, 0, `T prefix is accepted: ${arg}`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..889739cf59
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/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.plaindate.prototype.toplaindatetime
+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.PlainDate(2000, 5, 2);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.toPlainDateTime(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/PlainDate/prototype/toPlainDateTime/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-wrong-type.js
new file mode 100644
index 0000000000..9debfc601d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-wrong-type.js
@@ -0,0 +1,42 @@
+// |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.plaindate.prototype.toplaindatetime
+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.PlainDate(2000, 5, 2);
+
+const primitiveTests = [
+ [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.toPlainDateTime(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.toPlainDateTime(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/PlainDate/prototype/toPlainDateTime/argument-zoneddatetime-balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-zoneddatetime-balance-negative-time-units.js
new file mode 100644
index 0000000000..964d7d6c68
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-zoneddatetime-balance-negative-time-units.js
@@ -0,0 +1,47 @@
+// |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.plaindate.prototype.toplaindatetime
+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.plaindate.prototype.toplaindatetime step 4:
+ 4. Set _temporalTime_ to ? ToTemporalTime(_temporalTime_).
+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 date = new Temporal.PlainDate(2000, 5, 2);
+const pdt = date.toPlainDateTime(datetime);
+
+TemporalHelpers.assertPlainDateTime(pdt, 2000, 5, "M05", 2, 1, 1, 1, 1, 0, 999);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-zoneddatetime-negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-zoneddatetime-negative-epochnanoseconds.js
new file mode 100644
index 0000000000..19fb21bc72
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/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.plaindate.prototype.toplaindatetime
+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.PlainDate(2000, 5, 2);
+const result = instance.toPlainDateTime(datetime);
+TemporalHelpers.assertPlainDateTime(result, 2000, 5, "M05", 2, 16, 50, 35, 0, 0, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..e969e0d72d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/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.plaindate.prototype.toplaindatetime
+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 date = new Temporal.PlainDate(2000, 5, 2);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => date.toPlainDateTime(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..350e9e3644
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/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.plaindate.prototype.toplaindatetime
+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 date = new Temporal.PlainDate(2000, 5, 2);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => date.toPlainDateTime(datetime),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..8145f3160e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/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.plaindate.prototype.toplaindatetime
+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 date = new Temporal.PlainDate(2000, 5, 2);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => date.toPlainDateTime(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..32fcf358d6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/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.plaindate.prototype.toplaindatetime
+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 date = new Temporal.PlainDate(2000, 5, 2);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => date.toPlainDateTime(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/basic.js
new file mode 100644
index 0000000000..ee3f828471
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/basic.js
@@ -0,0 +1,34 @@
+// |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.plaindate.prototype.toplaindatetime
+description: Basic tests for toPlainDateTime().
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(2000, 5, 2);
+
+const string = date.toPlainDateTime("11:30:23");
+TemporalHelpers.assertPlainDateTime(string, 2000, 5, "M05", 2, 11, 30, 23, 0, 0, 0, "string");
+assert.sameValue(string.calendar, date.calendar, "string calendar");
+
+const optionBag = date.toPlainDateTime({ hour: 11, minute: 30, second: 23 });
+TemporalHelpers.assertPlainDateTime(optionBag, 2000, 5, "M05", 2, 11, 30, 23, 0, 0, 0, "option bag");
+assert.sameValue(optionBag.calendar, date.calendar, "option bag calendar");
+
+const plainTime = date.toPlainDateTime(Temporal.PlainTime.from("11:30:23"));
+TemporalHelpers.assertPlainDateTime(plainTime, 2000, 5, "M05", 2, 11, 30, 23, 0, 0, 0, "PlainTime");
+assert.sameValue(plainTime.calendar, date.calendar, "PlainTime calendar");
+
+const plainDateTime = date.toPlainDateTime(Temporal.PlainDateTime.from("1999-07-14T11:30:23"));
+TemporalHelpers.assertPlainDateTime(plainDateTime, 2000, 5, "M05", 2, 11, 30, 23, 0, 0, 0, "PlainDateTime");
+assert.sameValue(plainDateTime.calendar, date.calendar, "PlainDateTime calendar");
+
+const zonedDateTime = date.toPlainDateTime(Temporal.ZonedDateTime.from("1999-07-14T11:30:23Z[UTC]"));
+TemporalHelpers.assertPlainDateTime(zonedDateTime, 2000, 5, "M05", 2, 11, 30, 23, 0, 0, 0, "ZonedDateTime");
+assert.sameValue(zonedDateTime.calendar, date.calendar, "ZonedDateTime calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/branding.js
new file mode 100644
index 0000000000..4e05ce4608
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/branding.js
@@ -0,0 +1,25 @@
+// |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.plaindate.prototype.toplaindatetime
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toPlainDateTime = Temporal.PlainDate.prototype.toPlainDateTime;
+
+assert.sameValue(typeof toPlainDateTime, "function");
+
+assert.throws(TypeError, () => toPlainDateTime.call(undefined), "undefined");
+assert.throws(TypeError, () => toPlainDateTime.call(null), "null");
+assert.throws(TypeError, () => toPlainDateTime.call(true), "true");
+assert.throws(TypeError, () => toPlainDateTime.call(""), "empty string");
+assert.throws(TypeError, () => toPlainDateTime.call(Symbol()), "symbol");
+assert.throws(TypeError, () => toPlainDateTime.call(1), "1");
+assert.throws(TypeError, () => toPlainDateTime.call({}), "plain object");
+assert.throws(TypeError, () => toPlainDateTime.call(Temporal.PlainDate), "Temporal.PlainDate");
+assert.throws(TypeError, () => toPlainDateTime.call(Temporal.PlainDate.prototype), "Temporal.PlainDate.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/builtin.js
new file mode 100644
index 0000000000..e3a19cdf87
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/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.plaindate.prototype.toplaindatetime
+description: >
+ Tests that Temporal.PlainDate.prototype.toPlainDateTime
+ 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.PlainDate.prototype.toPlainDateTime),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDate.prototype.toPlainDateTime),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDate.prototype.toPlainDateTime),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDate.prototype.toPlainDateTime.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/custom.js
new file mode 100644
index 0000000000..feecd145ab
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/custom.js
@@ -0,0 +1,17 @@
+// |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.plaindate.prototype.toplaindatetime
+description: toPlainDateTime() doesn't call into the calendar.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarThrowEverything();
+const plainDate = new Temporal.PlainDate(2000, 5, 2, calendar);
+const result = plainDate.toPlainDateTime("11:30:23");
+assert.sameValue(result.getCalendar(), calendar, "calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/leap-second.js
new file mode 100644
index 0000000000..0e51120afe
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/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.plaindate.prototype.toplaindatetime
+description: Leap second is a valid ISO string for PlainTime
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+let arg = "2016-12-31T23:59:60";
+const result1 = instance.toPlainDateTime(arg);
+TemporalHelpers.assertPlainDateTime(
+ result1,
+ 2000, 5, "M05", 2, 23, 59, 59, 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.toPlainDateTime(arg);
+TemporalHelpers.assertPlainDateTime(
+ result2,
+ 2000, 5, "M05", 2, 23, 59, 59, 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/PlainDate/prototype/toPlainDateTime/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/length.js
new file mode 100644
index 0000000000..b379b4487f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/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.plaindate.prototype.toplaindatetime
+description: Temporal.PlainDate.prototype.toPlainDateTime.length is 0
+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.PlainDate.prototype.toPlainDateTime, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/limits.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/limits.js
new file mode 100644
index 0000000000..ef00cbc673
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/limits.js
@@ -0,0 +1,42 @@
+// |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.plaindate.prototype.toplaindatetime
+description: Checking limits of representable PlainDateTime
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const midnight = new Temporal.PlainTime(0, 0);
+const firstNs = new Temporal.PlainTime(0, 0, 0, 0, 0, 1);
+const lastNs = new Temporal.PlainTime(23, 59, 59, 999, 999, 999);
+const min = new Temporal.PlainDate(-271821, 4, 19);
+const max = new Temporal.PlainDate(275760, 9, 13);
+
+assert.throws(
+ RangeError,
+ () => min.toPlainDateTime(midnight),
+ "Cannot go below representable limit for PlainDateTime"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ max.toPlainDateTime(midnight),
+ 275760, 9, "M09", 13, 0, 0, 0, 0, 0, 0,
+ "Midnight on maximal representable PlainDate"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ min.toPlainDateTime(firstNs),
+ -271821, 4, "M04", 19, 0, 0, 0, 0, 0, 1,
+ "Computing the minimum (earliest) representable PlainDateTime"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ max.toPlainDateTime(lastNs),
+ 275760, 9, "M09", 13, 23, 59, 59, 999, 999, 999,
+ "Computing the maximum (latest) representable PlainDateTime"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/name.js
new file mode 100644
index 0000000000..6602a260cc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/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.plaindate.prototype.toplaindatetime
+description: Temporal.PlainDate.prototype.toPlainDateTime.name is "toPlainDateTime".
+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.PlainDate.prototype.toPlainDateTime, "name", {
+ value: "toPlainDateTime",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/not-a-constructor.js
new file mode 100644
index 0000000000..1c7ef2a91e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/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.plaindate.prototype.toplaindatetime
+description: >
+ Temporal.PlainDate.prototype.toPlainDateTime 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.PlainDate.prototype.toPlainDateTime();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDate.prototype.toPlainDateTime), false,
+ "isConstructor(Temporal.PlainDate.prototype.toPlainDateTime)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/plaintime-propertybag-no-time-units.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/plaintime-propertybag-no-time-units.js
new file mode 100644
index 0000000000..0c2dc3a98e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/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.plaindate.prototype.toplaindatetime
+description: Missing time units in property bag default to 0
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 1, 1);
+
+const props = {};
+assert.throws(TypeError, () => instance.toPlainDateTime(props), "TypeError if no properties are present");
+
+props.minute = 30;
+const result = instance.toPlainDateTime(props);
+TemporalHelpers.assertPlainDateTime(result, 2000, 1, "M01", 1, 0, 30, 0, 0, 0, 0, "missing time units default to 0");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/prop-desc.js
new file mode 100644
index 0000000000..4d1804f704
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/prop-desc.js
@@ -0,0 +1,24 @@
+// |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.plaindate.prototype.toplaindatetime
+description: The "toPlainDateTime" property of Temporal.PlainDate.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDate.prototype.toPlainDateTime,
+ "function",
+ "`typeof PlainDate.prototype.toPlainDateTime` is `function`"
+);
+
+verifyProperty(Temporal.PlainDate.prototype, "toPlainDateTime", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/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/PlainDate/prototype/toPlainDateTime/time-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/time-invalid.js
new file mode 100644
index 0000000000..410d715a42
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/time-invalid.js
@@ -0,0 +1,15 @@
+// |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.plaindate.prototype.toplaindatetime
+description: TypeError thrown if an invalid property bag passed
+features: [Temporal]
+---*/
+
+const plainDate = new Temporal.PlainDate(2000, 5, 2);
+assert.throws(TypeError, () => plainDate.toPlainDateTime({}), "empty object");
+assert.throws(TypeError, () => plainDate.toPlainDateTime({ minutes: 30 }), "plural property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/time-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/time-undefined.js
new file mode 100644
index 0000000000..19b2c1148a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/time-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.plaindate.prototype.toplaindatetime
+description: The time is assumed to be midnight if not given
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(2000, 5, 2);
+
+const explicit = date.toPlainDateTime(undefined);
+TemporalHelpers.assertPlainDateTime(explicit, 2000, 5, "M05", 2, 0, 0, 0, 0, 0, 0, "default time is midnight - explicit");
+
+const implicit = date.toPlainDateTime();
+TemporalHelpers.assertPlainDateTime(implicit, 2000, 5, "M05", 2, 0, 0, 0, 0, 0, 0, "default time is midnight - implicit");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/year-zero.js
new file mode 100644
index 0000000000..d91e610a16
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/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.plaindate.prototype.toplaindatetime
+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.PlainDate(2000, 5, 2);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.toPlainDateTime(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/basic.js
new file mode 100644
index 0000000000..8913418dd4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/basic.js
@@ -0,0 +1,18 @@
+// |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.plaindate.prototype.toplainmonthday
+description: Basic toPlainMonthDay tests.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = new Temporal.Calendar("iso8601");
+const pd = new Temporal.PlainDate(1970, 12, 24, calendar);
+const pmd = pd.toPlainMonthDay();
+TemporalHelpers.assertPlainMonthDay(pmd, "M12", 24);
+assert.sameValue(pmd.getISOFields().calendar, "iso8601");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/branding.js
new file mode 100644
index 0000000000..7e8ac4ce94
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/branding.js
@@ -0,0 +1,25 @@
+// |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.plaindate.prototype.toplainmonthday
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toPlainMonthDay = Temporal.PlainDate.prototype.toPlainMonthDay;
+
+assert.sameValue(typeof toPlainMonthDay, "function");
+
+assert.throws(TypeError, () => toPlainMonthDay.call(undefined), "undefined");
+assert.throws(TypeError, () => toPlainMonthDay.call(null), "null");
+assert.throws(TypeError, () => toPlainMonthDay.call(true), "true");
+assert.throws(TypeError, () => toPlainMonthDay.call(""), "empty string");
+assert.throws(TypeError, () => toPlainMonthDay.call(Symbol()), "symbol");
+assert.throws(TypeError, () => toPlainMonthDay.call(1), "1");
+assert.throws(TypeError, () => toPlainMonthDay.call({}), "plain object");
+assert.throws(TypeError, () => toPlainMonthDay.call(Temporal.PlainDate), "Temporal.PlainDate");
+assert.throws(TypeError, () => toPlainMonthDay.call(Temporal.PlainDate.prototype), "Temporal.PlainDate.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..b6551cc78e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/builtin-calendar-no-array-iteration.js
@@ -0,0 +1,23 @@
+// |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.plaindate.prototype.toplainmonthday
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.PlainDate(2023, 5, 2, "iso8601");
+instance.toPlainMonthDay();
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..415b89e0c6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plaindate.prototype.toplainmonthday
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const monthDayFromFieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "monthDayFromFields");
+Object.defineProperty(Temporal.Calendar.prototype, "monthDayFromFields", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("monthDayFromFields should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDate(2000, 5, 2, "iso8601");
+instance.toPlainMonthDay();
+
+Object.defineProperty(Temporal.Calendar.prototype, "monthDayFromFields", monthDayFromFieldsOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/builtin.js
new file mode 100644
index 0000000000..1c6fce388a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/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.plaindate.prototype.toplainmonthday
+description: >
+ Tests that Temporal.PlainDate.prototype.toPlainMonthDay
+ 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.PlainDate.prototype.toPlainMonthDay),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDate.prototype.toPlainMonthDay),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDate.prototype.toPlainMonthDay),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDate.prototype.toPlainMonthDay.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/calendar-arguments.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/calendar-arguments.js
new file mode 100644
index 0000000000..0c4cec8683
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/calendar-arguments.js
@@ -0,0 +1,32 @@
+// |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.plaindate.prototype.toplainmonthday
+description: Correct options value is passed to calendar method
+info: |
+ MonthDayFromFields ( calendar, fields [ , options ] )
+
+ 3. If options is not present, then
+ a. Set options to undefined.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ monthDayFromFields(...args) {
+ assert.sameValue(args.length, 2, "args.length");
+ assert.sameValue(typeof args[0], "object", "args[0]");
+ assert.sameValue(args[1], undefined, "args[1]");
+ return super.monthDayFromFields(...args);
+ }
+}
+const plainDate = new Temporal.PlainDate(2000, 5, 2, new CustomCalendar());
+const result = plainDate.toPlainMonthDay();
+TemporalHelpers.assertPlainMonthDay(result, "M05", 2);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/calendar-fields-iterable.js
new file mode 100644
index 0000000000..04a6061fe0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/calendar-fields-iterable.js
@@ -0,0 +1,30 @@
+// |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.plaindate.prototype.toplainmonthday
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plaindate.prototype.toplainmonthday step 4:
+ 4. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"monthCode"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "monthCode",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+const date = new Temporal.PlainDate(2000, 5, 2, calendar);
+date.toPlainMonthDay();
+
+assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/calendar-fromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/calendar-fromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..ed596ad4a1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/calendar-fromfields-called-with-null-prototype-fields.js
@@ -0,0 +1,18 @@
+// |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.plaindate.prototype.toplainmonthday
+description: >
+ Calendar.monthDayFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.PlainDate(2000, 5, 2, calendar);
+instance.toPlainMonthDay();
+assert.sameValue(calendar.monthDayFromFieldsCallCount, 1, "monthDayFromFields should have been called on the calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/calendar-invalid-return.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/calendar-invalid-return.js
new file mode 100644
index 0000000000..8bab7f5cdf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/calendar-invalid-return.js
@@ -0,0 +1,39 @@
+// |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.plaindate.prototype.toplainmonthday
+description: Throw when the returned value is not a PlainMonthDay.
+features: [Temporal]
+---*/
+
+class CustomCalendar extends Temporal.Calendar {
+ constructor(value) {
+ super("iso8601");
+ this.value = value;
+ }
+ monthDayFromFields() {
+ return this.value;
+ }
+}
+
+const tests = [
+ [undefined],
+ [null, "null"],
+ [true],
+ ["2000-05"],
+ [Symbol()],
+ [200005],
+ [200005n],
+ [{}, "plain object"],
+ [() => {}, "lambda"],
+ [Temporal.PlainMonthDay, "Temporal.PlainMonthDay"],
+ [Temporal.PlainMonthDay.prototype, "Temporal.PlainMonthDay.prototype"],
+];
+for (const [test, description = typeof test] of tests) {
+ const plainDate = new Temporal.PlainDate(2000, 5, 2, new CustomCalendar(test));
+ assert.throws(TypeError, () => plainDate.toPlainMonthDay(), `Expected error with ${description}`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/calendar-monthdayfromfields-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/calendar-monthdayfromfields-called-with-options-undefined.js
new file mode 100644
index 0000000000..2c1fbc5862
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/calendar-monthdayfromfields-called-with-options-undefined.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.plaindate.prototype.toplainmonthday
+description: >
+ Calendar.monthDayFromFields method is called with undefined as the options
+ value when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
+const instance = new Temporal.PlainDate(2000, 5, 2, calendar);
+instance.toPlainMonthDay();
+assert.sameValue(calendar.monthDayFromFieldsCallCount, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..1151eadc13
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/constructor-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |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.plaindate.prototype.toplainmonthday
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const date = new Temporal.PlainDate(2023, 5, 1, calendar);
+
+assert.throws(RangeError, () => date.toPlainMonthDay());
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/duplicate-calendar-fields.js
new file mode 100644
index 0000000000..3467871850
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/duplicate-calendar-fields.js
@@ -0,0 +1,18 @@
+// |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.plaindate.prototype.toplainmonthday
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['monthCode'], ['day']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const date = new Temporal.PlainDate(2023, 5, 1, calendar);
+
+ assert.throws(RangeError, () => date.toPlainMonthDay());
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/length.js
new file mode 100644
index 0000000000..e86efba232
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/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.plaindate.prototype.toplainmonthday
+description: Temporal.PlainDate.prototype.toPlainMonthDay.length is 0
+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.PlainDate.prototype.toPlainMonthDay, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/name.js
new file mode 100644
index 0000000000..6df4fb2d10
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/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.plaindate.prototype.toplainmonthday
+description: Temporal.PlainDate.prototype.toPlainMonthDay.name is "toPlainMonthDay".
+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.PlainDate.prototype.toPlainMonthDay, "name", {
+ value: "toPlainMonthDay",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/not-a-constructor.js
new file mode 100644
index 0000000000..0c64846eef
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/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.plaindate.prototype.toplainmonthday
+description: >
+ Temporal.PlainDate.prototype.toPlainMonthDay 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.PlainDate.prototype.toPlainMonthDay();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDate.prototype.toPlainMonthDay), false,
+ "isConstructor(Temporal.PlainDate.prototype.toPlainMonthDay)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/prop-desc.js
new file mode 100644
index 0000000000..e6d11cf65c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/prop-desc.js
@@ -0,0 +1,24 @@
+// |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.plaindate.prototype.toplainmonthday
+description: The "toPlainMonthDay" property of Temporal.PlainDate.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDate.prototype.toPlainMonthDay,
+ "function",
+ "`typeof PlainDate.prototype.toPlainMonthDay` is `function`"
+);
+
+verifyProperty(Temporal.PlainDate.prototype, "toPlainMonthDay", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/proto-in-calendar-fields.js
new file mode 100644
index 0000000000..d77d73bef1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/proto-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |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.plaindate.prototype.toplainmonthday
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const date = new Temporal.PlainDate(2023, 5, 1, calendar);
+
+assert.throws(RangeError, () => date.toPlainMonthDay());
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/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/PlainDate/prototype/toPlainYearMonth/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/basic.js
new file mode 100644
index 0000000000..f0375df0d3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/basic.js
@@ -0,0 +1,18 @@
+// |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.plaindate.prototype.toplainyearmonth
+description: Basic toPlainYearMonth tests.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = new Temporal.Calendar("iso8601");
+const pd = new Temporal.PlainDate(1970, 12, 24, calendar);
+const pym = pd.toPlainYearMonth();
+TemporalHelpers.assertPlainYearMonth(pym, 1970, 12, "M12");
+assert.sameValue(pym.getISOFields().calendar, "iso8601");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/branding.js
new file mode 100644
index 0000000000..474a20edae
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/branding.js
@@ -0,0 +1,25 @@
+// |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.plaindate.prototype.toplainyearmonth
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toPlainYearMonth = Temporal.PlainDate.prototype.toPlainYearMonth;
+
+assert.sameValue(typeof toPlainYearMonth, "function");
+
+assert.throws(TypeError, () => toPlainYearMonth.call(undefined), "undefined");
+assert.throws(TypeError, () => toPlainYearMonth.call(null), "null");
+assert.throws(TypeError, () => toPlainYearMonth.call(true), "true");
+assert.throws(TypeError, () => toPlainYearMonth.call(""), "empty string");
+assert.throws(TypeError, () => toPlainYearMonth.call(Symbol()), "symbol");
+assert.throws(TypeError, () => toPlainYearMonth.call(1), "1");
+assert.throws(TypeError, () => toPlainYearMonth.call({}), "plain object");
+assert.throws(TypeError, () => toPlainYearMonth.call(Temporal.PlainDate), "Temporal.PlainDate");
+assert.throws(TypeError, () => toPlainYearMonth.call(Temporal.PlainDate.prototype), "Temporal.PlainDate.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..120d0e59a4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/builtin-calendar-no-array-iteration.js
@@ -0,0 +1,23 @@
+// |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.plaindate.prototype.toplainyearmonth
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.PlainDate(2023, 5, 2, "iso8601");
+instance.toPlainYearMonth();
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..1254551e64
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plaindate.prototype.toplainyearmonth
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const yearMonthFromFieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "yearMonthFromFields");
+Object.defineProperty(Temporal.Calendar.prototype, "yearMonthFromFields", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("yearMonthFromFields should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDate(2000, 5, 2, "iso8601");
+instance.toPlainYearMonth();
+
+Object.defineProperty(Temporal.Calendar.prototype, "yearMonthFromFields", yearMonthFromFieldsOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/builtin.js
new file mode 100644
index 0000000000..2aaa7857cb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/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.plaindate.prototype.toplainyearmonth
+description: >
+ Tests that Temporal.PlainDate.prototype.toPlainYearMonth
+ 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.PlainDate.prototype.toPlainYearMonth),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDate.prototype.toPlainYearMonth),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDate.prototype.toPlainYearMonth),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDate.prototype.toPlainYearMonth.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/calendar-arguments.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/calendar-arguments.js
new file mode 100644
index 0000000000..af062b81de
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/calendar-arguments.js
@@ -0,0 +1,32 @@
+// |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.plaindate.prototype.toplainyearmonth
+description: Correct options value is passed to calendar method
+info: |
+ YearMonthFromFields ( calendar, fields [ , options ] )
+
+ 3. If options is not present, then
+ a. Set options to undefined.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ yearMonthFromFields(...args) {
+ assert.sameValue(args.length, 2, "args.length");
+ assert.sameValue(typeof args[0], "object", "args[0]");
+ assert.sameValue(args[1], undefined, "args[1]");
+ return super.yearMonthFromFields(...args);
+ }
+}
+const plainDate = new Temporal.PlainDate(2000, 5, 2, new CustomCalendar());
+const result = plainDate.toPlainYearMonth();
+TemporalHelpers.assertPlainYearMonth(result, 2000, 5, "M05");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/calendar-fields-iterable.js
new file mode 100644
index 0000000000..60d60b3ba5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/calendar-fields-iterable.js
@@ -0,0 +1,30 @@
+// |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.plaindate.prototype.toplainyearmonth
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plaindate.prototype.toplainyearmonth step 4:
+ 4. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "monthCode",
+ "year",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+const date = new Temporal.PlainDate(2000, 5, 2, calendar);
+date.toPlainYearMonth();
+
+assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/calendar-fromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/calendar-fromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..c22c2e0f28
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/calendar-fromfields-called-with-null-prototype-fields.js
@@ -0,0 +1,18 @@
+// |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.plaindate.prototype.toplainyearmonth
+description: >
+ Calendar.yearMonthFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.PlainDate(2000, 5, 2, calendar);
+instance.toPlainYearMonth();
+assert.sameValue(calendar.yearMonthFromFieldsCallCount, 1, "yearMonthFromFields should have been called on the calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/calendar-invalid-return.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/calendar-invalid-return.js
new file mode 100644
index 0000000000..79fdfa229c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/calendar-invalid-return.js
@@ -0,0 +1,39 @@
+// |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.plaindate.prototype.toplainyearmonth
+description: Throw when the returned value is not a PlainYearMonth.
+features: [Temporal]
+---*/
+
+class CustomCalendar extends Temporal.Calendar {
+ constructor(value) {
+ super("iso8601");
+ this.value = value;
+ }
+ yearMonthFromFields() {
+ return this.value;
+ }
+}
+
+const tests = [
+ [undefined],
+ [null, "null"],
+ [true],
+ ["2000-05"],
+ [Symbol()],
+ [200005],
+ [200005n],
+ [{}, "plain object"],
+ [() => {}, "lambda"],
+ [Temporal.PlainYearMonth, "Temporal.PlainYearMonth"],
+ [Temporal.PlainYearMonth.prototype, "Temporal.PlainYearMonth.prototype"],
+];
+for (const [test, description = typeof test] of tests) {
+ const plainDate = new Temporal.PlainDate(2000, 5, 2, new CustomCalendar(test));
+ assert.throws(TypeError, () => plainDate.toPlainYearMonth(), `Expected error with ${description}`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/calendar-yearmonthfromfields-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/calendar-yearmonthfromfields-called-with-options-undefined.js
new file mode 100644
index 0000000000..71c8e2f012
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/calendar-yearmonthfromfields-called-with-options-undefined.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.plaindate.prototype.toplainyearmonth
+description: >
+ Calendar.yearMonthFromFields method is called with undefined as the options
+ value when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
+const instance = new Temporal.PlainDate(2000, 5, 2, calendar);
+instance.toPlainYearMonth();
+assert.sameValue(calendar.yearMonthFromFieldsCallCount, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..25e3db2553
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/constructor-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |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.plaindate.prototype.toplainyearmonth
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const date = new Temporal.PlainDate(2023, 5, 1, calendar);
+
+assert.throws(RangeError, () => date.toPlainYearMonth());
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/duplicate-calendar-fields.js
new file mode 100644
index 0000000000..31e21202a1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/duplicate-calendar-fields.js
@@ -0,0 +1,18 @@
+// |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.plaindate.prototype.toplainyearmonth
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['monthCode'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const date = new Temporal.PlainDate(2023, 5, 1, calendar);
+
+ assert.throws(RangeError, () => date.toPlainYearMonth());
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/length.js
new file mode 100644
index 0000000000..c5166053c9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/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.plaindate.prototype.toplainyearmonth
+description: Temporal.PlainDate.prototype.toPlainYearMonth.length is 0
+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.PlainDate.prototype.toPlainYearMonth, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/limits.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/limits.js
new file mode 100644
index 0000000000..e45d4b90b8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/limits.js
@@ -0,0 +1,20 @@
+// |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.plaindate.prototype.toplainyearmonth
+description: toPlainYearMonth works within the supported range
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const min = Temporal.PlainDate.from('-271821-04-19');
+TemporalHelpers.assertPlainYearMonth(min.toPlainYearMonth(),
+ -271821, 4, "M04", "min");
+
+const max = Temporal.PlainDate.from('+275760-09-13');
+TemporalHelpers.assertPlainYearMonth(max.toPlainYearMonth(),
+ 275760, 9, "M09", "max");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/name.js
new file mode 100644
index 0000000000..219f4134e7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/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.plaindate.prototype.toplainyearmonth
+description: Temporal.PlainDate.prototype.toPlainYearMonth.name is "toPlainYearMonth".
+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.PlainDate.prototype.toPlainYearMonth, "name", {
+ value: "toPlainYearMonth",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/not-a-constructor.js
new file mode 100644
index 0000000000..cd849a5ec4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/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.plaindate.prototype.toplainyearmonth
+description: >
+ Temporal.PlainDate.prototype.toPlainYearMonth 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.PlainDate.prototype.toPlainYearMonth();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDate.prototype.toPlainYearMonth), false,
+ "isConstructor(Temporal.PlainDate.prototype.toPlainYearMonth)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/prop-desc.js
new file mode 100644
index 0000000000..b57bfe6a1f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/prop-desc.js
@@ -0,0 +1,24 @@
+// |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.plaindate.prototype.toplainyearmonth
+description: The "toPlainYearMonth" property of Temporal.PlainDate.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDate.prototype.toPlainYearMonth,
+ "function",
+ "`typeof PlainDate.prototype.toPlainYearMonth` is `function`"
+);
+
+verifyProperty(Temporal.PlainDate.prototype, "toPlainYearMonth", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/proto-in-calendar-fields.js
new file mode 100644
index 0000000000..91397b7c51
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/proto-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |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.plaindate.prototype.toplainyearmonth
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const date = new Temporal.PlainDate(2023, 5, 1, calendar);
+
+assert.throws(RangeError, () => date.toPlainYearMonth());
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/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/PlainDate/prototype/toString/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/basic.js
new file mode 100644
index 0000000000..1b856747c0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/basic.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.plaindate.protoype.tostring
+description: basic tests
+features: [Temporal]
+---*/
+
+const date1 = new Temporal.PlainDate(1976, 11, 18);
+assert.sameValue(date1.toString(), "1976-11-18");
+
+const date2 = new Temporal.PlainDate(1914, 2, 23);
+assert.sameValue(date2.toString(), "1914-02-23");
+
+const date3 = new Temporal.PlainDate(1996, 2, 29);
+assert.sameValue(date3.toString(), "1996-02-29");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/branding.js
new file mode 100644
index 0000000000..eb8f0cd16b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/branding.js
@@ -0,0 +1,25 @@
+// |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.plaindate.prototype.tostring
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toString = Temporal.PlainDate.prototype.toString;
+
+assert.sameValue(typeof toString, "function");
+
+assert.throws(TypeError, () => toString.call(undefined), "undefined");
+assert.throws(TypeError, () => toString.call(null), "null");
+assert.throws(TypeError, () => toString.call(true), "true");
+assert.throws(TypeError, () => toString.call(""), "empty string");
+assert.throws(TypeError, () => toString.call(Symbol()), "symbol");
+assert.throws(TypeError, () => toString.call(1), "1");
+assert.throws(TypeError, () => toString.call({}), "plain object");
+assert.throws(TypeError, () => toString.call(Temporal.PlainDate), "Temporal.PlainDate");
+assert.throws(TypeError, () => toString.call(Temporal.PlainDate.prototype), "Temporal.PlainDate.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..0ce71025f7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plaindate.prototype.tostring
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const idOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "id");
+Object.defineProperty(Temporal.Calendar.prototype, "id", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("id should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDate(2000, 5, 2, "iso8601");
+instance.toString();
+
+Object.defineProperty(Temporal.Calendar.prototype, "id", idOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/builtin.js
new file mode 100644
index 0000000000..070c996b91
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/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.plaindate.prototype.tostring
+description: >
+ Tests that Temporal.PlainDate.prototype.toString
+ 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.PlainDate.prototype.toString),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDate.prototype.toString),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDate.prototype.toString),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDate.prototype.toString.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/calendar-tostring.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/calendar-tostring.js
new file mode 100644
index 0000000000..b09d756a6d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/calendar-tostring.js
@@ -0,0 +1,56 @@
+// |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.plaindate.protoype.tostring
+description: Number of observable 'toString' calls on the calendar for each value of calendarName
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+let calls;
+const customCalendar = {
+ get id() {
+ ++calls;
+ return "custom";
+ },
+ toString() {
+ TemporalHelpers.assertUnreachable('toString should not be called');
+ },
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+const date = new Temporal.PlainDate(2000, 5, 2, customCalendar);
+[
+ ["always", "2000-05-02[u-ca=custom]", 1],
+ ["auto", "2000-05-02[u-ca=custom]", 1],
+ ["critical", "2000-05-02[!u-ca=custom]", 1],
+ ["never", "2000-05-02", 0],
+ [undefined, "2000-05-02[u-ca=custom]", 1],
+].forEach(([calendarName, expectedResult, expectedCalls]) => {
+ calls = 0;
+ const result = date.toString({ calendarName });
+ assert.sameValue(result, expectedResult, `id for calendarName = ${calendarName}`);
+ assert.sameValue(calls, expectedCalls, `calls to id getter for calendarName = ${calendarName}`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/calendarname-always.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/calendarname-always.js
new file mode 100644
index 0000000000..686b38ffbf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/calendarname-always.js
@@ -0,0 +1,48 @@
+// |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.plaindate.prototype.tostring
+description: If calendarName is "always", the calendar ID should be included.
+features: [Temporal]
+---*/
+
+const calendarMethods = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+
+const tests = [
+ [[], "2000-05-02[u-ca=iso8601]", "built-in ISO"],
+ [[{ id: "custom", ...calendarMethods }], "2000-05-02[u-ca=custom]", "custom"],
+ [[{ id: "iso8601", ...calendarMethods }], "2000-05-02[u-ca=iso8601]", "custom with iso8601 id"],
+ [[{ id: "ISO8601", ...calendarMethods }], "2000-05-02[u-ca=ISO8601]", "custom with caps id"],
+ [[{ id: "\u0131so8601", ...calendarMethods }], "2000-05-02[u-ca=\u0131so8601]", "custom with dotless i id"],
+];
+
+for (const [args, expected, description] of tests) {
+ const date = new Temporal.PlainDate(2000, 5, 2, ...args);
+ const result = date.toString({ calendarName: "always" });
+ assert.sameValue(result, expected, `${description} calendar for calendarName = always`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/calendarname-auto.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/calendarname-auto.js
new file mode 100644
index 0000000000..7b5e322e18
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/calendarname-auto.js
@@ -0,0 +1,48 @@
+// |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.plaindate.prototype.tostring
+description: If calendarName is "auto", "iso8601" should be omitted.
+features: [Temporal]
+---*/
+
+const calendarMethods = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+
+const tests = [
+ [[], "2000-05-02", "built-in ISO"],
+ [[{ id: "custom", ...calendarMethods }], "2000-05-02[u-ca=custom]", "custom"],
+ [[{ id: "iso8601", ...calendarMethods }], "2000-05-02", "custom with iso8601 id"],
+ [[{ id: "ISO8601", ...calendarMethods }], "2000-05-02[u-ca=ISO8601]", "custom with caps id"],
+ [[{ id: "\u0131so8601", ...calendarMethods }], "2000-05-02[u-ca=\u0131so8601]", "custom with dotless i id"],
+];
+
+for (const [args, expected, description] of tests) {
+ const date = new Temporal.PlainDate(2000, 5, 2, ...args);
+ const result = date.toString({ calendarName: "auto" });
+ assert.sameValue(result, expected, `${description} calendar for calendarName = auto`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/calendarname-critical.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/calendarname-critical.js
new file mode 100644
index 0000000000..ceb1876546
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/calendarname-critical.js
@@ -0,0 +1,50 @@
+// |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.plaindate.prototype.tostring
+description: >
+ If calendarName is "calendar", the calendar ID should be included and prefixed
+ with "!".
+features: [Temporal]
+---*/
+
+const calendarMethods = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+
+const tests = [
+ [[], "2000-05-02[!u-ca=iso8601]", "built-in ISO"],
+ [[{ id: "custom", ...calendarMethods }], "2000-05-02[!u-ca=custom]", "custom"],
+ [[{ id: "iso8601", ...calendarMethods }], "2000-05-02[!u-ca=iso8601]", "custom with iso8601 id"],
+ [[{ id: "ISO8601", ...calendarMethods }], "2000-05-02[!u-ca=ISO8601]", "custom with caps id"],
+ [[{ id: "\u0131so8601", ...calendarMethods }], "2000-05-02[!u-ca=\u0131so8601]", "custom with dotless i id"],
+];
+
+for (const [args, expected, description] of tests) {
+ const date = new Temporal.PlainDate(2000, 5, 2, ...args);
+ const result = date.toString({ calendarName: "critical" });
+ assert.sameValue(result, expected, `${description} calendar for calendarName = critical`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/calendarname-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/calendarname-invalid-string.js
new file mode 100644
index 0000000000..6bdcddd835
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/calendarname-invalid-string.js
@@ -0,0 +1,29 @@
+// |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.plaindate.protoype.tostring
+description: RangeError thrown when calendarName option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-toshowcalendaroption step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"calendarName"*, « *"string"* », « *"auto"*, *"always"*, *"never"*, *"critical"* », *"auto"*).
+ sec-temporal.plaindate.protoype.tostring step 4:
+ 4. Let _showCalendar_ be ? ToShowCalendarOption(_options_).
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(2000, 5, 2);
+const invalidValues = ["ALWAYS", "sometimes", "other string", "auto\0"];
+
+for (const calendarName of invalidValues) {
+ assert.throws(
+ RangeError,
+ () => date.toString({ calendarName }),
+ `${calendarName} is an invalid value for calendarName option`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/calendarname-never.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/calendarname-never.js
new file mode 100644
index 0000000000..0c77b349f1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/calendarname-never.js
@@ -0,0 +1,48 @@
+// |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.plaindate.prototype.tostring
+description: If calendarName is "never", the calendar ID should be omitted.
+features: [Temporal]
+---*/
+
+const calendarMethods = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+
+const tests = [
+ [[], "2000-05-02", "built-in ISO"],
+ [[{ id: "custom", ...calendarMethods }], "2000-05-02", "custom"],
+ [[{ id: "iso8601", ...calendarMethods }], "2000-05-02", "custom with iso8601 id"],
+ [[{ id: "ISO8601", ...calendarMethods }], "2000-05-02", "custom with caps id"],
+ [[{ id: "\u0131so8601", ...calendarMethods }], "2000-05-02", "custom with dotless i id"],
+];
+
+for (const [args, expected, description] of tests) {
+ const date = new Temporal.PlainDate(2000, 5, 2, ...args);
+ const result = date.toString({ calendarName: "never" });
+ assert.sameValue(result, expected, `${description} calendar for calendarName = never`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/calendarname-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/calendarname-undefined.js
new file mode 100644
index 0000000000..ef4e32a99a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/calendarname-undefined.js
@@ -0,0 +1,56 @@
+// |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.plaindate.protoype.tostring
+description: Fallback value for calendarName option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-toshowcalendaroption step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"calendarName"*, « *"string"* », « *"auto"*, *"always"*, *"never"*, *"critical"* », *"auto"*).
+ sec-temporal.plaindate.protoype.tostring step 4:
+ 4. Let _showCalendar_ be ? ToShowCalendarOption(_options_).
+features: [Temporal]
+---*/
+
+const calendarMethods = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+
+const tests = [
+ [[], "2000-05-02", "built-in ISO"],
+ [[{ id: "custom", ...calendarMethods }], "2000-05-02[u-ca=custom]", "custom"],
+ [[{ id: "iso8601", ...calendarMethods }], "2000-05-02", "custom with iso8601 id"],
+ [[{ id: "ISO8601", ...calendarMethods }], "2000-05-02[u-ca=ISO8601]", "custom with caps id"],
+ [[{ id: "\u0131so8601", ...calendarMethods }], "2000-05-02[u-ca=\u0131so8601]", "custom with dotless i id"],
+];
+
+for (const [args, expected, description] of tests) {
+ const date = new Temporal.PlainDate(2000, 5, 2, ...args);
+ const result = date.toString({ calendarName: undefined });
+ assert.sameValue(result, expected, `default calendarName option is auto with ${description} calendar`);
+ // See options-object.js for {} and options-undefined.js for absent options arg
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/calendarname-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/calendarname-wrong-type.js
new file mode 100644
index 0000000000..2137bf50ec
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/calendarname-wrong-type.js
@@ -0,0 +1,49 @@
+// |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.plaindate.protoype.tostring
+description: Type conversions for calendarName option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-toshowcalendaroption step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"calendarName"*, « *"string"* », « *"auto"*, *"always"*, *"never"*, *"critical"* », *"auto"*).
+ sec-temporal.plaindate.protoype.tostring step 4:
+ 4. Let _showCalendar_ be ? ToShowCalendarOption(_options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = {
+ id: "custom",
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+const date = new Temporal.PlainDate(2000, 5, 2, calendar);
+
+TemporalHelpers.checkStringOptionWrongType("calendarName", "auto",
+ (calendarName) => date.toString({ calendarName }),
+ (result, descr) => assert.sameValue(result, "2000-05-02[u-ca=custom]", descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/length.js
new file mode 100644
index 0000000000..43c3eefba3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/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.plaindate.prototype.tostring
+description: Temporal.PlainDate.prototype.toString.length is 0
+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.PlainDate.prototype.toString, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/name.js
new file mode 100644
index 0000000000..f18c131ab2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/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.plaindate.prototype.tostring
+description: Temporal.PlainDate.prototype.toString.name is "toString".
+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.PlainDate.prototype.toString, "name", {
+ value: "toString",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/not-a-constructor.js
new file mode 100644
index 0000000000..a3f47607d2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/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.plaindate.prototype.tostring
+description: >
+ Temporal.PlainDate.prototype.toString 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.PlainDate.prototype.toString();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDate.prototype.toString), false,
+ "isConstructor(Temporal.PlainDate.prototype.toString)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/options-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/options-object.js
new file mode 100644
index 0000000000..32da641983
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/options-object.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.plaindate.prototype.tostring
+description: Empty or a function object may be used as options
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+const result1 = instance.toString({});
+assert.sameValue(
+ result1, "2000-05-02",
+ "options may be an empty plain object"
+);
+
+const result2 = instance.toString(() => {});
+assert.sameValue(
+ result2, "2000-05-02",
+ "options may be a function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/options-undefined.js
new file mode 100644
index 0000000000..5b44173c28
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/options-undefined.js
@@ -0,0 +1,48 @@
+// |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.plaindate.prototype.tostring
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const calendar = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "custom",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+const date1 = new Temporal.PlainDate(2000, 5, 2);
+const date2 = new Temporal.PlainDate(2000, 5, 2, calendar);
+
+[
+ [date1, "2000-05-02"],
+ [date2, "2000-05-02[u-ca=custom]"],
+].forEach(([date, expected]) => {
+ const explicit = date.toString(undefined);
+ assert.sameValue(explicit, expected, "default calendarName option is auto");
+
+ const implicit = date.toString();
+ assert.sameValue(implicit, expected, "default calendarName option is auto");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/options-wrong-type.js
new file mode 100644
index 0000000000..5b0589dc3b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/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.plaindate.prototype.tostring
+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.PlainDate(2000, 5, 2);
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.toString(value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/order-of-operations.js
new file mode 100644
index 0000000000..1872b8b5ce
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/order-of-operations.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.plaindate.prototype.tostring
+description: Properties on an object passed to toString() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "get options.calendarName",
+ "get options.calendarName.toString",
+ "call options.calendarName.toString",
+ "get this.calendar.id",
+];
+const actual = [];
+
+const calendar = TemporalHelpers.calendarObserver(actual, "this.calendar");
+const instance = new Temporal.PlainDate(2000, 5, 2, calendar);
+// clear observable operations that occurred during the constructor call
+actual.splice(0);
+
+const options = TemporalHelpers.propertyBagObserver(actual, {
+ calendarName: "auto",
+}, "options");
+
+instance.toString(options);
+assert.compareArray(actual, expected, "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/prop-desc.js
new file mode 100644
index 0000000000..4575c55739
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/prop-desc.js
@@ -0,0 +1,24 @@
+// |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.plaindate.prototype.tostring
+description: The "toString" property of Temporal.PlainDate.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDate.prototype.toString,
+ "function",
+ "`typeof PlainDate.prototype.toString` is `function`"
+);
+
+verifyProperty(Temporal.PlainDate.prototype, "toString", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/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/PlainDate/prototype/toString/year-format.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/year-format.js
new file mode 100644
index 0000000000..e6e29731c9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toString/year-format.js
@@ -0,0 +1,50 @@
+// |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.plaindate.prototype.tostring
+description: Verify that the year is appropriately formatted as 4 or 6 digits
+features: [Temporal]
+---*/
+
+let instance = new Temporal.PlainDate(-100000, 12, 3);
+assert.sameValue(instance.toString(), "-100000-12-03", "large negative year formatted as 6-digit");
+
+instance = new Temporal.PlainDate(-10000, 4, 5);
+assert.sameValue(instance.toString(), "-010000-04-05", "smallest 5-digit negative year formatted as 6-digit");
+
+instance = new Temporal.PlainDate(-9999, 6, 7);
+assert.sameValue(instance.toString(), "-009999-06-07", "largest 4-digit negative year formatted as 6-digit");
+
+instance = new Temporal.PlainDate(-1000, 8, 9);
+assert.sameValue(instance.toString(), "-001000-08-09", "smallest 4-digit negative year formatted as 6-digit");
+
+instance = new Temporal.PlainDate(-999, 10, 9);
+assert.sameValue(instance.toString(), "-000999-10-09", "largest 3-digit negative year formatted as 6-digit");
+
+instance = new Temporal.PlainDate(-1, 8, 7);
+assert.sameValue(instance.toString(), "-000001-08-07", "year -1 formatted as 6-digit");
+
+instance = new Temporal.PlainDate(0, 6, 5);
+assert.sameValue(instance.toString(), "0000-06-05", "year 0 formatted as 4-digit");
+
+instance = new Temporal.PlainDate(1, 4, 3);
+assert.sameValue(instance.toString(), "0001-04-03", "year 1 formatted as 4-digit");
+
+instance = new Temporal.PlainDate(999, 2, 10);
+assert.sameValue(instance.toString(), "0999-02-10", "largest 3-digit positive year formatted as 4-digit");
+
+instance = new Temporal.PlainDate(1000, 1, 23);
+assert.sameValue(instance.toString(), "1000-01-23", "smallest 4-digit positive year formatted as 4-digit");
+
+instance = new Temporal.PlainDate(9999, 4, 5);
+assert.sameValue(instance.toString(), "9999-04-05", "largest 4-digit positive year formatted as 4-digit");
+
+instance = new Temporal.PlainDate(10000, 6, 7);
+assert.sameValue(instance.toString(), "+010000-06-07", "smallest 5-digit positive year formatted as 6-digit");
+
+instance = new Temporal.PlainDate(100000, 8, 9);
+assert.sameValue(instance.toString(), "+100000-08-09", "large positive year formatted as 6-digit");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toStringTag/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toStringTag/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toStringTag/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toStringTag/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toStringTag/prop-desc.js
new file mode 100644
index 0000000000..d5e25e8242
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toStringTag/prop-desc.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.plaindate.prototype-@@tostringtag
+description: The @@toStringTag property of Temporal.PlainDate
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype, Symbol.toStringTag, {
+ value: "Temporal.PlainDate",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toStringTag/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toStringTag/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toStringTag/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-number.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-number.js
new file mode 100644
index 0000000000..6c45963cf3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/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.plaindate.prototype.tozoneddatetime
+description: A number is invalid in place of an ISO string for Temporal.PlainTime
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+const numbers = [
+ 1,
+ -123456.987654321,
+ 1234567,
+ 123456.9876543219,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => instance.toZonedDateTime({ plainTime: arg, timeZone: "UTC" }),
+ `A number (${arg}) is not a valid ISO string for PlainTime`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..b6135a89ea
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-calendar-annotation.js
@@ -0,0 +1,42 @@
+// |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.plaindate.prototype.tozoneddatetime
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+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.PlainDate(2000, 5, 2);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.toZonedDateTime({ plainTime: arg, timeZone: "UTC" });
+
+ assert.sameValue(
+ result.epochNanoseconds,
+ 957_270_896_987_654_321n,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..7f3c36037b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/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.plaindate.prototype.tozoneddatetime
+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.PlainDate(2000, 5, 2);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.toZonedDateTime({ plainTime: arg, timeZone: "UTC" }),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..6c9d395886
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-date-with-utc-offset.js
@@ -0,0 +1,52 @@
+// |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.plaindate.prototype.tozoneddatetime
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+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.toZonedDateTime({ plainTime: arg, timeZone: "UTC" });
+
+ assert.sameValue(
+ result.epochNanoseconds,
+ 957_270_896_987_654_321n,
+ `"${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.toZonedDateTime({ plainTime: arg, timeZone: "UTC" }),
+ `"${arg}" UTC offset without time is not valid for PlainTime`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..527260a260
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/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.plaindate.prototype.tozoneddatetime
+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.PlainDate(2000, 5, 2);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.toZonedDateTime({ plainTime: arg, timeZone: "UTC" }),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..c72d9cc208
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/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.plaindate.prototype.tozoneddatetime
+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.PlainDate(2000, 5, 2);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.toZonedDateTime({ plainTime: arg, timeZone: "UTC" }),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-no-implicit-midnight.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-no-implicit-midnight.js
new file mode 100644
index 0000000000..8845c8cd45
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/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.plaindate.prototype.tozoneddatetime
+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.PlainDate(2000, 5, 2);
+assert.throws(
+ RangeError,
+ () => instance.toZonedDateTime({ plainTime: arg, timeZone: "UTC" }),
+ "Date-only string throws, does not implicitly convert to midnight"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-time-designator-required-for-disambiguation.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-time-designator-required-for-disambiguation.js
new file mode 100644
index 0000000000..99693c739d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/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.plaindate.prototype.tozoneddatetime
+description: ISO 8601 time designator "T" required in cases of ambiguity
+includes: [temporalHelpers.js]
+features: [Temporal, arrow-function]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+TemporalHelpers.ISO.plainTimeStringsAmbiguous().forEach((string) => {
+ let arg = string;
+ assert.throws(
+ RangeError,
+ () => instance.toZonedDateTime({ plainTime: arg, timeZone: "UTC" }),
+ `'${arg}' is ambiguous and requires T prefix`
+ );
+ // The same string with a T prefix should not throw:
+ arg = `T${string}`;
+ instance.toZonedDateTime({ plainTime: arg, timeZone: "UTC" });
+
+ arg = ` ${string}`;
+ assert.throws(
+ RangeError,
+ () => instance.toZonedDateTime({ plainTime: arg, timeZone: "UTC" }),
+ `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.toZonedDateTime({ plainTime: arg, timeZone: "UTC" }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-time-separators.js
new file mode 100644
index 0000000000..2855e5ffd9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-time-separators.js
@@ -0,0 +1,31 @@
+// |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.plaindate.prototype.tozoneddatetime
+description: Time separator in string argument can vary
+features: [Temporal]
+---*/
+
+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.PlainDate(2000, 5, 2);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.toZonedDateTime({ plainTime: arg, timeZone: "UTC" });
+
+ assert.sameValue(
+ result.epochNanoseconds,
+ 957_270_896_987_654_321n,
+ `variant time separators (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..03aa7e6b03
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-time-zone-annotation.js
@@ -0,0 +1,50 @@
+// |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.plaindate.prototype.tozoneddatetime
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+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.PlainDate(2000, 5, 2);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.toZonedDateTime({ plainTime: arg, timeZone: "UTC" });
+
+ assert.sameValue(
+ result.epochNanoseconds,
+ 957_270_896_987_654_321n,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..83c2cf209f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-unknown-annotation.js
@@ -0,0 +1,39 @@
+// |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.plaindate.prototype.tozoneddatetime
+description: Various forms of unknown annotation
+features: [Temporal]
+---*/
+
+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.PlainDate(2000, 5, 2);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.toZonedDateTime({ plainTime: arg, timeZone: "UTC" });
+
+ assert.sameValue(
+ result.epochNanoseconds,
+ 957_270_896_987_654_321n,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-with-time-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-with-time-designator.js
new file mode 100644
index 0000000000..d5fb0b8304
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-with-time-designator.js
@@ -0,0 +1,31 @@
+// |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.plaindate.prototype.tozoneddatetime
+description: ISO 8601 time designator "T" allowed at the start of PlainTime strings
+features: [Temporal, arrow-function]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 1, 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.toZonedDateTime({ plainTime: arg, timeZone: "UTC" });
+ assert.sameValue(result.epochNanoseconds, 946686600_000_000_000n, `T prefix is accepted: ${arg}`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..fe1278b2c7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/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.plaindate.prototype.tozoneddatetime
+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.PlainDate(2000, 5, 2);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.toZonedDateTime({ plainTime: arg, timeZone: "UTC" }),
+ "String with UTC designator should not be valid as a PlainTime"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-wrong-type.js
new file mode 100644
index 0000000000..e2f64f517b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-wrong-type.js
@@ -0,0 +1,42 @@
+// |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.plaindate.prototype.tozoneddatetime
+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.PlainDate(2000, 5, 2);
+
+const primitiveTests = [
+ [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.toZonedDateTime({ plainTime: arg, timeZone: "UTC" }),
+ `${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.toZonedDateTime({ plainTime: arg, timeZone: "UTC" }), `${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/PlainDate/prototype/toZonedDateTime/argument-zoneddatetime-negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-zoneddatetime-negative-epochnanoseconds.js
new file mode 100644
index 0000000000..f8cb95c755
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-zoneddatetime-negative-epochnanoseconds.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.plaindate.prototype.tozoneddatetime
+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]
+---*/
+
+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.PlainDate(2000, 5, 2);
+const result = instance.toZonedDateTime({ plainTime: datetime, timeZone: "UTC" });
+assert.sameValue(result.epochNanoseconds, 957286235_000_000_001n);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/basic.js
new file mode 100644
index 0000000000..1811886891
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/basic.js
@@ -0,0 +1,33 @@
+// |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.plaindate.prototype.tozoneddatetime
+description: Basic tests for toZonedDateTime().
+features: [Temporal]
+---*/
+
+const plainDate = Temporal.PlainDate.from("2020-01-01");
+const timeZone = Temporal.TimeZone.from("UTC");
+const plainTime = Temporal.PlainTime.from("12:00");
+
+let result = plainDate.toZonedDateTime({ timeZone, plainTime });
+assert.sameValue(result.toString(), "2020-01-01T12:00:00+00:00[UTC]", "objects passed");
+
+result = plainDate.toZonedDateTime(timeZone);
+assert.sameValue(result.toString(), "2020-01-01T00:00:00+00:00[UTC]", "time zone object argument");
+
+result = plainDate.toZonedDateTime("UTC");
+assert.sameValue(result.toString(), "2020-01-01T00:00:00+00:00[UTC]", "time zone string argument");
+
+result = plainDate.toZonedDateTime({ timeZone });
+assert.sameValue(result.toString(), "2020-01-01T00:00:00+00:00[UTC]", "time zone object property");
+
+result = plainDate.toZonedDateTime({ timeZone: "UTC", plainTime });
+assert.sameValue(result.toString(), "2020-01-01T12:00:00+00:00[UTC]", "time zone string property");
+
+result = plainDate.toZonedDateTime({ timeZone, plainTime: "12:00" });
+assert.sameValue(result.toString(), "2020-01-01T12:00:00+00:00[UTC]", "time string property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/branding.js
new file mode 100644
index 0000000000..9eddebf6d0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/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.plaindate.prototype.tozoneddatetime
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toZonedDateTime = Temporal.PlainDate.prototype.toZonedDateTime;
+
+assert.sameValue(typeof toZonedDateTime, "function");
+
+const args = [new Temporal.TimeZone("UTC")];
+
+assert.throws(TypeError, () => toZonedDateTime.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => toZonedDateTime.apply(null, args), "null");
+assert.throws(TypeError, () => toZonedDateTime.apply(true, args), "true");
+assert.throws(TypeError, () => toZonedDateTime.apply("", args), "empty string");
+assert.throws(TypeError, () => toZonedDateTime.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => toZonedDateTime.apply(1, args), "1");
+assert.throws(TypeError, () => toZonedDateTime.apply({}, args), "plain object");
+assert.throws(TypeError, () => toZonedDateTime.apply(Temporal.PlainDate, args), "Temporal.PlainDate");
+assert.throws(TypeError, () => toZonedDateTime.apply(Temporal.PlainDate.prototype, args), "Temporal.PlainDate.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/builtin.js
new file mode 100644
index 0000000000..c31498e3c5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/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.plaindate.prototype.tozoneddatetime
+description: >
+ Tests that Temporal.PlainDate.prototype.toZonedDateTime
+ 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.PlainDate.prototype.toZonedDateTime),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDate.prototype.toZonedDateTime),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDate.prototype.toZonedDateTime),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDate.prototype.toZonedDateTime.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/calendar.js
new file mode 100644
index 0000000000..485ee36087
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/calendar.js
@@ -0,0 +1,22 @@
+// |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.plaindate.prototype.tozoneddatetime
+description: Calendar of the receiver is used
+features: [Temporal]
+---*/
+
+const calendar = new Temporal.Calendar("iso8601");
+const timeCalendar = { toString() { return "iso8601"; } };
+const plainDate = new Temporal.PlainDate(2000, 5, 2, calendar);
+const result = plainDate.toZonedDateTime({
+ timeZone: "UTC",
+ plainTime: { hour: 12, minute: 30, calendar: timeCalendar },
+});
+assert.sameValue(result.epochNanoseconds, 957270600_000_000_000n);
+assert.sameValue(result.timeZoneId, "UTC");
+assert.sameValue(result.getCalendar(), calendar);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/getpossibleinstantsfor-called-with-iso8601-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/getpossibleinstantsfor-called-with-iso8601-calendar.js
new file mode 100644
index 0000000000..9dcceb14a9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/getpossibleinstantsfor-called-with-iso8601-calendar.js
@@ -0,0 +1,58 @@
+// |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.plaindate.prototype.tozoneddatetime
+description: >
+ Time zone's getPossibleInstantsFor is called with a PlainDateTime with the
+ built-in ISO 8601 calendar
+features: [Temporal]
+info: |
+ DisambiguatePossibleInstants:
+ 2. Let _n_ be _possibleInstants_'s length.
+ ...
+ 5. Assert: _n_ = 0.
+ ...
+ 19. If _disambiguation_ is *"earlier"*, then
+ ...
+ c. Let _earlierDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*).
+ d. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _earlierDateTime_).
+ ...
+ 20. Assert: _disambiguation_ is *"compatible"* or *"later"*.
+ ...
+ 23. Let _laterDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*).
+ 24. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _laterDateTime_).
+---*/
+
+class SkippedDateTime extends Temporal.TimeZone {
+ constructor() {
+ super("UTC");
+ this.calls = 0;
+ }
+
+ getPossibleInstantsFor(dateTime) {
+ // Calls occur in pairs. For the first one return no possible instants so
+ // that DisambiguatePossibleInstants will call it again
+ if (this.calls++ % 2 == 0) {
+ return [];
+ }
+
+ assert.sameValue(
+ dateTime.getISOFields().calendar,
+ "iso8601",
+ "getPossibleInstantsFor called with dateTime with built-in ISO 8601 calendar"
+ );
+ return super.getPossibleInstantsFor(dateTime);
+ }
+}
+
+const nonBuiltinISOCalendar = new Temporal.Calendar("iso8601");
+const timeZone = new SkippedDateTime();
+
+const instance = new Temporal.PlainDate(2000, 5, 2, nonBuiltinISOCalendar);
+instance.toZonedDateTime(timeZone);
+
+assert.sameValue(timeZone.calls, 2, "getPossibleInstantsFor should have been called 2 times");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/leap-second.js
new file mode 100644
index 0000000000..2560495911
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/leap-second.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.plaindate.prototype.tozoneddatetime
+description: Leap second is a valid ISO string for PlainTime
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+let arg = "2016-12-31T23:59:60";
+const result1 = instance.toZonedDateTime({ plainTime: arg, timeZone: "UTC" });
+assert.sameValue(
+ result1.epochNanoseconds,
+ 957311999_000_000_000n,
+ "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.toZonedDateTime({ plainTime: arg, timeZone: "UTC" });
+assert.sameValue(
+ result2.epochNanoseconds,
+ 957311999_000_000_000n,
+ "second: 60 is ignored in property bag for PlainTime"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/length.js
new file mode 100644
index 0000000000..405ef56df9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/length.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.plaindate.prototype.tozoneddatetime
+description: Temporal.PlainDate.prototype.toZonedDateTime.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.PlainDate.prototype.toZonedDateTime, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/name.js
new file mode 100644
index 0000000000..15818816d2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/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.plaindate.prototype.tozoneddatetime
+description: Temporal.PlainDate.prototype.toZonedDateTime.name is "toZonedDateTime".
+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.PlainDate.prototype.toZonedDateTime, "name", {
+ value: "toZonedDateTime",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/not-a-constructor.js
new file mode 100644
index 0000000000..80ad27d974
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/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.plaindate.prototype.tozoneddatetime
+description: >
+ Temporal.PlainDate.prototype.toZonedDateTime 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.PlainDate.prototype.toZonedDateTime();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDate.prototype.toZonedDateTime), false,
+ "isConstructor(Temporal.PlainDate.prototype.toZonedDateTime)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/order-of-operations.js
new file mode 100644
index 0000000000..b84a4c5fa4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/order-of-operations.js
@@ -0,0 +1,98 @@
+// |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.plaindate.prototype.tozoneddatetime
+description: User code calls happen in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const expected = [
+ "get item.timeZone",
+ "has item.timeZone.getOffsetNanosecondsFor",
+ "has item.timeZone.getPossibleInstantsFor",
+ "has item.timeZone.id",
+ "get item.plainTime",
+ // ToTemporalTime
+ "get item.plainTime.hour",
+ "get item.plainTime.hour.valueOf",
+ "call item.plainTime.hour.valueOf",
+ "get item.plainTime.microsecond",
+ "get item.plainTime.microsecond.valueOf",
+ "call item.plainTime.microsecond.valueOf",
+ "get item.plainTime.millisecond",
+ "get item.plainTime.millisecond.valueOf",
+ "call item.plainTime.millisecond.valueOf",
+ "get item.plainTime.minute",
+ "get item.plainTime.minute.valueOf",
+ "call item.plainTime.minute.valueOf",
+ "get item.plainTime.nanosecond",
+ "get item.plainTime.nanosecond.valueOf",
+ "call item.plainTime.nanosecond.valueOf",
+ "get item.plainTime.second",
+ "get item.plainTime.second.valueOf",
+ "call item.plainTime.second.valueOf",
+ // lookup in PlainDate.p.toZonedDateTime
+ "get item.timeZone.getOffsetNanosecondsFor",
+ "get item.timeZone.getPossibleInstantsFor",
+ // GetInstantFor
+ "call item.timeZone.getPossibleInstantsFor",
+];
+
+const calendar = TemporalHelpers.calendarObserver(actual, "this.calendar");
+const instance = new Temporal.PlainDate(2000, 1, 1, calendar);
+const springForwardInstance = new Temporal.PlainDate(2000, 4, 2, calendar);
+const fallBackInstance = new Temporal.PlainDate(2000, 10, 29, calendar);
+actual.splice(0); // clear calendar calls that happened in constructors
+
+const plainTime = TemporalHelpers.propertyBagObserver(actual, {
+ hour: 2,
+ minute: 30,
+ second: 0,
+ millisecond: 0,
+ microsecond: 0,
+ nanosecond: 0,
+}, "item.plainTime");
+const dstTimeZone = TemporalHelpers.springForwardFallBackTimeZone();
+const timeZone = TemporalHelpers.timeZoneObserver(actual, "item.timeZone", {
+ getOffsetNanosecondsFor: dstTimeZone.getOffsetNanosecondsFor,
+ getPossibleInstantsFor: dstTimeZone.getPossibleInstantsFor,
+});
+const item = TemporalHelpers.propertyBagObserver(actual, {
+ plainTime,
+ timeZone,
+}, "item");
+
+instance.toZonedDateTime(item);
+assert.compareArray(actual, expected, "order of operations at normal wall-clock time");
+actual.splice(0); // clear
+
+const plainTime130 = TemporalHelpers.propertyBagObserver(actual, {
+ hour: 1,
+ minute: 30,
+ second: 0,
+ millisecond: 0,
+ microsecond: 0,
+ nanosecond: 0,
+}, "item.plainTime");
+const item130 = TemporalHelpers.propertyBagObserver(actual, {
+ plainTime: plainTime130,
+ timeZone,
+}, "item");
+
+fallBackInstance.toZonedDateTime(item130);
+assert.compareArray(actual, expected, "order of operations at repeated wall-clock time");
+actual.splice(0); // clear
+
+springForwardInstance.toZonedDateTime(item);
+assert.compareArray(actual, expected.concat([
+ "call item.timeZone.getOffsetNanosecondsFor",
+ "call item.timeZone.getOffsetNanosecondsFor",
+ "call item.timeZone.getPossibleInstantsFor",
+]), "order of operations at skipped wall-clock time");
+actual.splice(0); // clear
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/plaintime-argument-zoneddatetime-balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/plaintime-argument-zoneddatetime-balance-negative-time-units.js
new file mode 100644
index 0000000000..e631b28963
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/plaintime-argument-zoneddatetime-balance-negative-time-units.js
@@ -0,0 +1,49 @@
+// |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.plaindate.prototype.tozoneddatetime
+description: Negative time fields are balanced upwards in a ZonedDateTime given as plainTime
+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.plaindate.prototype.tozoneddatetime step 6.a:
+ a. Set _temporalTime_ to ? ToTemporalTime(_temporalTime_).
+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 otherTimeZone = new Temporal.TimeZone("UTC"); // should not be used to convert datetime to PlainTime
+const date = new Temporal.PlainDate(2000, 5, 2);
+const zdt = date.toZonedDateTime({ timeZone: otherTimeZone, plainTime: datetime });
+
+assert.sameValue(zdt.microsecond, 0);
+assert.sameValue(zdt.nanosecond, 999);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/plaintime-argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/plaintime-argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..576fc4b23a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/plaintime-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.plaindate.prototype.tozoneddatetime
+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 date = new Temporal.PlainDate(2000, 5, 2);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => date.toZonedDateTime({ plainTime: datetime, timeZone: "UTC" }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/plaintime-argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/plaintime-argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..7adf2dd547
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/plaintime-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.plaindate.prototype.tozoneddatetime
+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 date = new Temporal.PlainDate(2000, 5, 2);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => date.toZonedDateTime({ plainTime: datetime, timeZone: "UTC" }),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/plaintime-argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/plaintime-argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..d29f3afa1f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/plaintime-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.plaindate.prototype.tozoneddatetime
+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 date = new Temporal.PlainDate(2000, 5, 2);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => date.toZonedDateTime({ plainTime: datetime, timeZone: "UTC" }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/plaintime-argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/plaintime-argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..f2ad7aa761
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/plaintime-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.plaindate.prototype.tozoneddatetime
+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 date = new Temporal.PlainDate(2000, 5, 2);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => date.toZonedDateTime({ plainTime: datetime, timeZone: "UTC" }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/plaintime-propertybag-no-time-units.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/plaintime-propertybag-no-time-units.js
new file mode 100644
index 0000000000..f29691c8c9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/plaintime-propertybag-no-time-units.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.plaindate.prototype.tozoneddatetime
+description: Missing time units in property bag default to 0
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 1, 1);
+
+const props = {};
+assert.throws(TypeError, () => instance.toZonedDateTime({ plainTime: props, timeZone: "UTC" }), "TypeError if no properties are present");
+
+props.minute = 30;
+const result = instance.toZonedDateTime({ plainTime: props, timeZone: "UTC" });
+assert.sameValue(result.epochNanoseconds, 946686600_000_000_000n, "missing time units default to 0");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/prop-desc.js
new file mode 100644
index 0000000000..04db137fc2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/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.plaindate.prototype.tozoneddatetime
+description: The "toZonedDateTime" property of Temporal.PlainDate.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDate.prototype.toZonedDateTime,
+ "function",
+ "`typeof PlainDate.prototype.toZonedDateTime` is `function`"
+);
+
+verifyProperty(Temporal.PlainDate.prototype, "toZonedDateTime", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/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/PlainDate/prototype/toZonedDateTime/timezone-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-case-insensitive.js
new file mode 100644
index 0000000000..9e66040e43
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-case-insensitive.js
@@ -0,0 +1,17 @@
+// |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.plaindate.prototype.tozoneddatetime
+description: Time zone names are case insensitive
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+const timeZone = 'uTc';
+const result = instance.toZonedDateTime(timeZone);
+assert.sameValue(result.timeZoneId, 'UTC', `Time zone created from string "${timeZone}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..1b664b2991
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-non-integer.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.plaindate.prototype.tozoneddatetime
+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 date = new Temporal.PlainDate(2000, 5, 2);
+ const plainTime = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+ timeZone.getPossibleInstantsFor = function () {
+ return [];
+ };
+ assert.throws(RangeError, () => date.toZonedDateTime({ plainTime, timeZone }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..d4096890d3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-not-callable.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.plaindate.prototype.tozoneddatetime
+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 date = new Temporal.PlainDate(2000, 5, 2);
+ const plainTime = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+ timeZone.getPossibleInstantsFor = function () {
+ return [];
+ };
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => date.toZonedDateTime({ plainTime, timeZone }),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..8d96870354
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-out-of-range.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.plaindate.prototype.tozoneddatetime
+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 date = new Temporal.PlainDate(2000, 5, 2);
+ const plainTime = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+ timeZone.getPossibleInstantsFor = function () {
+ return [];
+ };
+ assert.throws(RangeError, () => date.toZonedDateTime({ plainTime, timeZone }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..d59f7149ae
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,31 @@
+// |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.plaindate.prototype.tozoneddatetime
+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 date = new Temporal.PlainDate(2000, 5, 2);
+ const plainTime = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+ timeZone.getPossibleInstantsFor = function () {
+ return [];
+ };
+ assert.throws(TypeError, () => date.toZonedDateTime({ plainTime, timeZone }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-getpossibleinstantsfor-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-getpossibleinstantsfor-iterable.js
new file mode 100644
index 0000000000..05331a4606
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-getpossibleinstantsfor-iterable.js
@@ -0,0 +1,45 @@
+// |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.plaindate.prototype.tozoneddatetime
+description: An iterable returned from timeZone.getPossibleInstantsFor is consumed after each call
+info: |
+ sec-temporal.plaindate.prototype.tozoneddatetime step 7:
+ 7. Let _instant_ be ? BuiltinTimeZoneGetInstantFor(_timeZone_, _temporalDateTime_, *"compatible"*).
+ sec-temporal-builtintimezonegetinstantfor step 1:
+ 1. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_).
+ sec-temporal-builtintimezonegetinstantfor step 14:
+ 14. Assert: _disambiguation_ is *"compatible"* or *"later"*.
+ sec-temporal-builtintimezonegetinstantfor step 16:
+ 16. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _later_).
+ sec-temporal-getpossibleinstantsfor step 2:
+ 2. Let _list_ be ? IterableToList(_possibleInstants_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected1 = [
+ "2000-05-02T00:00:00",
+];
+
+TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => {
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ date.toZonedDateTime(timeZone);
+}, expected1);
+
+// Same, but test the other path where the time doesn't exist and
+// GetPossibleInstantsFor is called again on a later time
+
+const expected2 = [
+ "2030-01-01T00:30:00",
+ "2030-01-01T01:30:00",
+];
+
+TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => {
+ const date = new Temporal.PlainDate(2030, 1, 1);
+ date.toZonedDateTime({ plainTime: new Temporal.PlainTime(0, 30), timeZone });
+}, expected2);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-getpossibleinstantsfor.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-getpossibleinstantsfor.js
new file mode 100644
index 0000000000..1a1d2f8494
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-getpossibleinstantsfor.js
@@ -0,0 +1,31 @@
+// |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.plaindate.prototype.tozoneddatetime
+description: Calendar of the receiver is used
+features: [Temporal]
+---*/
+
+const calendar = new Temporal.Calendar("iso8601");
+class CustomTimeZone extends Temporal.TimeZone {
+ constructor() {
+ super("UTC");
+ }
+ getPossibleInstantsFor(plainDateTime) {
+ assert.sameValue(plainDateTime.getCalendar(), calendar);
+ return [new Temporal.Instant(987654321_000_000_000n)];
+ }
+}
+const timeZone = new CustomTimeZone();
+const plainDate = new Temporal.PlainDate(2000, 5, 2, calendar);
+const result = plainDate.toZonedDateTime({
+ timeZone,
+ plainTime: { hour: 12, minute: 30 },
+});
+assert.sameValue(result.epochNanoseconds, 987654321_000_000_000n);
+assert.sameValue(result.getTimeZone(), timeZone);
+assert.sameValue(result.getCalendar(), calendar);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-string-datetime.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-string-datetime.js
new file mode 100644
index 0000000000..24c2c66267
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-string-datetime.js
@@ -0,0 +1,65 @@
+// |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.plaindate.prototype.tozoneddatetime
+description: Conversion of ISO date-time strings to Temporal.TimeZone instances
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+let timeZone = "2021-08-19T17:30";
+assert.throws(RangeError, () => instance.toZonedDateTime(timeZone), "bare date-time string is not a time zone");
+
+[
+ "2021-08-19T17:30-07:00:01",
+ "2021-08-19T17:30-07:00:00",
+ "2021-08-19T17:30-07:00:00.1",
+ "2021-08-19T17:30-07:00:00.0",
+ "2021-08-19T17:30-07:00:00.01",
+ "2021-08-19T17:30-07:00:00.00",
+ "2021-08-19T17:30-07:00:00.001",
+ "2021-08-19T17:30-07:00:00.000",
+ "2021-08-19T17:30-07:00:00.0001",
+ "2021-08-19T17:30-07:00:00.0000",
+ "2021-08-19T17:30-07:00:00.00001",
+ "2021-08-19T17:30-07:00:00.00000",
+ "2021-08-19T17:30-07:00:00.000001",
+ "2021-08-19T17:30-07:00:00.000000",
+ "2021-08-19T17:30-07:00:00.0000001",
+ "2021-08-19T17:30-07:00:00.0000000",
+ "2021-08-19T17:30-07:00:00.00000001",
+ "2021-08-19T17:30-07:00:00.00000000",
+ "2021-08-19T17:30-07:00:00.000000001",
+ "2021-08-19T17:30-07:00:00.000000000",
+].forEach((timeZone) => {
+ assert.throws(
+ RangeError,
+ () => instance.toZonedDateTime(timeZone),
+ `ISO string ${timeZone} with a sub-minute offset is not a valid time zone`
+ );
+});
+
+timeZone = "2021-08-19T17:30Z";
+const result1 = instance.toZonedDateTime(timeZone);
+assert.sameValue(result1.timeZoneId, "UTC", "date-time + Z is UTC time zone");
+
+timeZone = "2021-08-19T17:30-07:00";
+const result2 = instance.toZonedDateTime(timeZone);
+assert.sameValue(result2.timeZoneId, "-07:00", "date-time + offset is the offset time zone");
+
+timeZone = "2021-08-19T17:30[UTC]";
+const result3 = instance.toZonedDateTime(timeZone);
+assert.sameValue(result3.timeZoneId, "UTC", "date-time + IANA annotation is the IANA time zone");
+
+timeZone = "2021-08-19T17:30Z[UTC]";
+const result4 = instance.toZonedDateTime(timeZone);
+assert.sameValue(result4.timeZoneId, "UTC", "date-time + Z + IANA annotation is the IANA time zone");
+
+timeZone = "2021-08-19T17:30-07:00[UTC]";
+const result5 = instance.toZonedDateTime(timeZone);
+assert.sameValue(result5.timeZoneId, "UTC", "date-time + offset + IANA annotation is the IANA time zone");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-string-leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-string-leap-second.js
new file mode 100644
index 0000000000..ee9b23f844
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-string-leap-second.js
@@ -0,0 +1,20 @@
+// |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.plaindate.prototype.tozoneddatetime
+description: Leap second is a valid ISO string for TimeZone
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+let timeZone = "2016-12-31T23:59:60+00:00[UTC]";
+
+const result = instance.toZonedDateTime(timeZone);
+assert.sameValue(result.timeZoneId, "UTC", "leap second is a valid ISO string for TimeZone");
+
+timeZone = "2021-08-19T17:30:45.123456789+23:59[+23:59:60]";
+assert.throws(RangeError, () => instance.toZonedDateTime(timeZone), "leap second in time zone name not valid");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-string-multiple-offsets.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-string-multiple-offsets.js
new file mode 100644
index 0000000000..144bf2d184
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-string-multiple-offsets.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.plaindate.prototype.tozoneddatetime
+description: Time zone parsing from ISO strings uses the bracketed offset, not the ISO string offset
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+const timeZone = "2021-08-19T17:30:45.123456789-12:12[+01:46]";
+
+const result = instance.toZonedDateTime(timeZone);
+assert.sameValue(result.timeZoneId, "+01:46", "Time zone string determined from bracket name");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-string-year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-string-year-zero.js
new file mode 100644
index 0000000000..f17dae299b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-string-year-zero.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.plaindate.prototype.tozoneddatetime
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+const instance = new Temporal.PlainDate(2000, 5, 2);
+invalidStrings.forEach((timeZone) => {
+ assert.throws(
+ RangeError,
+ () => instance.toZonedDateTime(timeZone),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-string.js
new file mode 100644
index 0000000000..80efaabacc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-string.js
@@ -0,0 +1,39 @@
+// |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.plaindate.prototype.tozoneddatetime
+description: Time zone IDs are valid input for a time zone
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getPossibleInstantsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getPossibleInstantsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be looked up");
+ },
+});
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+["UTC", "+01:30"].forEach((timeZone) => {
+ const result = instance.toZonedDateTime(timeZone);
+ assert.sameValue(result.getISOFields().timeZone, timeZone, `time zone slot should store string "${timeZone}"`);
+});
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", getPossibleInstantsForOriginal);
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-wrong-type.js
new file mode 100644
index 0000000000..88b2ae55d4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-wrong-type.js
@@ -0,0 +1,42 @@
+// |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.plaindate.prototype.tozoneddatetime
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or object for TimeZone
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [19761118, "number that would convert to a valid ISO string in other contexts"],
+ [1n, "bigint"],
+];
+
+for (const [timeZone, description] of primitiveTests) {
+ assert.throws(
+ typeof timeZone === 'string' ? RangeError : TypeError,
+ () => instance.toZonedDateTime(timeZone),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "object not implementing time zone protocol"],
+ [new Temporal.Calendar("iso8601"), "calendar instance"],
+];
+
+for (const [timeZone, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.toZonedDateTime(timeZone), `${description} is not a valid object and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/year-zero.js
new file mode 100644
index 0000000000..99b5545515
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/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.plaindate.prototype.tozoneddatetime
+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.PlainDate(2000, 5, 2);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.toZonedDateTime({ plainTime: arg, timeZone: "UTC" }),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..65a50eeeb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-builtin-calendar-no-array-iteration.js
@@ -0,0 +1,24 @@
+// |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.plaindate.prototype.until
+description: >
+ Calling the method with a property bag argument with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+const arg = { year: 2000, month: 5, day: 2, calendar: "iso8601" };
+instance.until(arg);
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-calendar-datefromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..81df965c93
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-calendar-datefromfields-called-with-null-prototype-fields.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.plaindate.prototype.until
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.PlainDate(2000, 5, 2);
+const arg = { year: 2000, month: 5, day: 2, calendar };
+instance.until(arg);
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..11d0874d9f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-constructor-in-calendar-fields.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.plaindate.prototype.until
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+assert.throws(RangeError, () => instance.until(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-duplicate-calendar-fields.js
new file mode 100644
index 0000000000..ef00260ae3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-duplicate-calendar-fields.js
@@ -0,0 +1,19 @@
+// |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.plaindate.prototype.until
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['day'], ['month'], ['monthCode'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar };
+ const instance = new Temporal.PlainDate(2000, 5, 2);
+
+ assert.throws(RangeError, () => instance.until(arg));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-leap-second.js
new file mode 100644
index 0000000000..e62b69de21
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-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.plaindate.prototype.until
+description: Leap second is a valid ISO string for PlainDate
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2016, 12, 31);
+
+let arg = "2016-12-31T23:59:60";
+const result1 = instance.until(arg);
+TemporalHelpers.assertDuration(
+ result1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "leap second is a valid ISO string for PlainDate"
+);
+
+arg = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+const result2 = instance.until(arg);
+TemporalHelpers.assertDuration(
+ result2,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "second: 60 is ignored in property bag for PlainDate"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-number.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-number.js
new file mode 100644
index 0000000000..c181972581
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/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.plaindate.prototype.until
+description: A number cannot be used in place of a Temporal.PlainDate
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(1976, 11, 18);
+
+const numbers = [
+ 1,
+ 19761118,
+ -19761118,
+ 1234567890,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => instance.until(arg),
+ 'Numbers cannot be used in place of an ISO string for PlainDate'
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-plaindatetime.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-plaindatetime.js
new file mode 100644
index 0000000000..8dfa1b88ad
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-plaindatetime.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.plaindate.until
+description: Fast path for converting Temporal.PlainDateTime to Temporal.PlainDate by reading internal slots
+info: |
+ sec-temporal.plaindate.prototype.until step 3:
+ 3. Set _other_ to ? ToTemporalDate(_other_).
+ sec-temporal-totemporaldate step 2.b:
+ b. If _item_ has an [[InitializedTemporalDateTime]] internal slot, then
+ i. Return ! CreateTemporalDate(_item_.[[ISOYear]], _item_.[[ISOMonth]], _item_.[[ISODay]], _item_.[[Calendar]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkPlainDateTimeConversionFastPath((datetime) => {
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ const result = date.until(datetime);
+ assert.sameValue(result.total({ unit: "nanoseconds" }), 0, "time part dropped");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-propertybag-calendar-case-insensitive.js
new file mode 100644
index 0000000000..c3a029272a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-propertybag-calendar-case-insensitive.js
@@ -0,0 +1,20 @@
+// |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.plaindate.prototype.until
+description: The calendar name is case-insensitive
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(1976, 11, 18);
+
+const calendar = "IsO8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.until(arg);
+TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-propertybag-calendar-leap-second.js
new file mode 100644
index 0000000000..a05da04f05
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-propertybag-calendar-leap-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.plaindate.prototype.until
+description: Leap second is a valid ISO string for a calendar in a property bag
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(1976, 11, 18);
+
+const calendar = "2016-12-31T23:59:60";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.until(arg);
+TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "leap second is a valid ISO string for calendar"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-propertybag-calendar-number.js
new file mode 100644
index 0000000000..c46e6ff2ec
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-propertybag-calendar-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.plaindate.prototype.until
+description: A number as calendar in a property bag is not accepted
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(1976, 11, 18);
+
+const numbers = [
+ 1,
+ 19970327,
+ -19970327,
+ 1234567890,
+];
+for (const calendar of numbers) {
+ const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+ assert.throws(
+ TypeError,
+ () => instance.until(arg),
+ "Numbers cannot be used as a calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-propertybag-calendar-string.js
new file mode 100644
index 0000000000..2dddc518d7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-propertybag-calendar-string.js
@@ -0,0 +1,20 @@
+// |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.plaindate.prototype.until
+description: A calendar ID is valid input for Calendar
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(1976, 11, 18);
+
+const calendar = "iso8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.until(arg);
+TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, `Calendar created from string "${calendar}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-propertybag-calendar-wrong-type.js
new file mode 100644
index 0000000000..65304590aa
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-propertybag-calendar-wrong-type.js
@@ -0,0 +1,46 @@
+// |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.plaindate.prototype.until
+description: >
+ Appropriate error thrown when a calendar property from a property bag cannot
+ be converted to a calendar object or string
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [calendar, description] of primitiveTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ typeof calendar === 'string' ? RangeError : TypeError,
+ () => instance.until(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+ [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields()
+];
+
+for (const [calendar, description] of typeErrorTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(TypeError, () => instance.until(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/PlainDate/prototype/until/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-propertybag-calendar-year-zero.js
new file mode 100644
index 0000000000..abe100487e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-propertybag-calendar-year-zero.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.plaindate.prototype.until
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T17:45",
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+01:00",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+const instance = new Temporal.PlainDate(2000, 5, 2);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.until(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-proto-in-calendar-fields.js
new file mode 100644
index 0000000000..01c0f901aa
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-proto-in-calendar-fields.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.plaindate.prototype.until
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+assert.throws(RangeError, () => instance.until(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..a19cb0caff
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-calendar-annotation.js
@@ -0,0 +1,34 @@
+// |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.plaindate.prototype.until
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2000-05-02[u-ca=iso8601]", "without time or time zone"],
+ ["2000-05-02[UTC][u-ca=iso8601]", "with time zone and no time"],
+ ["2000-05-02T15:23[u-ca=iso8601]", "without time zone"],
+ ["2000-05-02T15:23[UTC][u-ca=iso8601]", "with time zone"],
+ ["2000-05-02T15:23[!u-ca=iso8601]", "with ! and no time zone"],
+ ["2000-05-02T15:23[UTC][!u-ca=iso8601]", "with ! and time zone"],
+ ["2000-05-02T15:23[u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.until(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/PlainDate/prototype/until/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..ac10df4a67
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-critical-unknown-annotation.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.plaindate.prototype.until
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[!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.PlainDate(2000, 5, 2);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.until(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..06692bd6b3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-date-with-utc-offset.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.plaindate.prototype.until
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+const validStrings = [
+ "2000-05-02T00+00:00",
+ "2000-05-02T00+00:00[UTC]",
+ "2000-05-02T00+00:00[!UTC]",
+ "2000-05-02T00-02:30[America/St_Johns]",
+];
+
+for (const arg of validStrings) {
+ const result = instance.until(arg);
+
+ TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ `"${arg}" is a valid UTC offset with time for PlainDate`
+ );
+}
+
+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.until(arg),
+ `"${arg}" UTC offset without time is not valid for PlainDate`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-invalid.js
new file mode 100644
index 0000000000..47c10e5c58
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-invalid.js
@@ -0,0 +1,64 @@
+// |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.plaindate.prototype.until
+description: >
+ RangeError thrown if an invalid ISO string (or syntactically valid ISO string
+ that is not supported) is used as a PlainDate
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ // invalid ISO strings:
+ "",
+ "invalid iso8601",
+ "2020-01-00",
+ "2020-01-32",
+ "2020-02-30",
+ "2021-02-29",
+ "2020-00-01",
+ "2020-13-01",
+ "2020-01-01T",
+ "2020-01-01T25:00:00",
+ "2020-01-01T01:60:00",
+ "2020-01-01T01:60:61",
+ "2020-01-01junk",
+ "2020-01-01T00:00:00junk",
+ "2020-01-01T00:00:00+00:00junk",
+ "2020-01-01T00:00:00+00:00[UTC]junk",
+ "2020-01-01T00:00:00+00:00[UTC][u-ca=iso8601]junk",
+ "02020-01-01",
+ "2020-001-01",
+ "2020-01-001",
+ "2020-01-01T001",
+ "2020-01-01T01:001",
+ "2020-01-01T01:01:001",
+ // valid, but forms not supported in Temporal:
+ "2020-W01-1",
+ "2020-001",
+ "+0002020-01-01",
+ // valid, but this calendar must not exist:
+ "2020-01-01[u-ca=notexist]",
+ // may be valid in other contexts, but insufficient information for PlainDate:
+ "2020-01",
+ "+002020-01",
+ "01-01",
+ "2020-W01",
+ "P1Y",
+ "-P12Y",
+ // valid, but outside the supported range:
+ "-999999-01-01",
+ "+999999-01-01",
+];
+const instance = new Temporal.PlainDate(2000, 5, 2);
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => instance.until(arg),
+ `"${arg}" should not be a valid ISO string for a PlainDate`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..db7f9127f1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/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.plaindate.prototype.until
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[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.PlainDate(2000, 5, 2);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.until(arg),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..1ec82cadc5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-multiple-time-zone.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.plaindate.prototype.until
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[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.PlainDate(2000, 5, 2);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.until(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-time-separators.js
new file mode 100644
index 0000000000..e9c944cc64
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-time-separators.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.plaindate.prototype.until
+description: Time separator in string argument can vary
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2000-05-02T15:23", "uppercase T"],
+ ["2000-05-02t15:23", "lowercase T"],
+ ["2000-05-02 15:23", "space between date and time"],
+];
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.until(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/PlainDate/prototype/until/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..f8cdb4306e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-time-zone-annotation.js
@@ -0,0 +1,39 @@
+// |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.plaindate.prototype.until
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2000-05-02[Asia/Kolkata]", "named, with no time"],
+ ["2000-05-02[!Europe/Vienna]", "named, with ! and no time"],
+ ["2000-05-02[+00:00]", "numeric, with no time"],
+ ["2000-05-02[!-02:30]", "numeric, with ! and no time"],
+ ["2000-05-02T15:23[America/Sao_Paulo]", "named, with no offset"],
+ ["2000-05-02T15:23[!Asia/Tokyo]", "named, with ! and no offset"],
+ ["2000-05-02T15:23[-02:30]", "numeric, with no offset"],
+ ["2000-05-02T15:23[!+00:00]", "numeric, with ! and no offset"],
+ ["2000-05-02T15:23+00:00[America/New_York]", "named, with offset"],
+ ["2000-05-02T15:23+00:00[!UTC]", "named, with offset and !"],
+ ["2000-05-02T15:23+00:00[+01:00]", "numeric, with offset"],
+ ["2000-05-02T15:23+00:00[!-08:00]", "numeric, with offset and !"],
+];
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.until(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/PlainDate/prototype/until/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..c8b5af5c6f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-unknown-annotation.js
@@ -0,0 +1,33 @@
+// |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.plaindate.prototype.until
+description: Various forms of unknown annotation
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2000-05-02[foo=bar]", "without time"],
+ ["2000-05-02T15:23[foo=bar]", "alone"],
+ ["2000-05-02T15:23[UTC][foo=bar]", "with time zone"],
+ ["2000-05-02T15:23[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["2000-05-02T15:23[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["2000-05-02T15:23[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.until(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/PlainDate/prototype/until/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..95f193aed7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-with-utc-designator.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.plaindate.prototype.until
+description: RangeError thrown if a string with UTC designator is used as a PlainDate
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "2019-10-01T09:00:00Z",
+ "2019-10-01T09:00:00Z[UTC]",
+];
+const instance = new Temporal.PlainDate(2000, 5, 2);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.until(arg),
+ "String with UTC designator should not be valid as a PlainDate"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-wrong-type.js
new file mode 100644
index 0000000000..d6886c8c7d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/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.plaindate.prototype.until
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainDate
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+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.until(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.PlainDate, "Temporal.PlainDate, object"],
+ [Temporal.PlainDate.prototype, "Temporal.PlainDate.prototype, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.until(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/PlainDate/prototype/until/argument-zoneddatetime-convert.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-zoneddatetime-convert.js
new file mode 100644
index 0000000000..807b83051b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-zoneddatetime-convert.js
@@ -0,0 +1,22 @@
+// |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.plaindate.prototype.until
+description: An exception from TimeZone#getOffsetNanosecondsFor() is propagated.
+features: [Temporal]
+---*/
+
+class TZ extends Temporal.TimeZone {
+ constructor() { super("UTC") }
+ getOffsetNanosecondsFor() { throw new Test262Error() }
+}
+
+const tz = new TZ();
+const arg = new Temporal.ZonedDateTime(0n, tz);
+const instance = new Temporal.PlainDate(1976, 11, 18);
+
+assert.throws(Test262Error, () => instance.until(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-zoneddatetime-slots.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-zoneddatetime-slots.js
new file mode 100644
index 0000000000..7777ce9c05
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-zoneddatetime-slots.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.plaindate.prototype.until
+description: Getters are not called when converting a ZonedDateTime to a PlainDate.
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.ZonedDateTime.prototype);
+const getters = ["year", "month", "monthCode", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond", "calendar"];
+
+for (const property of getters) {
+ Object.defineProperty(Temporal.ZonedDateTime.prototype, property, {
+ get() {
+ actual.push(`get ${property}`);
+ const value = prototypeDescrs[property].get.call(this);
+ return {
+ toString() {
+ actual.push(`toString ${property}`);
+ return value.toString();
+ },
+ valueOf() {
+ actual.push(`valueOf ${property}`);
+ return value;
+ },
+ };
+ },
+ });
+}
+
+const arg = new Temporal.ZonedDateTime(0n, "UTC");
+const instance = new Temporal.PlainDate(1976, 11, 18);
+instance.until(arg);
+assert.compareArray(actual, []);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..cc57645903
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/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.plaindate.prototype.until
+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 date = new Temporal.PlainDate(2000, 5, 2);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => date.until(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..12ee440503
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/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.plaindate.prototype.until
+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 date = new Temporal.PlainDate(2000, 5, 2);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => date.until(datetime),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..31bf9a778f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/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.plaindate.prototype.until
+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 date = new Temporal.PlainDate(2000, 5, 2);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => date.until(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..5bdc156026
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/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.plaindate.prototype.until
+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 date = new Temporal.PlainDate(2000, 5, 2);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => date.until(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/basic.js
new file mode 100644
index 0000000000..d2166e9e3e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/basic.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.plaindate.prototype.until
+description: Basic tests for until().
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainDate = new Temporal.PlainDate(1969, 7, 24);
+const plainDate2 = Temporal.PlainDate.from({ year: 1969, month: 10, day: 5 });
+TemporalHelpers.assertDuration(plainDate.until(plainDate2), 0, 0, 0, /* days = */ 73, 0, 0, 0, 0, 0, 0, "same year");
+
+const earlier = new Temporal.PlainDate(1969, 7, 24);
+const later = new Temporal.PlainDate(1996, 3, 3);
+TemporalHelpers.assertDuration(earlier.until(later), 0, 0, 0, /* days = */ 9719, 0, 0, 0, 0, 0, 0, "different year");
+
+TemporalHelpers.assertDuration(plainDate.until({ year: 2019, month: 7, day: 24 }), 0, 0, 0, /* days = */ 18262, 0, 0, 0, 0, 0, 0, "option bag");
+TemporalHelpers.assertDuration(plainDate.until("2019-07-24"), 0, 0, 0, /* days = */ 18262, 0, 0, 0, 0, 0, 0, "string");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/branding.js
new file mode 100644
index 0000000000..2625e235f3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/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.plaindate.prototype.until
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const until = Temporal.PlainDate.prototype.until;
+
+assert.sameValue(typeof until, "function");
+
+const args = [new Temporal.PlainDate(2022, 6, 22)];
+
+assert.throws(TypeError, () => until.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => until.apply(null, args), "null");
+assert.throws(TypeError, () => until.apply(true, args), "true");
+assert.throws(TypeError, () => until.apply("", args), "empty string");
+assert.throws(TypeError, () => until.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => until.apply(1, args), "1");
+assert.throws(TypeError, () => until.apply({}, args), "plain object");
+assert.throws(TypeError, () => until.apply(Temporal.PlainDate, args), "Temporal.PlainDate");
+assert.throws(TypeError, () => until.apply(Temporal.PlainDate.prototype, args), "Temporal.PlainDate.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..400f425860
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plaindate.prototype.until
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dateUntilOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dateUntil");
+Object.defineProperty(Temporal.Calendar.prototype, "dateUntil", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("dateUntil should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDate(2000, 5, 2, "iso8601");
+instance.until(new Temporal.PlainDate(2001, 6, 13));
+
+Object.defineProperty(Temporal.Calendar.prototype, "dateUntil", dateUntilOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/builtin.js
new file mode 100644
index 0000000000..d65f2e5af7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/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.plaindate.prototype.until
+description: >
+ Tests that Temporal.PlainDate.prototype.until
+ 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.PlainDate.prototype.until),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDate.prototype.until),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDate.prototype.until),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDate.prototype.until.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-dateadd-called-with-plaindate-instance.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-dateadd-called-with-plaindate-instance.js
new file mode 100644
index 0000000000..6b72eabd28
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-dateadd-called-with-plaindate-instance.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.plaindate.prototype.until
+description: >
+ relativeTo parameters that are not ZonedDateTime or undefined, are always
+ converted to PlainDate for observable calendar calls
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarDateAddPlainDateInstance();
+const instance = new Temporal.PlainDate(1970, 1, 1, calendar);
+calendar.specificPlainDate = instance;
+instance.until(new Temporal.PlainDate(2000, 5, 2, calendar), { smallestUnit: "month" });
+assert(calendar.dateAddCallCount > 0, "assertions in calendar.dateAdd() should have been tested");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-datefromfields-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-datefromfields-called-with-options-undefined.js
new file mode 100644
index 0000000000..c4455c2f1e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-datefromfields-called-with-options-undefined.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.plaindate.prototype.until
+description: >
+ Calendar.dateFromFields method is called with undefined as the options value
+ when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
+const instance = new Temporal.PlainDate(2000, 5, 2, calendar);
+instance.until({ year: 2000, month: 5, day: 3, calendar });
+assert.sameValue(calendar.dateFromFieldsCallCount, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-dateuntil-called-with-null-prototype-options.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-dateuntil-called-with-null-prototype-options.js
new file mode 100644
index 0000000000..2945cae16d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-dateuntil-called-with-null-prototype-options.js
@@ -0,0 +1,20 @@
+// |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.plaindate.prototype.until
+description: >
+ Calendar.dateUntil method is called with a null-prototype object as the
+ options value when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckOptionsPrototypePollution();
+const instance = new Temporal.PlainDate(2000, 5, 2, calendar);
+const argument = new Temporal.PlainDate(2022, 6, 14, calendar);
+instance.until(argument, { largestUnit: "months" });
+assert.sameValue(calendar.dateUntilCallCount, 1, "dateUntil should have been called on the calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-dateuntil-called-with-singular-largestunit.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-dateuntil-called-with-singular-largestunit.js
new file mode 100644
index 0000000000..7a7f9d92f2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-dateuntil-called-with-singular-largestunit.js
@@ -0,0 +1,30 @@
+// |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.plaindate.prototype.until
+description: The options object passed to calendar.dateUntil has a largestUnit property with its value in the singular form
+info: |
+ sec-temporal.plaindate.prototype.until steps 12–13:
+ 13. Let _untilOptions_ be ? MergeLargestUnitOption(_options_, _largestUnit_).
+ 14. Let _result_ be ? CalendarDateUntil(_temporalDate_.[[Calendar]], _temporalDate_, _other_, _untilOptions_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkCalendarDateUntilLargestUnitSingular(
+ (calendar, largestUnit) => {
+ const earlier = new Temporal.PlainDate(2000, 5, 2, calendar);
+ const later = new Temporal.PlainDate(2001, 6, 3, calendar);
+ earlier.until(later, { largestUnit });
+ },
+ {
+ years: ["year"],
+ months: ["month"],
+ weeks: ["week"],
+ days: []
+ }
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-fields-iterable.js
new file mode 100644
index 0000000000..b77217dd0c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-fields-iterable.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.plaindate.prototype.until
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plaindate.prototype.until step 3:
+ 3. Set _other_ to ? ToTemporalDate(_other_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const date = new Temporal.PlainDate(2000, 5, 2, calendar1);
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+date.until({ year: 2005, month: 6, day: 2, calendar: calendar2 });
+
+assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-id-match.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-id-match.js
new file mode 100644
index 0000000000..ddff8dae6c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-id-match.js
@@ -0,0 +1,33 @@
+// |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.plaindate.prototype.until
+description: Calculation is performed if calendars' toString results match
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+class Calendar1 extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ toString() {
+ return "A";
+ }
+}
+class Calendar2 extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ toString() {
+ return "A";
+ }
+}
+
+const plainDate1 = new Temporal.PlainDate(2000, 1, 1, new Calendar1());
+const plainDate2 = new Temporal.PlainDate(2000, 1, 2, new Calendar2());
+TemporalHelpers.assertDuration(plainDate1.until(plainDate2), 0, 0, 0, /* days = */ 1, 0, 0, 0, 0, 0, 0);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-invalid-return.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-invalid-return.js
new file mode 100644
index 0000000000..632e9d69e3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-invalid-return.js
@@ -0,0 +1,42 @@
+// |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.plaindate.prototype.until
+description: Throw when the returned value from the calendar's dateUntil method is not a Duration.
+features: [Temporal]
+---*/
+
+class CustomCalendar extends Temporal.Calendar {
+ constructor(value) {
+ super("iso8601");
+ this.value = value;
+ }
+ dateUntil() {
+ return this.value;
+ }
+}
+
+const tests = [
+ [undefined],
+ [null, "null"],
+ [true],
+ ["2000-05"],
+ [Symbol()],
+ [200005],
+ [200005n],
+ [{}, "plain object"],
+ [() => {}, "lambda"],
+ [Temporal.Duration, "Temporal.Duration"],
+ [Temporal.Duration.prototype, "Temporal.Duration.prototype"],
+];
+for (const [test, description = typeof test] of tests) {
+ const plainDate = new Temporal.PlainDate(2000, 5, 2, new CustomCalendar(test));
+ assert.throws(
+ TypeError, () => plainDate.until("2022-06-20", { largestUnit: "years" }),
+ `Expected error with ${description}`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-mismatch.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-mismatch.js
new file mode 100644
index 0000000000..65be788ce3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-mismatch.js
@@ -0,0 +1,62 @@
+// |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.plaindate.prototype.until
+description: RangeError thrown if calendars' id properties do not match
+features: [Temporal]
+---*/
+
+const calendar1 = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "A",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+const calendar2 = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "B",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+
+const plainDate1 = new Temporal.PlainDate(2000, 1, 1, calendar1);
+const plainDate2 = new Temporal.PlainDate(2000, 1, 1, calendar2);
+assert.throws(RangeError, () => plainDate1.until(plainDate2));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-temporal-object.js
new file mode 100644
index 0000000000..4f03a0b94d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/calendar-temporal-object.js
@@ -0,0 +1,29 @@
+// |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.plaindate.prototype.until
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plaindate.prototype.until step 3:
+ 3. Set _other_ to ? ToTemporalDate(_other_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const date = new Temporal.PlainDate(2000, 5, 2, temporalObject);
+ date.until({ year: 2005, month: 6, day: 2, calendar: temporalObject });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/custom.js
new file mode 100644
index 0000000000..c2d9d2cb82
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/custom.js
@@ -0,0 +1,35 @@
+// |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.plaindate.prototype.until
+description: Basic tests with custom calendar
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const result = new Temporal.Duration(1, 3, 5, 7, 9);
+const options = { largestUnit: "years" };
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ dateUntil(...args) {
+ ++calls;
+ assert.sameValue(args.length, 3, "Three arguments");
+ assert.sameValue(args[0], plainDate, "First argument");
+ assert.sameValue(args[1], other, "Second argument");
+ assert.sameValue(args[2].largestUnit, "year", "Third argument: largestUnit");
+ return result;
+ }
+}
+const calendar = new CustomCalendar();
+const plainDate = new Temporal.PlainDate(1976, 11, 18, calendar);
+const other = new Temporal.PlainDate(2022, 6, 20, calendar);
+TemporalHelpers.assertDuration(plainDate.until(other, options),
+ 1, 3, 5, 7, 0, 0, 0, 0, 0, 0, "result");
+assert.sameValue(calls, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/days-in-month.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/days-in-month.js
new file mode 100644
index 0000000000..72ff38ca21
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/days-in-month.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.plaindate.prototype.until
+description: until() should take length of month into account.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainDate1 = Temporal.PlainDate.from("2019-01-01");
+const plainDate2 = Temporal.PlainDate.from("2019-02-01");
+const plainDate3 = Temporal.PlainDate.from("2019-03-01");
+TemporalHelpers.assertDuration(plainDate1.until(plainDate2), 0, 0, 0, /* days = */ 31, 0, 0, 0, 0, 0, 0, "January 2019");
+TemporalHelpers.assertDuration(plainDate2.until(plainDate3), 0, 0, 0, /* days = */ 28, 0, 0, 0, 0, 0, 0, "February 2019");
+
+const plainDate4 = Temporal.PlainDate.from("2020-02-01");
+const plainDate5 = Temporal.PlainDate.from("2020-03-01");
+TemporalHelpers.assertDuration(plainDate4.until(plainDate5), 0, 0, 0, /* days = */ 29, 0, 0, 0, 0, 0, 0, "February 2020");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/days-in-year.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/days-in-year.js
new file mode 100644
index 0000000000..6ca8c59327
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/days-in-year.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.plaindate.prototype.until
+description: until() should take length of year into account.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainDate1 = Temporal.PlainDate.from("2019-01-01");
+const plainDate2 = Temporal.PlainDate.from("2020-01-01");
+const plainDate3 = Temporal.PlainDate.from("2021-01-01");
+TemporalHelpers.assertDuration(plainDate1.until(plainDate2), 0, 0, 0, /* days = */ 365, 0, 0, 0, 0, 0, 0, "From January 2019");
+TemporalHelpers.assertDuration(plainDate2.until(plainDate3), 0, 0, 0, /* days = */ 366, 0, 0, 0, 0, 0, 0, "From January 2020");
+
+const plainDate4 = Temporal.PlainDate.from("2019-06-01");
+const plainDate5 = Temporal.PlainDate.from("2020-06-01");
+const plainDate6 = Temporal.PlainDate.from("2021-06-01");
+TemporalHelpers.assertDuration(plainDate4.until(plainDate5), 0, 0, 0, /* days = */ 366, 0, 0, 0, 0, 0, 0, "From June 2019");
+TemporalHelpers.assertDuration(plainDate5.until(plainDate6), 0, 0, 0, /* days = */ 365, 0, 0, 0, 0, 0, 0, "From June 2020");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..6bd0c01f29
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/infinity-throws-rangeerror.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.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plaindate.prototype.until
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+const base = { year: 2000, month: 5, day: 2 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.until({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.until({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/largestunit-default.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/largestunit-default.js
new file mode 100644
index 0000000000..907df1f977
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/largestunit-default.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.plaindate.prototype.until
+description: Default value for largestUnit option is days
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const feb20 = Temporal.PlainDate.from("2020-02-01");
+const feb21 = Temporal.PlainDate.from("2021-02-01");
+TemporalHelpers.assertDuration(feb20.until(feb21), 0, 0, 0, /* days = */ 366, 0, 0, 0, 0, 0, 0, "no options");
+TemporalHelpers.assertDuration(feb20.until(feb21, undefined), 0, 0, 0, /* days = */ 366, 0, 0, 0, 0, 0, 0, "undefined options");
+TemporalHelpers.assertDuration(feb20.until(feb21, {}), 0, 0, 0, /* days = */ 366, 0, 0, 0, 0, 0, 0, "no largestUnit");
+TemporalHelpers.assertDuration(feb20.until(feb21, { largestUnit: undefined }), 0, 0, 0, /* days = */ 366, 0, 0, 0, 0, 0, 0, "undefined largestUnit");
+TemporalHelpers.assertDuration(feb20.until(feb21, { largestUnit: "days" }), 0, 0, 0, /* days = */ 366, 0, 0, 0, 0, 0, 0, "days");
+TemporalHelpers.assertDuration(feb20.until(feb21, { largestUnit: "auto" }), 0, 0, 0, /* days = */ 366, 0, 0, 0, 0, 0, 0, "auto");
+TemporalHelpers.assertDuration(feb20.until(feb21, () => {}), 0, 0, 0, /* days = */ 366, 0, 0, 0, 0, 0, 0, "no largestUnit (function)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/largestunit-higher-units.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/largestunit-higher-units.js
new file mode 100644
index 0000000000..a8f8812d9d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/largestunit-higher-units.js
@@ -0,0 +1,29 @@
+// |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.plaindate.prototype.until
+description: Tests calculations with higher largestUnit than the default of 'days'
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(1969, 7, 24);
+const later = Temporal.PlainDate.from({ year: 2019, month: 7, day: 24 });
+const duration = date.until(later, { largestUnit: "years" });
+TemporalHelpers.assertDuration(duration, /* years = */ 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, "crossing epoch");
+
+const feb20 = Temporal.PlainDate.from("2020-02-01");
+const feb21 = Temporal.PlainDate.from("2021-02-01");
+TemporalHelpers.assertDuration(feb20.until(feb21, { largestUnit: "years" }), /* years = */ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "start of February, years");
+TemporalHelpers.assertDuration(feb20.until(feb21, { largestUnit: "months" }), 0, /* months = */ 12, 0, 0, 0, 0, 0, 0, 0, 0, "start of February, months");
+TemporalHelpers.assertDuration(feb20.until(feb21, { largestUnit: "weeks" }), 0, 0, /* weeks = */ 52, /* days = */ 2, 0, 0, 0, 0, 0, 0, "start of February, weeks");
+
+const lastFeb20 = Temporal.PlainDate.from("2020-02-29");
+const lastFeb21 = Temporal.PlainDate.from("2021-02-28");
+TemporalHelpers.assertDuration(lastFeb20.until(lastFeb21, { largestUnit: "years" }), /* years = */ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "end of February, years");
+TemporalHelpers.assertDuration(lastFeb20.until(lastFeb21, { largestUnit: "months" }), 0, /* months = */ 12, 0, 0, 0, 0, 0, 0, 0, 0, "end of February, months");
+TemporalHelpers.assertDuration(lastFeb20.until(lastFeb21, { largestUnit: "weeks" }), 0, 0, /* weeks = */ 52, /* days = */ 1, 0, 0, 0, 0, 0, 0, "end of February, weeks");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/largestunit-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/largestunit-invalid-string.js
new file mode 100644
index 0000000000..145b8d3024
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/largestunit-invalid-string.js
@@ -0,0 +1,41 @@
+// |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.plaindate.prototype.until
+description: RangeError thrown when largestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2001, 6, 3);
+const badValues = [
+ "era",
+ "eraYear",
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+ "month\0",
+ "YEAR",
+ "eras",
+ "eraYears",
+ "hours",
+ "minutes",
+ "seconds",
+ "milliseconds",
+ "microseconds",
+ "nanoseconds",
+ "months\0",
+ "YEARS",
+ "other string"
+];
+for (const largestUnit of badValues) {
+ assert.throws(RangeError, () => earlier.until(later, { largestUnit }),
+ `"${largestUnit}" is not a valid value for largestUnit`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/largestunit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/largestunit-plurals-accepted.js
new file mode 100644
index 0000000000..84769e1877
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/largestunit-plurals-accepted.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.plaindate.prototype.until
+description: Plural units are accepted as well for the largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2001, 6, 12);
+const validUnits = [
+ "year",
+ "month",
+ "week",
+ "day",
+];
+TemporalHelpers.checkPluralUnitsAccepted((largestUnit) => earlier.until(later, { largestUnit }), validUnits);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/largestunit-smallestunit-mismatch.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/largestunit-smallestunit-mismatch.js
new file mode 100644
index 0000000000..91ba76b412
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/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.plaindate.prototype.until
+description: RangeError thrown when smallestUnit is larger than largestUnit
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2001, 6, 3);
+const units = ["years", "months", "weeks", "days"];
+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, () => earlier.until(later, { largestUnit, smallestUnit }));
+ }
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/largestunit-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/largestunit-undefined.js
new file mode 100644
index 0000000000..0b24449663
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/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.plaindate.prototype.until
+description: Fallback value for largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2001, 6, 3);
+
+const explicit = earlier.until(later, { largestUnit: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 397, 0, 0, 0, 0, 0, 0, "default largestUnit is day");
+const implicit = earlier.until(later, {});
+TemporalHelpers.assertDuration(implicit, 0, 0, 0, 397, 0, 0, 0, 0, 0, 0, "default largestUnit is day");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/largestunit-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/largestunit-wrong-type.js
new file mode 100644
index 0000000000..42d56c641e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/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.plaindate.prototype.until
+description: Type conversions for largestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2001, 6, 3);
+TemporalHelpers.checkStringOptionWrongType("largestUnit", "year",
+ (largestUnit) => earlier.until(later, { largestUnit }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/length.js
new file mode 100644
index 0000000000..31c204731d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/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.plaindate.prototype.until
+description: Temporal.PlainDate.prototype.until.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.PlainDate.prototype.until, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/name.js
new file mode 100644
index 0000000000..99b7ae2be1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/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.plaindate.prototype.until
+description: Temporal.PlainDate.prototype.until.name is "until".
+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.PlainDate.prototype.until, "name", {
+ value: "until",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/not-a-constructor.js
new file mode 100644
index 0000000000..2d4a6df0d0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/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.plaindate.prototype.until
+description: >
+ Temporal.PlainDate.prototype.until 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.PlainDate.prototype.until();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDate.prototype.until), false,
+ "isConstructor(Temporal.PlainDate.prototype.until)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/options-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/options-object.js
new file mode 100644
index 0000000000..978d5189f8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/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.plaindate.prototype.until
+description: Empty or a function object may be used as options
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+const result1 = instance.until(new Temporal.PlainDate(1976, 11, 18), {});
+TemporalHelpers.assertDuration(
+ result1, 0, 0, 0, -8566, 0, 0, 0, 0, 0, 0,
+ "options may be an empty plain object"
+);
+
+const result2 = instance.until(new Temporal.PlainDate(1976, 11, 18), () => {});
+TemporalHelpers.assertDuration(
+ result2, 0, 0, 0, -8566, 0, 0, 0, 0, 0, 0,
+ "options may be a function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/options-wrong-type.js
new file mode 100644
index 0000000000..dd67a3bd6b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/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.plaindate.prototype.until
+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.PlainDate(2000, 5, 2);
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.until(new Temporal.PlainDate(1976, 11, 18), value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/order-of-operations.js
new file mode 100644
index 0000000000..427670784a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/order-of-operations.js
@@ -0,0 +1,214 @@
+// |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.plaindate.prototype.until
+description: Properties on objects passed to until() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ // ToTemporalDate
+ "get other.calendar",
+ "has other.calendar.dateAdd",
+ "has other.calendar.dateFromFields",
+ "has other.calendar.dateUntil",
+ "has other.calendar.day",
+ "has other.calendar.dayOfWeek",
+ "has other.calendar.dayOfYear",
+ "has other.calendar.daysInMonth",
+ "has other.calendar.daysInWeek",
+ "has other.calendar.daysInYear",
+ "has other.calendar.fields",
+ "has other.calendar.id",
+ "has other.calendar.inLeapYear",
+ "has other.calendar.mergeFields",
+ "has other.calendar.month",
+ "has other.calendar.monthCode",
+ "has other.calendar.monthDayFromFields",
+ "has other.calendar.monthsInYear",
+ "has other.calendar.weekOfYear",
+ "has other.calendar.year",
+ "has other.calendar.yearMonthFromFields",
+ "has other.calendar.yearOfWeek",
+ "get other.calendar.dateFromFields",
+ "get other.calendar.fields",
+ "call other.calendar.fields",
+ "get other.day",
+ "get other.day.valueOf",
+ "call other.day.valueOf",
+ "get other.month",
+ "get other.month.valueOf",
+ "call other.month.valueOf",
+ "get other.monthCode",
+ "get other.monthCode.toString",
+ "call other.monthCode.toString",
+ "get other.year",
+ "get other.year.valueOf",
+ "call other.year.valueOf",
+ "call other.calendar.dateFromFields",
+ // CalendarEquals
+ "get this.calendar.id",
+ "get other.calendar.id",
+ // 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 ownCalendar = TemporalHelpers.calendarObserver(actual, "this.calendar");
+const instance = new Temporal.PlainDate(2000, 5, 2, ownCalendar);
+
+const otherDatePropertyBag = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2001,
+ month: 6,
+ monthCode: "M06",
+ day: 2,
+ calendar: TemporalHelpers.calendarObserver(actual, "other.calendar"),
+}, "other");
+
+function createOptionsObserver({ smallestUnit = "days", largestUnit = "auto", roundingMode = "halfExpand", roundingIncrement = 1 } = {}) {
+ return TemporalHelpers.propertyBagObserver(actual, {
+ // order is significant, due to iterating through properties in order to
+ // copy them to an internal null-prototype object:
+ roundingIncrement,
+ roundingMode,
+ largestUnit,
+ smallestUnit,
+ additional: "property",
+ }, "options");
+}
+
+// clear any observable things that happened while constructing the objects
+actual.splice(0);
+
+// basic order of observable operations with calendar call, without rounding:
+instance.until(otherDatePropertyBag, createOptionsObserver({ largestUnit: "years" }));
+assert.compareArray(actual, expected.concat([
+ // lookup
+ "get this.calendar.dateAdd",
+ "get this.calendar.dateUntil",
+ // CalendarDateUntil
+ "call this.calendar.dateUntil",
+]), "order of operations");
+actual.splice(0); // clear
+
+// short-circuit for identical objects:
+
+const identicalPropertyBag = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2000,
+ month: 5,
+ monthCode: "M05",
+ day: 2,
+ calendar: TemporalHelpers.calendarObserver(actual, "other.calendar"),
+}, "other");
+
+instance.since(identicalPropertyBag, createOptionsObserver());
+assert.compareArray(actual, expected, "order of operations with identical dates");
+actual.splice(0); // clear
+
+// code path through RoundDuration that rounds to the nearest year:
+const expectedOpsForYearRounding = expected.concat([
+ // lookup
+ "get this.calendar.dateAdd",
+ "get this.calendar.dateUntil",
+ // CalendarDateUntil
+ "call this.calendar.dateUntil",
+ // RoundDuration
+ "call this.calendar.dateAdd", // 12.d
+ "call this.calendar.dateAdd", // 12.f
+ "call this.calendar.dateUntil", // 12.n
+ "call this.calendar.dateAdd", // 12.x MoveRelativeDate
+ // (12.r not called because other units can't add up to >1 year at this point)
+ // BalanceDateDurationRelative
+ "call this.calendar.dateAdd", // 9.c
+ "call this.calendar.dateUntil" // 9.d
+]);
+instance.until(otherDatePropertyBag, createOptionsObserver({ smallestUnit: "years" }));
+assert.compareArray(actual, expectedOpsForYearRounding, "order of operations with smallestUnit = years");
+actual.splice(0); // clear
+
+// code path through RoundDuration that rounds to the nearest year and skips a DateUntil call:
+const otherDatePropertyBagSameMonth = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2001,
+ month: 5,
+ monthCode: "M05",
+ day: 2,
+ calendar: TemporalHelpers.calendarObserver(actual, "other.calendar"),
+}, "other");
+const expectedOpsForYearRoundingSameMonth = expected.concat([
+ // lookup
+ "get this.calendar.dateAdd",
+ "get this.calendar.dateUntil",
+ // CalendarDateUntil
+ "call this.calendar.dateUntil",
+ // RoundDuration
+ "call this.calendar.dateAdd", // 12.d
+ "call this.calendar.dateAdd", // 12.f
+ "call this.calendar.dateAdd", // 12.x MoveRelativeDate
+ // (12.n not called because months and weeks == 0)
+ // BalanceDateDurationRelative
+ "call this.calendar.dateAdd", // 9.c
+ "call this.calendar.dateUntil" // 9.d
+]);
+instance.until(otherDatePropertyBagSameMonth, createOptionsObserver({ smallestUnit: "years" }));
+assert.compareArray(actual, expectedOpsForYearRoundingSameMonth, "order of operations with smallestUnit = years and no excess months/weeks");
+actual.splice(0); // clear
+
+// code path through RoundDuration that rounds to the nearest month:
+const expectedOpsForMonthRounding = expected.concat([
+ // lookup
+ "get this.calendar.dateAdd",
+ "get this.calendar.dateUntil",
+ // CalendarDateUntil
+ "call this.calendar.dateUntil",
+ // RoundDuration
+ "call this.calendar.dateAdd", // 13.c
+ "call this.calendar.dateAdd", // 13.e
+ "call this.calendar.dateAdd", // 13.w MoveRelativeDate
+ // BalanceDateDurationRelative
+ "call this.calendar.dateAdd", // 10.d
+ "call this.calendar.dateUntil" // 10.e
+]);
+instance.until(otherDatePropertyBag, createOptionsObserver({ smallestUnit: "months" }));
+assert.compareArray(actual, expectedOpsForMonthRounding, "order of operations with smallestUnit = months");
+actual.splice(0); // clear
+
+// code path through RoundDuration that rounds to the nearest week:
+const expectedOpsForWeekRounding = expected.concat([
+ // lookup
+ "get this.calendar.dateAdd",
+ "get this.calendar.dateUntil",
+ // CalendarDateUntil
+ "call this.calendar.dateUntil",
+ // RoundDuration
+ "call this.calendar.dateUntil", // 14.f
+ "call this.calendar.dateAdd", // 14.p MoveRelativeDate
+ // BalanceDateDurationRelative
+ "call this.calendar.dateAdd", // 16
+ "call this.calendar.dateUntil" // 17
+]);
+instance.until(otherDatePropertyBag, createOptionsObserver({ smallestUnit: "weeks" }));
+assert.compareArray(actual, expectedOpsForWeekRounding, "order of operations with smallestUnit = weeks");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/prop-desc.js
new file mode 100644
index 0000000000..0c230a999e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/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.plaindate.prototype.until
+description: The "until" property of Temporal.PlainDate.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDate.prototype.until,
+ "function",
+ "`typeof PlainDate.prototype.until` is `function`"
+);
+
+verifyProperty(Temporal.PlainDate.prototype, "until", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/round-cross-unit-boundary.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/round-cross-unit-boundary.js
new file mode 100644
index 0000000000..6b15e24902
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/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.plaindate.prototype.until
+description: Rounding can cross unit boundaries up to largestUnit
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2022, 1, 1);
+const later = new Temporal.PlainDate(2023, 12, 25);
+const duration = earlier.until(later, { largestUnit: "years", smallestUnit: "months", roundingMode: "expand" });
+TemporalHelpers.assertDuration(duration, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, "1 year 11 months balances to 2 years");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/rounding-relative.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/rounding-relative.js
new file mode 100644
index 0000000000..b7b28c9338
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/rounding-relative.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.plaindate.prototype.until
+description: Should round relative to the receiver.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const date1 = Temporal.PlainDate.from("2019-01-01");
+const date2 = Temporal.PlainDate.from("2019-02-15");
+
+TemporalHelpers.assertDuration(
+ date1.until(date2, { smallestUnit: "months", roundingMode: "halfExpand" }),
+ 0, /* months = */ 2, 0, 0, 0, 0, 0, 0, 0, 0);
+TemporalHelpers.assertDuration(
+ date2.until(date1, { smallestUnit: "months", roundingMode: "halfExpand" }),
+ 0, /* months = */ -1, 0, 0, 0, 0, 0, 0, 0, 0);
+
+const cases = [
+ ["2019-03-01", "2019-01-29", 1, 1],
+ ["2019-01-29", "2019-03-01", -1, -3],
+ ["2019-03-29", "2019-01-30", 1, 29],
+ ["2019-01-30", "2019-03-29", -1, -29],
+ ["2019-03-30", "2019-01-31", 1, 30],
+ ["2019-01-31", "2019-03-30", -1, -28],
+ ["2019-03-31", "2019-01-31", 2, 0],
+ ["2019-01-31", "2019-03-31", -2, 0]
+];
+for (const [end, start, months, days] of cases) {
+ const result = Temporal.PlainDate.from(start).until(end, { largestUnit: "months" });
+ TemporalHelpers.assertDuration(result, 0, months, 0, days, 0, 0, 0, 0, 0, 0, `${end} - ${start}`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/rounding-zero-year-month-week-length.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/rounding-zero-year-month-week-length.js
new file mode 100644
index 0000000000..1d56c472c9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/rounding-zero-year-month-week-length.js
@@ -0,0 +1,34 @@
+// |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.plaindate.prototype.until
+description: >
+ A malicious calendar resulting in a year, month, or week length of zero is
+ handled correctly
+info: |
+ RoundDuration
+ 10.z. If _oneYearDays_ = 0, throw a *RangeError* exception.
+ ...
+ 11.z. If _oneMonthDays_ = 0, throw a *RangeError* exception.
+ ...
+ 12.s. If _oneWeekDays_ = 0, throw a *RangeError* exception.
+features: [Temporal]
+---*/
+
+const cal = new class extends Temporal.Calendar {
+ dateAdd(date, duration, options) {
+ // Called several times, last call sets oneYear/Month/WeekDays to 0
+ return new Temporal.PlainDate(1970, 1, 1);
+ }
+}("iso8601");
+
+const d1 = new Temporal.PlainDate(1970, 1, 1, cal);
+const d2 = new Temporal.PlainDate(1971, 1, 1, cal);
+
+assert.throws(RangeError, () => d1.until(d2, { smallestUnit: "years" }), "zero year length handled correctly");
+assert.throws(RangeError, () => d1.until(d2, { smallestUnit: "months" }), "zero month length handled correctly");
+assert.throws(RangeError, () => d1.until(d2, { smallestUnit: "weeks" }), "zero week length handled correctly");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingincrement-nan.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingincrement-nan.js
new file mode 100644
index 0000000000..7e68bdd813
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/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.plaindate.prototype.until
+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.plaindate.prototype.until step 11:
+ 11. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, *undefined*, *false*).
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2000, 5, 7);
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: NaN }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingincrement-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingincrement-non-integer.js
new file mode 100644
index 0000000000..349135ad08
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingincrement-non-integer.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.plaindate.prototype.until
+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.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2000, 5, 7);
+const result = earlier.until(later, { roundingIncrement: 2.5, roundingMode: "trunc" });
+TemporalHelpers.assertDuration(result, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, "roundingIncrement 2.5 truncates to 2");
+const result2 = earlier.until(later, { smallestUnit: "days", roundingIncrement: 1e9 + 0.5, roundingMode: "expand" });
+TemporalHelpers.assertDuration(result2, 0, 0, 0, 1e9, 0, 0, 0, 0, 0, 0, "roundingIncrement 1e9 + 0.5 truncates to 1e9");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingincrement-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingincrement-out-of-range.js
new file mode 100644
index 0000000000..26092686df
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/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.plaindate.prototype.until
+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.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2000, 5, 7);
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: -Infinity }));
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: -1 }));
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: 0 }));
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: 0.9 }));
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: 1e9 + 1 }));
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: Infinity }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingincrement-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingincrement-undefined.js
new file mode 100644
index 0000000000..7670614029
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingincrement-undefined.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.plaindate.prototype.until
+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.plaindate.prototype.until step 11:
+ 11. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, *undefined*, *false*).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2000, 5, 7);
+
+const explicit = earlier.until(later, { roundingIncrement: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, "default roundingIncrement is 1");
+
+// See options-undefined.js for {}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingincrement-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingincrement-wrong-type.js
new file mode 100644
index 0000000000..c2cc289fb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/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.plaindate.prototype.until
+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.plaindate.prototype.until step 11:
+ 11. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, *undefined*, *false*).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2000, 5, 7);
+
+TemporalHelpers.checkRoundingIncrementOptionWrongType(
+ (roundingIncrement) => earlier.until(later, { roundingIncrement }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, descr),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingincrement.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingincrement.js
new file mode 100644
index 0000000000..5a86e247c8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingincrement.js
@@ -0,0 +1,31 @@
+// |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.plaindate.prototype.until
+description: Tests calculations with roundingMode "trunc".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = Temporal.PlainDate.from("2019-01-08");
+const later = Temporal.PlainDate.from("2021-09-07");
+
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "years", roundingIncrement: 4, roundingMode: "halfExpand" }),
+ /* years = */ 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, "years");
+
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "months", roundingIncrement: 10, roundingMode: "halfExpand" }),
+ 0, /* months = */ 30, 0, 0, 0, 0, 0, 0, 0, 0, "months");
+
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "weeks", roundingIncrement: 12, roundingMode: "halfExpand" }),
+ 0, 0, /* weeks = */ 144, 0, 0, 0, 0, 0, 0, 0, "weeks");
+
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "days", roundingIncrement: 100, roundingMode: "halfExpand" }),
+ 0, 0, 0, /* days = */ 1000, 0, 0, 0, 0, 0, 0, "days");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-ceil.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-ceil.js
new file mode 100644
index 0000000000..fe40fdd5f1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-ceil.js
@@ -0,0 +1,39 @@
+// |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.plaindate.prototype.until
+description: Tests calculations with roundingMode "ceil".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2019, 1, 8);
+const later = new Temporal.PlainDate(2021, 9, 7);
+
+const expected = [
+ ["years", [3], [-2]],
+ ["months", [0, 32], [0, -31]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+];
+
+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(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { 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/PlainDate/prototype/until/roundingmode-expand.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-expand.js
new file mode 100644
index 0000000000..d3b3271b50
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-expand.js
@@ -0,0 +1,39 @@
+// |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.plaindate.prototype.until
+description: Tests calculations with roundingMode "expand".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2019, 1, 8);
+const later = new Temporal.PlainDate(2021, 9, 7);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [0, 32], [0, -32]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+];
+
+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(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { 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/PlainDate/prototype/until/roundingmode-floor.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-floor.js
new file mode 100644
index 0000000000..37c8a1c1a5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-floor.js
@@ -0,0 +1,39 @@
+// |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.plaindate.prototype.until
+description: Tests calculations with roundingMode "floor".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2019, 1, 8);
+const later = new Temporal.PlainDate(2021, 9, 7);
+
+const expected = [
+ ["years", [2], [-3]],
+ ["months", [0, 31], [0, -32]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+];
+
+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(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { 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/PlainDate/prototype/until/roundingmode-halfCeil.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-halfCeil.js
new file mode 100644
index 0000000000..55118dd705
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-halfCeil.js
@@ -0,0 +1,39 @@
+// |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.plaindate.prototype.until
+description: Tests calculations with roundingMode "halfCeil".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2019, 1, 8);
+const later = new Temporal.PlainDate(2021, 9, 7);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [0, 32], [0, -32]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+];
+
+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(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { 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/PlainDate/prototype/until/roundingmode-halfEven.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-halfEven.js
new file mode 100644
index 0000000000..3b42461cdb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-halfEven.js
@@ -0,0 +1,39 @@
+// |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.plaindate.prototype.until
+description: Tests calculations with roundingMode "halfEven".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2019, 1, 8);
+const later = new Temporal.PlainDate(2021, 9, 7);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [0, 32], [0, -32]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+];
+
+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(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { 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/PlainDate/prototype/until/roundingmode-halfExpand.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-halfExpand.js
new file mode 100644
index 0000000000..c5fc068b82
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-halfExpand.js
@@ -0,0 +1,39 @@
+// |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.plaindate.prototype.until
+description: Tests calculations with roundingMode "halfExpand".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2019, 1, 8);
+const later = new Temporal.PlainDate(2021, 9, 7);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [0, 32], [0, -32]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+];
+
+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(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { 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/PlainDate/prototype/until/roundingmode-halfFloor.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-halfFloor.js
new file mode 100644
index 0000000000..b4f0c8f767
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-halfFloor.js
@@ -0,0 +1,39 @@
+// |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.plaindate.prototype.until
+description: Tests calculations with roundingMode "halfFloor".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2019, 1, 8);
+const later = new Temporal.PlainDate(2021, 9, 7);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [0, 32], [0, -32]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+];
+
+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(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { 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/PlainDate/prototype/until/roundingmode-halfTrunc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-halfTrunc.js
new file mode 100644
index 0000000000..abd790fe34
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-halfTrunc.js
@@ -0,0 +1,39 @@
+// |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.plaindate.prototype.until
+description: Tests calculations with roundingMode "halfTrunc".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2019, 1, 8);
+const later = new Temporal.PlainDate(2021, 9, 7);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [0, 32], [0, -32]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+];
+
+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(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { 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/PlainDate/prototype/until/roundingmode-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-invalid-string.js
new file mode 100644
index 0000000000..7cc40d387e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/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.plaindate.prototype.until
+description: RangeError thrown when roundingMode option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2001, 6, 3);
+for (const roundingMode of ["other string", "cile", "CEIL", "ce\u0131l", "auto", "halfexpand", "floor\0"]) {
+ assert.throws(RangeError, () => earlier.until(later, { smallestUnit: "microsecond", roundingMode }));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-trunc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-trunc.js
new file mode 100644
index 0000000000..43cbe4a1f0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-trunc.js
@@ -0,0 +1,39 @@
+// |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.plaindate.prototype.until
+description: Tests calculations with roundingMode "trunc".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2019, 1, 8);
+const later = new Temporal.PlainDate(2021, 9, 7);
+
+const expected = [
+ ["years", [2], [-2]],
+ ["months", [0, 31], [0, -31]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+];
+
+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(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { 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/PlainDate/prototype/until/roundingmode-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-undefined.js
new file mode 100644
index 0000000000..0370a2e95a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-undefined.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.plaindate.prototype.until
+description: Fallback value for roundingMode option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 1, 1);
+
+const later1 = new Temporal.PlainDate(2005, 2, 20);
+const explicit1 = earlier.until(later1, { smallestUnit: "year", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit1, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, "default roundingMode is trunc");
+const implicit1 = earlier.until(later1, { smallestUnit: "year" });
+TemporalHelpers.assertDuration(implicit1, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, "default roundingMode is trunc");
+
+const later2 = new Temporal.PlainDate(2005, 12, 15);
+const explicit2 = earlier.until(later2, { smallestUnit: "year", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit2, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, "default roundingMode is trunc");
+const implicit2 = earlier.until(later2, { smallestUnit: "year" });
+TemporalHelpers.assertDuration(implicit2, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, "default roundingMode is trunc");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/roundingmode-wrong-type.js
new file mode 100644
index 0000000000..0c95403f84
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/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.plaindate.prototype.until
+description: Type conversions for roundingMode option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2001, 6, 3);
+TemporalHelpers.checkStringOptionWrongType("roundingMode", "trunc",
+ (roundingMode) => earlier.until(later, { smallestUnit: "year", roundingMode }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/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/PlainDate/prototype/until/smallestunit-higher-units.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/smallestunit-higher-units.js
new file mode 100644
index 0000000000..e6cd7b717a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/smallestunit-higher-units.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.plaindate.prototype.until
+description: Tests calculations with higher smallestUnit than the default of "days"
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = Temporal.PlainDate.from("2019-01-08");
+const later = Temporal.PlainDate.from("2021-09-07");
+
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "years", roundingMode: "halfExpand" }),
+ /* years = */ 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, "years");
+
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "months", roundingMode: "halfExpand" }),
+ 0, /* months = */ 32, 0, 0, 0, 0, 0, 0, 0, 0, "months");
+
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "weeks", roundingMode: "halfExpand" }),
+ 0, 0, /* weeks = */ 139, 0, 0, 0, 0, 0, 0, 0, "weeks");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/smallestunit-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/smallestunit-invalid-string.js
new file mode 100644
index 0000000000..6979d8f13c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/smallestunit-invalid-string.js
@@ -0,0 +1,41 @@
+// |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.plaindate.prototype.until
+description: RangeError thrown when smallestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2001, 6, 3);
+const badValues = [
+ "era",
+ "eraYear",
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+ "month\0",
+ "YEAR",
+ "eras",
+ "eraYears",
+ "hours",
+ "minutes",
+ "seconds",
+ "milliseconds",
+ "microseconds",
+ "nanoseconds",
+ "months\0",
+ "YEARS",
+ "other string",
+];
+for (const smallestUnit of badValues) {
+ assert.throws(RangeError, () => earlier.until(later, { smallestUnit }),
+ `"${smallestUnit}" is not a valid value for smallest unit`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/smallestunit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/smallestunit-plurals-accepted.js
new file mode 100644
index 0000000000..75f6d73609
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/smallestunit-plurals-accepted.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.plaindate.prototype.until
+description: Plural units are accepted as well for the smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2001, 6, 12);
+const validUnits = [
+ "year",
+ "month",
+ "week",
+ "day",
+];
+TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => earlier.until(later, { smallestUnit }), validUnits);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/smallestunit-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/smallestunit-undefined.js
new file mode 100644
index 0000000000..359541c4f2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/smallestunit-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.plaindate.prototype.until
+description: Fallback value for smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2001, 6, 3);
+
+const explicit = earlier.until(later, { smallestUnit: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 397, 0, 0, 0, 0, 0, 0, "default smallestUnit is day");
+const implicit = earlier.until(later, {});
+TemporalHelpers.assertDuration(implicit, 0, 0, 0, 397, 0, 0, 0, 0, 0, 0, "default smallestUnit is day");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/smallestunit-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/smallestunit-wrong-type.js
new file mode 100644
index 0000000000..f257997db1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/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.plaindate.prototype.until
+description: Type conversions for smallestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2001, 6, 3);
+TemporalHelpers.checkStringOptionWrongType("smallestUnit", "year",
+ (smallestUnit) => earlier.until(later, { smallestUnit }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/weeks-months.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/weeks-months.js
new file mode 100644
index 0000000000..54dbd17bc1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/weeks-months.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.plaindate.prototype.until
+description: until() should not return weeks and months together.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(1969, 7, 24);
+const laterDate = new Temporal.PlainDate(1969, 9, 4);
+TemporalHelpers.assertDuration(date.until(laterDate, { largestUnit: "weeks" }),
+ 0, 0, /* weeks = */ 6, 0, 0, 0, 0, 0, 0, 0, "weeks");
+TemporalHelpers.assertDuration(date.until(laterDate, { largestUnit: "months" }),
+ 0, /* months = */ 1, 0, 11, 0, 0, 0, 0, 0, 0, "months");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/year-zero.js
new file mode 100644
index 0000000000..f92ca0a5e4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/year-zero.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.plaindate.prototype.until
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T00:45",
+ "-000000-10-31T00:45+01:00",
+ "-000000-10-31T00:45+00:00[UTC]",
+];
+const instance = new Temporal.PlainDate(2000, 5, 2);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.until(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/basic.js
new file mode 100644
index 0000000000..3b8a525f93
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/basic.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.plaindate.prototype.valueof
+description: Basic tests for valueOf().
+features: [Temporal]
+---*/
+
+const plainDate = Temporal.PlainDate.from("1963-02-13");
+const plainDate2 = Temporal.PlainDate.from("1963-02-13");
+
+assert.throws(TypeError, () => plainDate.valueOf(), "valueOf");
+assert.throws(TypeError, () => plainDate < plainDate, "<");
+assert.throws(TypeError, () => plainDate <= plainDate, "<=");
+assert.throws(TypeError, () => plainDate > plainDate, ">");
+assert.throws(TypeError, () => plainDate >= plainDate, ">=");
+assert.sameValue(plainDate === plainDate, true, "===");
+assert.sameValue(plainDate === plainDate2, false, "===");
+assert.sameValue(plainDate !== plainDate, false, "!==");
+assert.sameValue(plainDate !== plainDate2, true, "!==");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/branding.js
new file mode 100644
index 0000000000..f3d9029747
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/branding.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.plaindate.prototype.valueof
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const valueOf = Temporal.PlainDate.prototype.valueOf;
+
+assert.sameValue(typeof valueOf, "function");
+
+assert.throws(TypeError, () => valueOf.call(undefined), "undefined");
+assert.throws(TypeError, () => valueOf.call(null), "null");
+assert.throws(TypeError, () => valueOf.call(true), "true");
+assert.throws(TypeError, () => valueOf.call(""), "empty string");
+assert.throws(TypeError, () => valueOf.call(Symbol()), "symbol");
+assert.throws(TypeError, () => valueOf.call(1), "1");
+assert.throws(TypeError, () => valueOf.call({}), "plain object");
+assert.throws(TypeError, () => valueOf.call(Temporal.PlainDate), "Temporal.PlainDate");
+assert.throws(TypeError, () => valueOf.call(Temporal.PlainDate.prototype), "Temporal.PlainDate.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/builtin.js
new file mode 100644
index 0000000000..04ce6abb81
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/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.plaindate.prototype.valueof
+description: >
+ Tests that Temporal.PlainDate.prototype.valueOf
+ 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.PlainDate.prototype.valueOf),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDate.prototype.valueOf),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDate.prototype.valueOf),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDate.prototype.valueOf.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/length.js
new file mode 100644
index 0000000000..3460276c29
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/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.plaindate.prototype.valueof
+description: Temporal.PlainDate.prototype.valueOf.length is 0
+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.PlainDate.prototype.valueOf, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/name.js
new file mode 100644
index 0000000000..a42c0be477
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/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.plaindate.prototype.valueof
+description: Temporal.PlainDate.prototype.valueOf.name is "valueOf".
+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.PlainDate.prototype.valueOf, "name", {
+ value: "valueOf",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/not-a-constructor.js
new file mode 100644
index 0000000000..20de55f8c5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/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.plaindate.prototype.valueof
+description: >
+ Temporal.PlainDate.prototype.valueOf 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.PlainDate.prototype.valueOf();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDate.prototype.valueOf), false,
+ "isConstructor(Temporal.PlainDate.prototype.valueOf)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/prop-desc.js
new file mode 100644
index 0000000000..fc586ef792
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/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.plaindate.prototype.valueof
+description: The "valueOf" property of Temporal.PlainDate.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDate.prototype.valueOf,
+ "function",
+ "`typeof PlainDate.prototype.valueOf` is `function`"
+);
+
+verifyProperty(Temporal.PlainDate.prototype, "valueOf", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/valueOf/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/PlainDate/prototype/weekOfYear/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/weekOfYear/basic.js
new file mode 100644
index 0000000000..0e60fc7e81
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/weekOfYear/basic.js
@@ -0,0 +1,37 @@
+// |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-get-temporal.plaindate.prototype.weekofyear
+description: Basic tests for weekOfYear().
+features: [Temporal]
+---*/
+
+for (let i = 29; i <= 31; ++i) {
+ const plainDate = new Temporal.PlainDate(1975, 12, i);
+ assert.sameValue(plainDate.weekOfYear, 1, `${plainDate} should be in week 1`);
+}
+for (let i = 1; i <= 4; ++i) {
+ const plainDate = new Temporal.PlainDate(1976, 1, i);
+ assert.sameValue(plainDate.weekOfYear, 1, `${plainDate} should be in week 1`);
+}
+for (let i = 5; i <= 11; ++i) {
+ const plainDate = new Temporal.PlainDate(1976, 1, i);
+ assert.sameValue(plainDate.weekOfYear, 2, `${plainDate} should be in week 2`);
+}
+for (let i = 20; i <= 26; ++i) {
+ const plainDate = new Temporal.PlainDate(1976, 12, i);
+ assert.sameValue(plainDate.weekOfYear, 52, `${plainDate} should be in week 52`);
+}
+for (let i = 27; i <= 31; ++i) {
+ const plainDate = new Temporal.PlainDate(1976, 12, i);
+ assert.sameValue(plainDate.weekOfYear, 53, `${plainDate} should be in week 53`);
+}
+for (let i = 1; i <= 2; ++i) {
+ const plainDate = new Temporal.PlainDate(1977, 1, i);
+ assert.sameValue(plainDate.weekOfYear, 53, `${plainDate} should be in week 53`);
+}
+
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/weekOfYear/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/weekOfYear/branding.js
new file mode 100644
index 0000000000..cb26e7db4b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/weekOfYear/branding.js
@@ -0,0 +1,25 @@
+// |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-get-temporal.plaindate.prototype.weekofyear
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const weekOfYear = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "weekOfYear").get;
+
+assert.sameValue(typeof weekOfYear, "function");
+
+assert.throws(TypeError, () => weekOfYear.call(undefined), "undefined");
+assert.throws(TypeError, () => weekOfYear.call(null), "null");
+assert.throws(TypeError, () => weekOfYear.call(true), "true");
+assert.throws(TypeError, () => weekOfYear.call(""), "empty string");
+assert.throws(TypeError, () => weekOfYear.call(Symbol()), "symbol");
+assert.throws(TypeError, () => weekOfYear.call(1), "1");
+assert.throws(TypeError, () => weekOfYear.call({}), "plain object");
+assert.throws(TypeError, () => weekOfYear.call(Temporal.PlainDate), "Temporal.PlainDate");
+assert.throws(TypeError, () => weekOfYear.call(Temporal.PlainDate.prototype), "Temporal.PlainDate.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/weekOfYear/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/weekOfYear/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/weekOfYear/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/weekOfYear/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/weekOfYear/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..7a8e2479c9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/weekOfYear/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plaindate.prototype.weekofyear
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const weekOfYearOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "weekOfYear");
+Object.defineProperty(Temporal.Calendar.prototype, "weekOfYear", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("weekOfYear should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDate(2000, 5, 2, "iso8601");
+instance.weekOfYear;
+
+Object.defineProperty(Temporal.Calendar.prototype, "weekOfYear", weekOfYearOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/weekOfYear/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/weekOfYear/custom.js
new file mode 100644
index 0000000000..1604da720c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/weekOfYear/custom.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-get-temporal.plaindate.prototype.weekofyear
+description: Custom calendar tests for weekOfYear().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ weekOfYear(...args) {
+ ++calls;
+ assert.compareArray(args, [pd], "weekOfYear arguments");
+ return 7;
+ }
+}
+
+const calendar = new CustomCalendar();
+const pd = new Temporal.PlainDate(1830, 8, 25, calendar);
+const result = pd.weekOfYear;
+assert.sameValue(result, 7, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/weekOfYear/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/weekOfYear/prop-desc.js
new file mode 100644
index 0000000000..b1dde97e11
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/weekOfYear/prop-desc.js
@@ -0,0 +1,17 @@
+// |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-get-temporal.plaindate.prototype.weekofyear
+description: The "weekOfYear" property of Temporal.PlainDate.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "weekOfYear");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/weekOfYear/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/weekOfYear/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/weekOfYear/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/weekOfYear/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/weekOfYear/validate-calendar-value.js
new file mode 100644
index 0000000000..7836bbff10
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/weekOfYear/validate-calendar-value.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-get-temporal.plaindate.prototype.weekofyear
+description: Validate result returned from calendar weekOfYear() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [null, TypeError],
+ [false, TypeError],
+ [Infinity, RangeError],
+ [-Infinity, RangeError],
+ [NaN, RangeError],
+ [-7, RangeError],
+ [-0.1, RangeError],
+ ["string", TypeError],
+ [Symbol("foo"), TypeError],
+ [7n, TypeError],
+ [{}, TypeError],
+ [true, TypeError],
+ [7.1, RangeError],
+ ["7", TypeError],
+ ["7.5", TypeError],
+ [{valueOf() { return 7; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ weekOfYear() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainDate(1981, 12, 15, calendar);
+ assert.throws(error, () => instance.weekOfYear, `${typeof result} ${String(result)} not converted to positive integer`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/basic.js
new file mode 100644
index 0000000000..cf5e85bd1d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/basic.js
@@ -0,0 +1,31 @@
+// |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.plaindate.prototype.with
+description: Basic tests for with().
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainDate = new Temporal.PlainDate(1976, 11, 18);
+
+const withYear = plainDate.with({ year: 2019 });
+TemporalHelpers.assertPlainDate(withYear, 2019, 11, "M11", 18, "with(year)");
+
+const withMonth = plainDate.with({ month: 5 });
+TemporalHelpers.assertPlainDate(withMonth, 1976, 5, "M05", 18, "with(month)");
+
+const withMonthCode = plainDate.with({ monthCode: 'M05' });
+TemporalHelpers.assertPlainDate(withMonthCode, 1976, 5, "M05", 18, "with(monthCode)");
+
+const withDay = plainDate.with({ day: 17 });
+TemporalHelpers.assertPlainDate(withDay, 1976, 11, "M11", 17, "with(day)");
+
+const withPlural = plainDate.with({ months: 12, day: 15 });
+TemporalHelpers.assertPlainDate(withPlural, 1976, 11, "M11", 15, "with(plural)");
+
+assert.throws(RangeError, () => plainDate.with({ month: 5, monthCode: 'M06' }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/branding.js
new file mode 100644
index 0000000000..bd2d266e41
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/branding.js
@@ -0,0 +1,27 @@
+// |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.plaindate.prototype.with
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const with_ = Temporal.PlainDate.prototype.with;
+
+assert.sameValue(typeof with_, "function");
+
+const args = [{ year: 2022 }];
+
+assert.throws(TypeError, () => with_.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => with_.apply(null, args), "null");
+assert.throws(TypeError, () => with_.apply(true, args), "true");
+assert.throws(TypeError, () => with_.apply("", args), "empty string");
+assert.throws(TypeError, () => with_.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => with_.apply(1, args), "1");
+assert.throws(TypeError, () => with_.apply({}, args), "plain object");
+assert.throws(TypeError, () => with_.apply(Temporal.PlainDate, args), "Temporal.PlainDate");
+assert.throws(TypeError, () => with_.apply(Temporal.PlainDate.prototype, args), "Temporal.PlainDate.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..4e4d090692
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/builtin-calendar-no-array-iteration.js
@@ -0,0 +1,23 @@
+// |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.plaindate.prototype.with
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.PlainDate(2023, 5, 2, "iso8601");
+instance.with({ day: 5 });
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..94beab4fd1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,46 @@
+// |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.plaindate.prototype.with
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const fieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "fields");
+Object.defineProperty(Temporal.Calendar.prototype, "fields", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("fields should not be looked up");
+ },
+});
+const mergeFieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "mergeFields");
+Object.defineProperty(Temporal.Calendar.prototype, "mergeFields", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("mergeFields should not be looked up");
+ },
+});
+const dateFromFieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dateFromFields");
+Object.defineProperty(Temporal.Calendar.prototype, "dateFromFields", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("dateFromFields should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDate(2000, 5, 2, "iso8601");
+instance.with({ year: 2001 });
+
+Object.defineProperty(Temporal.Calendar.prototype, "fields", fieldsOriginal);
+Object.defineProperty(Temporal.Calendar.prototype, "mergeFields", mergeFieldsOriginal);
+Object.defineProperty(Temporal.Calendar.prototype, "dateFromFields", dateFromFieldsOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/builtin.js
new file mode 100644
index 0000000000..d356db6b7d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/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.plaindate.prototype.with
+description: >
+ Tests that Temporal.PlainDate.prototype.with
+ 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.PlainDate.prototype.with),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDate.prototype.with),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDate.prototype.with),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDate.prototype.with.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/calendar-fields-iterable.js
new file mode 100644
index 0000000000..2fd26bafa7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/calendar-fields-iterable.js
@@ -0,0 +1,32 @@
+// |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.plaindate.prototype.with
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plaindate.prototype.with step 9:
+ 9. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+const date = new Temporal.PlainDate(2000, 5, 2, calendar);
+date.with({ year: 2005 });
+
+assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/calendar-fromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/calendar-fromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..36242e3577
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/calendar-fromfields-called-with-null-prototype-fields.js
@@ -0,0 +1,18 @@
+// |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.plaindate.prototype.with
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.PlainDate(2000, 5, 2, calendar);
+instance.with({ day: 24 });
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should have been called on the calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/calendar-invalid-return.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/calendar-invalid-return.js
new file mode 100644
index 0000000000..9555916651
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/calendar-invalid-return.js
@@ -0,0 +1,39 @@
+// |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.plaindate.prototype.with
+description: Throw when the returned value from the calendar's dateFromFields method is not a PlainDate.
+features: [Temporal]
+---*/
+
+class CustomCalendar extends Temporal.Calendar {
+ constructor(value) {
+ super("iso8601");
+ this.value = value;
+ }
+ dateFromFields() {
+ return this.value;
+ }
+}
+
+const tests = [
+ [undefined],
+ [null, "null"],
+ [true],
+ ["2000-05"],
+ [Symbol()],
+ [200005],
+ [200005n],
+ [{}, "plain object"],
+ [() => {}, "lambda"],
+ [Temporal.PlainDate, "Temporal.PlainDate"],
+ [Temporal.PlainDate.prototype, "Temporal.PlainDate.prototype"],
+];
+for (const [test, description = typeof test] of tests) {
+ const plainDate = new Temporal.PlainDate(2000, 5, 2, new CustomCalendar(test));
+ assert.throws(TypeError, () => plainDate.with({ year: 1 }), `Expected error with ${description}`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/calendar-merge-fields-returns-primitive.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/calendar-merge-fields-returns-primitive.js
new file mode 100644
index 0000000000..80de0a1a3d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/calendar-merge-fields-returns-primitive.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.plaindate.prototype.with
+description: >
+ with() should throw a TypeError if mergeFields() returns a primitive,
+ without passing the value on to any other calendar methods
+includes: [compareArray.js, temporalHelpers.js]
+features: [BigInt, Symbol, Temporal]
+---*/
+
+[undefined, null, true, 3.14159, "bad value", Symbol("no"), 7n].forEach((primitive) => {
+ const calendar = TemporalHelpers.calendarMergeFieldsReturnsPrimitive(primitive);
+ const instance = new Temporal.PlainDate(2000, 5, 2, calendar);
+ assert.throws(TypeError, () => instance.with({ year: 2005 }), "bad return from mergeFields() throws");
+ assert.sameValue(calendar.dateFromFieldsCallCount, 0, "dateFromFields() never called");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/calendar-mergefields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/calendar-mergefields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..a02da126df
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/calendar-mergefields-called-with-null-prototype-fields.js
@@ -0,0 +1,18 @@
+// |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.plaindate.prototype.with
+description: >
+ Calendar.mergeFields method is called with null-prototype fields objects
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckMergeFieldsPrototypePollution();
+const instance = new Temporal.PlainDate(2000, 5, 2, calendar);
+instance.with({ day: 24 });
+assert.sameValue(calendar.mergeFieldsCallCount, 1, "mergeFields should have been called on the calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..ec53ff16a7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/constructor-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |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.plaindate.prototype.with
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const date = new Temporal.PlainDate(2023, 5, 1, calendar);
+
+assert.throws(RangeError, () => date.with({day: 15}));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/copies-merge-fields-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/copies-merge-fields-object.js
new file mode 100644
index 0000000000..ac83620275
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/copies-merge-fields-object.js
@@ -0,0 +1,38 @@
+// |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.plaindate.prototype.with
+description: The object returned from mergeFields() is copied before being passed to dateFromFields().
+info: |
+ sec-temporal.plaindate.prototype.with steps 13–15:
+ 13. Set _fields_ to ? CalendarMergeFields(_calendar_, _fields_, _partialDate_).
+ 14. Set _fields_ to ? PrepareTemporalFields(_fields_, _fieldNames_, «»).
+ 15. Return ? DateFromFields(_calendar_, _fields_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "get day",
+ "get day.valueOf",
+ "call day.valueOf",
+ "get month",
+ "get month.valueOf",
+ "call month.valueOf",
+ "get monthCode",
+ "get monthCode.toString",
+ "call monthCode.toString",
+ "get year",
+ "get year.valueOf",
+ "call year.valueOf",
+];
+
+const calendar = TemporalHelpers.calendarMergeFieldsGetters();
+const date = new Temporal.PlainDate(2021, 3, 31, calendar);
+date.with({ year: 2022 });
+
+assert.compareArray(calendar.mergeFieldsReturnOperations, expected, "getters called on mergeFields return");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/copy-properties-not-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/copy-properties-not-undefined.js
new file mode 100644
index 0000000000..b7dde1f9af
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/copy-properties-not-undefined.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.plaindate.prototype.with
+description: PreparePartialTemporalFields copies only defined properties of source object
+info: |
+ 4. For each value _property_ of _fieldNames_, do
+ a. Let _value_ be ? Get(_fields_, _property_).
+ b. If _value_ is not *undefined*, then
+ ...
+ iii. Perform ! CreateDataPropertyOrThrow(_result_, _property_, _value_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainDate = new Temporal.PlainDate(2006, 1, 24);
+
+TemporalHelpers.assertPlainDate(plainDate.with({ day: 1, year: undefined }),
+ 2006, 1, "M01", 1,
+ "only the properties that are present and defined in the plain object are copied"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/custom.js
new file mode 100644
index 0000000000..6efb016438
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/custom.js
@@ -0,0 +1,39 @@
+// |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.plaindate.prototype.with
+description: Basic tests with custom calendar
+features: [Temporal]
+---*/
+
+const result = new Temporal.PlainDate(1920, 5, 3);
+const options = {
+ extra: "property",
+};
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ dateFromFields(...args) {
+ ++calls;
+ assert.sameValue(args.length, 2, "Two arguments");
+ assert.sameValue(typeof args[0], "object", "First argument: type");
+ assert.sameValue(args[0].day, 18, "First argument: day");
+ assert.sameValue(args[0].month, 11, "First argument: month");
+ assert.sameValue(args[0].monthCode, "M11", "First argument: monthCode");
+ assert.sameValue(args[0].year, 43, "First argument: year");
+ assert.notSameValue(args[1], options, "Second argument is a copy of options");
+ assert.sameValue(args[1].extra, "property", "All properties are copied");
+ assert.sameValue(Object.getPrototypeOf(args[1]), null, "Copy has null prototype");
+ return result;
+ }
+}
+const calendar = new CustomCalendar();
+const plainDate = new Temporal.PlainDate(1976, 11, 18, calendar);
+assert.sameValue(plainDate.with({ year: 43 }, options), result);
+assert.sameValue(calls, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/duplicate-calendar-fields.js
new file mode 100644
index 0000000000..8e1628294d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/duplicate-calendar-fields.js
@@ -0,0 +1,18 @@
+// |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.plaindate.prototype.with
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['monthCode'], ['day'], ['month'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const date = new Temporal.PlainDate(2023, 5, 1, calendar);
+
+ assert.throws(RangeError, () => date.with({day: 15}));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..aa6e168ea7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/infinity-throws-rangeerror.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.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plaindate.prototype.with
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day"].forEach((prop) => {
+ ["constrain", "reject"].forEach((overflow) => {
+ assert.throws(RangeError, () => instance.with({ [prop]: inf }, { overflow }), `${prop} property cannot be ${inf} (overflow ${overflow}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.with({ [prop]: obj }, { overflow }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/length.js
new file mode 100644
index 0000000000..22beee3de6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/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.plaindate.prototype.with
+description: Temporal.PlainDate.prototype.with.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.PlainDate.prototype.with, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/name.js
new file mode 100644
index 0000000000..8cc69ee18d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/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.plaindate.prototype.with
+description: Temporal.PlainDate.prototype.with.name is "with".
+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.PlainDate.prototype.with, "name", {
+ value: "with",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/not-a-constructor.js
new file mode 100644
index 0000000000..c730e54a2b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/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.plaindate.prototype.with
+description: >
+ Temporal.PlainDate.prototype.with 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.PlainDate.prototype.with();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDate.prototype.with), false,
+ "isConstructor(Temporal.PlainDate.prototype.with)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/options-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/options-object.js
new file mode 100644
index 0000000000..52ec0f1d8c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/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.plaindate.prototype.with
+description: Empty or a function object may be used as options
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+const result1 = instance.with({ day: 5 }, {});
+TemporalHelpers.assertPlainDate(
+ result1, 2000, 5, "M05", 5,
+ "options may be an empty plain object"
+);
+
+const result2 = instance.with({ day: 5 }, () => {});
+TemporalHelpers.assertPlainDate(
+ result2, 2000, 5, "M05", 5,
+ "options may be a function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/options-undefined.js
new file mode 100644
index 0000000000..99efdcbf51
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/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.plaindate.prototype.with
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(2000, 2, 2);
+const fields = { day: 31 };
+
+const explicit = date.with(fields, undefined);
+assert.sameValue(explicit.month, 2, "default overflow is constrain");
+assert.sameValue(explicit.day, 29, "default overflow is constrain");
+
+const implicit = date.with(fields);
+assert.sameValue(implicit.month, 2, "default overflow is constrain");
+assert.sameValue(implicit.day, 29, "default overflow is constrain");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/options-wrong-type.js
new file mode 100644
index 0000000000..ed53fa255e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/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.plaindate.prototype.with
+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.PlainDate(2000, 5, 2);
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.with({ day: 5 }, value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/order-of-operations.js
new file mode 100644
index 0000000000..5601354e15
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/order-of-operations.js
@@ -0,0 +1,80 @@
+// |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.plaindate.prototype.with
+description: Properties on an object passed to with() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ // RejectObjectWithCalendarOrTimeZone
+ "get fields.calendar",
+ "get fields.timeZone",
+ // CopyDataProperties
+ "ownKeys options",
+ "getOwnPropertyDescriptor options.overflow",
+ "get options.overflow",
+ "getOwnPropertyDescriptor options.extra",
+ "get options.extra",
+ // lookup
+ "get this.calendar.dateFromFields",
+ "get this.calendar.fields",
+ "get this.calendar.mergeFields",
+ // CalendarFields
+ "call this.calendar.fields",
+ // PrepareTemporalFields on receiver
+ "get this.calendar.day",
+ "call this.calendar.day",
+ "get this.calendar.month",
+ "call this.calendar.month",
+ "get this.calendar.monthCode",
+ "call this.calendar.monthCode",
+ "get this.calendar.year",
+ "call this.calendar.year",
+ // PrepareTemporalFields on argument
+ "get fields.day",
+ "get fields.day.valueOf",
+ "call fields.day.valueOf",
+ "get fields.month",
+ "get fields.month.valueOf",
+ "call fields.month.valueOf",
+ "get fields.monthCode",
+ "get fields.monthCode.toString",
+ "call fields.monthCode.toString",
+ "get fields.year",
+ "get fields.year.valueOf",
+ "call fields.year.valueOf",
+ // CalendarMergeFields
+ "call this.calendar.mergeFields",
+ // CalendarDateFromFields
+ "call this.calendar.dateFromFields",
+ // inside Calendar.p.dateFromFields
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+];
+const actual = [];
+
+const calendar = TemporalHelpers.calendarObserver(actual, "this.calendar");
+const instance = new Temporal.PlainDate(2000, 5, 2, calendar);
+// clear observable operations that occurred during the constructor call
+actual.splice(0);
+
+const fields = TemporalHelpers.propertyBagObserver(actual, {
+ year: 1.7,
+ month: 1.7,
+ monthCode: "M01",
+ day: 1.7,
+}, "fields");
+
+const options = TemporalHelpers.propertyBagObserver(actual, {
+ overflow: "constrain",
+ extra: "property",
+}, "options");
+
+instance.with(fields, options);
+assert.compareArray(actual, expected, "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/overflow-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/overflow-invalid-string.js
new file mode 100644
index 0000000000..725da2cbff
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/overflow-invalid-string.js
@@ -0,0 +1,31 @@
+// |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.plaindate.prototype.with
+description: RangeError thrown when overflow option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-isodatefromfields step 2:
+ 2. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plaindate.prototype.with step 16:
+ 16. Return ? DateFromFields(_calendar_, _fields_, _options_).
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(2000, 5, 2);
+
+const badOverflows = ["", "CONSTRAIN", "balance", "other string", "constra\u0131n", "reject\0"];
+for (const overflow of badOverflows) {
+ assert.throws(
+ RangeError,
+ () => date.with({ month: 8 }, { overflow }),
+ `invalid overflow ("${overflow}")`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/overflow-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/overflow-undefined.js
new file mode 100644
index 0000000000..52768ac466
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/overflow-undefined.js
@@ -0,0 +1,29 @@
+// |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.plaindate.prototype.with
+description: Fallback value for overflow option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-isodatefromfields step 2:
+ 2. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plaindate.prototype.with step 16:
+ 16. Return ? DateFromFields(_calendar_, _fields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(2000, 5, 2);
+const explicit = date.with({ month: 15 }, { overflow: undefined });
+TemporalHelpers.assertPlainDate(explicit, 2000, 12, "M12", 2, "default overflow is constrain");
+const implicit = date.with({ month: 15 }, {});
+TemporalHelpers.assertPlainDate(implicit, 2000, 12, "M12", 2, "default overflow is constrain");
+const fun = date.with({ month: 15 }, () => {});
+TemporalHelpers.assertPlainDate(fun, 2000, 12, "M12", 2, "default overflow is constrain");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/overflow-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/overflow-wrong-type.js
new file mode 100644
index 0000000000..bc53f043ff
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/overflow-wrong-type.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.plaindate.prototype.with
+description: Type conversions for overflow option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-isodatefromfields step 2:
+ 2. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plaindate.prototype.with step 16:
+ 16. Return ? DateFromFields(_calendar_, _fields_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(2000, 5, 2);
+TemporalHelpers.checkStringOptionWrongType("overflow", "constrain",
+ (overflow) => date.with({ month: 8 }, { overflow }),
+ (result, descr) => TemporalHelpers.assertPlainDate(result, 2000, 8, "M08", 2, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/plaindatelike-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/plaindatelike-invalid.js
new file mode 100644
index 0000000000..4d1e422690
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/plaindatelike-invalid.js
@@ -0,0 +1,50 @@
+// |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.plaindate.prototype.with
+description: Throws TypeError on an argument that is not a PlainDate-like property bag
+features: [Temporal]
+---*/
+
+const plainDate = new Temporal.PlainDate(1976, 11, 18);
+
+const tests = [
+ // Step 3.
+ [undefined],
+ [null],
+ [true],
+ ["2019-05-17"],
+ ["2019-05-17T12:34"],
+ ["2019-05-17T12:34Z"],
+ ["18:05:42.577"],
+ ["42"],
+ [Symbol(), "symbol"],
+ [42, "number"],
+ [42n, "bigint"],
+
+ // Step 4.
+ // RejectObjectWithCalendarOrTimeZone step 2.
+ [Temporal.PlainDate.from("2019-05-17"), "PlainDate"],
+ [Temporal.PlainDateTime.from("2019-05-17T12:34"), "PlainDateTime"],
+ [Temporal.PlainMonthDay.from("2019-05-17"), "PlainMonthDay"],
+ [Temporal.PlainTime.from("12:34"), "PlainTime"],
+ [Temporal.PlainYearMonth.from("2019-05-17"), "PlainYearMonth"],
+ [Temporal.ZonedDateTime.from("2019-05-17T12:34Z[UTC]"), "ZonedDateTime"],
+ // RejectObjectWithCalendarOrTimeZone step 3-4.
+ [{ year: 2021, calendar: "iso8601" }, "calendar"],
+ // RejectObjectWithCalendarOrTimeZone step 5-6.
+ [{ year: 2021, timeZone: "UTC" }, "timeZone"],
+
+ // Step 7.
+ [{}, "empty object"],
+ [{ months: 12 }, "only plural property"],
+
+];
+
+for (const [value, message = String(value)] of tests) {
+ assert.throws(TypeError, () => plainDate.with(value), message);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/prop-desc.js
new file mode 100644
index 0000000000..857b8e17a7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/prop-desc.js
@@ -0,0 +1,24 @@
+// |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.plaindate.prototype.with
+description: The "with" property of Temporal.PlainDate.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDate.prototype.with,
+ "function",
+ "`typeof PlainDate.prototype.with` is `function`"
+);
+
+verifyProperty(Temporal.PlainDate.prototype, "with", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/proto-in-calendar-fields.js
new file mode 100644
index 0000000000..68eef30019
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/proto-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |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.plaindate.prototype.with
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const date = new Temporal.PlainDate(2023, 5, 1, calendar);
+
+assert.throws(RangeError, () => date.with({day: 15}));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/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/PlainDate/prototype/with/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/subclassing-ignored.js
new file mode 100644
index 0000000000..65b263f341
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/subclassing-ignored.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.plaindate.prototype.with
+description: Objects of a subclass are never created as return values for with()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.PlainDate,
+ [2000, 5, 2],
+ "with",
+ [{ day: 20 }],
+ (result) => TemporalHelpers.assertPlainDate(result, 2000, 5, "M05", 20),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/basic.js
new file mode 100644
index 0000000000..daafac19f2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/basic.js
@@ -0,0 +1,31 @@
+// |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.plaindate.prototype.withcalendar
+description: Basic tests for withCalendar().
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainDate = Temporal.PlainDate.from("1976-11-18");
+const calendar = Temporal.Calendar.from("iso8601");
+
+const objectResult = plainDate.withCalendar(calendar);
+assert.notSameValue(objectResult, plainDate, "object: new object");
+TemporalHelpers.assertPlainDate(objectResult, 1976, 11, "M11", 18, "object");
+assert.sameValue(objectResult.getCalendar(), calendar, "object: calendar");
+
+const stringResult = plainDate.withCalendar("iso8601");
+assert.notSameValue(stringResult, plainDate, "string: new object");
+TemporalHelpers.assertPlainDate(stringResult, 1976, 11, "M11", 18, "string");
+assert.sameValue(stringResult.getISOFields().calendar, "iso8601", "string: calendar slot stores a string");
+
+const originalCalendar = plainDate.getCalendar();
+const sameResult = plainDate.withCalendar(originalCalendar);
+assert.notSameValue(sameResult, plainDate, "original: new object");
+TemporalHelpers.assertPlainDate(sameResult, 1976, 11, "M11", 18, "original");
+assert.sameValue(sameResult.getCalendar(), originalCalendar, "original: calendar slot stores and object");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/branding.js
new file mode 100644
index 0000000000..b12a12f12d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/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.plaindate.prototype.withcalendar
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const withCalendar = Temporal.PlainDate.prototype.withCalendar;
+
+assert.sameValue(typeof withCalendar, "function");
+
+const args = [new Temporal.Calendar("iso8601")];
+
+assert.throws(TypeError, () => withCalendar.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => withCalendar.apply(null, args), "null");
+assert.throws(TypeError, () => withCalendar.apply(true, args), "true");
+assert.throws(TypeError, () => withCalendar.apply("", args), "empty string");
+assert.throws(TypeError, () => withCalendar.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => withCalendar.apply(1, args), "1");
+assert.throws(TypeError, () => withCalendar.apply({}, args), "plain object");
+assert.throws(TypeError, () => withCalendar.apply(Temporal.PlainDate, args), "Temporal.PlainDate");
+assert.throws(TypeError, () => withCalendar.apply(Temporal.PlainDate.prototype, args), "Temporal.PlainDate.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..7869dea1f9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plaindate.prototype.withcalendar
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const idOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "id");
+Object.defineProperty(Temporal.Calendar.prototype, "id", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("id should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDate(2000, 5, 2, "iso8601");
+instance.withCalendar("iso8601");
+
+Object.defineProperty(Temporal.Calendar.prototype, "id", idOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/builtin.js
new file mode 100644
index 0000000000..92c0aab861
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/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.plaindate.prototype.withcalendar
+description: >
+ Tests that Temporal.PlainDate.prototype.withCalendar
+ 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.PlainDate.prototype.withCalendar),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDate.prototype.withCalendar),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDate.prototype.withCalendar),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDate.prototype.withCalendar.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/calendar-case-insensitive.js
new file mode 100644
index 0000000000..a367f57bf4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/calendar-case-insensitive.js
@@ -0,0 +1,39 @@
+// |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.plaindate.prototype.withcalendar
+description: Calendar names are case-insensitive
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(1976, 11, 18, {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "replace-me",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+});
+
+const arg = "iSo8601";
+const result = instance.withCalendar(arg);
+assert.sameValue(result.calendarId, "iso8601", "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/calendar-number.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/calendar-number.js
new file mode 100644
index 0000000000..94eb9a3d7b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/calendar-number.js
@@ -0,0 +1,50 @@
+// |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.plaindate.prototype.withcalendar
+description: A number is not allowed to be a calendar
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(1976, 11, 18, {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "replace-me",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+});
+
+const numbers = [
+ 1,
+ -19761118,
+ 19761118,
+ 1234567890,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => instance.withCalendar(arg),
+ "A number is not a valid ISO string for Calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/calendar-string-leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/calendar-string-leap-second.js
new file mode 100644
index 0000000000..5c8fc1773b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/calendar-string-leap-second.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.plaindate.prototype.withcalendar
+description: Leap second is a valid ISO string for Calendar
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(1976, 11, 18, {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "replace-me",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+});
+
+const arg = "2016-12-31T23:59:60";
+const result = instance.withCalendar(arg);
+assert.sameValue(
+ result.calendarId,
+ "iso8601",
+ "leap second is a valid ISO string for Calendar"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/calendar-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/calendar-string.js
new file mode 100644
index 0000000000..8fb978666d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/calendar-string.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.plaindate.prototype.withcalendar
+description: A calendar ID is valid input for Calendar
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(1976, 11, 18, {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "replace-me",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+});
+
+const arg = "iso8601";
+
+const result = instance.withCalendar(arg);
+assert.sameValue(result.getISOFields().calendar, "iso8601", `Calendar created from string "${arg}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/calendar-temporal-object.js
new file mode 100644
index 0000000000..31039caed6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/calendar-temporal-object.js
@@ -0,0 +1,64 @@
+// |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.plaindate.prototype.withcalendar
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal-totemporalcalendar step 1.b:
+ b. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const plainDate = new Temporal.PlainDate(2000, 5, 2);
+const plainDateTime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const plainMonthDay = new Temporal.PlainMonthDay(5, 2);
+const plainYearMonth = new Temporal.PlainYearMonth(2000, 5);
+const zonedDateTime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+
+[plainDate, plainDateTime, plainMonthDay, plainYearMonth, zonedDateTime].forEach((arg) => {
+ const actual = [];
+ const expected = [];
+
+ const calendar = arg.getISOFields().calendar;
+
+ Object.defineProperty(arg, "calendar", {
+ get() {
+ actual.push("get calendar");
+ return calendar;
+ },
+ });
+
+ const instance = new Temporal.PlainDate(1976, 11, 18, {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "replace-me",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+});
+ const result = instance.withCalendar(arg);
+ assert.sameValue(result.getISOFields().calendar, calendar, "Temporal object coerced to calendar");
+
+ assert.compareArray(actual, expected, "calendar getter not called");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/calendar-wrong-type.js
new file mode 100644
index 0000000000..c6184f42b7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/calendar-wrong-type.js
@@ -0,0 +1,64 @@
+// |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.plaindate.prototype.withcalendar
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or object for Calendar
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(1976, 11, 18, {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "replace-me",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+});
+
+const primitiveTests = [
+ [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.withCalendar(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.withCalendar(arg), `${description} is not a valid object and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/length.js
new file mode 100644
index 0000000000..1c0d85af21
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/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.plaindate.prototype.withcalendar
+description: Temporal.PlainDate.prototype.withCalendar.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.PlainDate.prototype.withCalendar, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/missing-argument.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/missing-argument.js
new file mode 100644
index 0000000000..c8d2409041
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/missing-argument.js
@@ -0,0 +1,15 @@
+// |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.plaindate.prototype.withcalendar
+description: TypeError thrown when calendar argument not given
+features: [Temporal]
+---*/
+
+const plainDate = Temporal.PlainDate.from("1976-11-18");
+assert.throws(TypeError, () => plainDate.withCalendar(), "missing argument");
+assert.throws(TypeError, () => plainDate.withCalendar(undefined), "undefined argument");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/name.js
new file mode 100644
index 0000000000..ff2da63f67
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/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.plaindate.prototype.withcalendar
+description: Temporal.PlainDate.prototype.withCalendar.name is "withCalendar".
+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.PlainDate.prototype.withCalendar, "name", {
+ value: "withCalendar",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/not-a-constructor.js
new file mode 100644
index 0000000000..222f96e84a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/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.plaindate.prototype.withcalendar
+description: >
+ Temporal.PlainDate.prototype.withCalendar 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.PlainDate.prototype.withCalendar();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDate.prototype.withCalendar), false,
+ "isConstructor(Temporal.PlainDate.prototype.withCalendar)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/prop-desc.js
new file mode 100644
index 0000000000..3cb792447e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/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.plaindate.prototype.withcalendar
+description: The "withCalendar" property of Temporal.PlainDate.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDate.prototype.withCalendar,
+ "function",
+ "`typeof PlainDate.prototype.withCalendar` is `function`"
+);
+
+verifyProperty(Temporal.PlainDate.prototype, "withCalendar", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/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/PlainDate/prototype/withCalendar/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/subclassing-ignored.js
new file mode 100644
index 0000000000..c188e3fe7a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/subclassing-ignored.js
@@ -0,0 +1,50 @@
+// |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.plaindate.prototype.withcalendar
+description: Objects of a subclass are never created as return values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const customCalendar = {
+ era() { return undefined; },
+ eraYear() { return undefined; },
+ year() { return 1900; },
+ month() { return 2; },
+ monthCode() { return "M02"; },
+ day() { return 5; },
+ id: "custom-calendar",
+ toString() { return "custom-calendar"; },
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.PlainDate,
+ [2000, 5, 2],
+ "withCalendar",
+ [customCalendar],
+ (result) => {
+ TemporalHelpers.assertPlainDate(result, 1900, 2, "M02", 5);
+ assert.sameValue(result.getCalendar(), customCalendar, "calendar result");
+ },
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/year/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/year/branding.js
new file mode 100644
index 0000000000..f41cf4a738
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/year/branding.js
@@ -0,0 +1,25 @@
+// |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-get-temporal.plaindate.prototype.year
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const year = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "year").get;
+
+assert.sameValue(typeof year, "function");
+
+assert.throws(TypeError, () => year.call(undefined), "undefined");
+assert.throws(TypeError, () => year.call(null), "null");
+assert.throws(TypeError, () => year.call(true), "true");
+assert.throws(TypeError, () => year.call(""), "empty string");
+assert.throws(TypeError, () => year.call(Symbol()), "symbol");
+assert.throws(TypeError, () => year.call(1), "1");
+assert.throws(TypeError, () => year.call({}), "plain object");
+assert.throws(TypeError, () => year.call(Temporal.PlainDate), "Temporal.PlainDate");
+assert.throws(TypeError, () => year.call(Temporal.PlainDate.prototype), "Temporal.PlainDate.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/year/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/year/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/year/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/year/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/year/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..c17301105f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/year/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plaindate.prototype.year
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const yearOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "year");
+Object.defineProperty(Temporal.Calendar.prototype, "year", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("year should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDate(2000, 5, 2, "iso8601");
+instance.year;
+
+Object.defineProperty(Temporal.Calendar.prototype, "year", yearOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/year/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/year/custom.js
new file mode 100644
index 0000000000..a333cff4e0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/year/custom.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-get-temporal.plaindate.prototype.year
+description: Custom calendar tests for year().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ year(...args) {
+ ++calls;
+ assert.compareArray(args, [pd], "year arguments");
+ return 7;
+ }
+}
+
+const calendar = new CustomCalendar();
+const pd = new Temporal.PlainDate(1830, 8, 25, calendar);
+const result = pd.year;
+assert.sameValue(result, 7, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/year/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/year/prop-desc.js
new file mode 100644
index 0000000000..45c773b912
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/year/prop-desc.js
@@ -0,0 +1,17 @@
+// |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-get-temporal.plaindate.prototype.year
+description: The "year" property of Temporal.PlainDate.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "year");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/year/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/year/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/year/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/year/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/year/validate-calendar-value.js
new file mode 100644
index 0000000000..cf014e9017
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/year/validate-calendar-value.js
@@ -0,0 +1,54 @@
+// |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-get-temporal.plaindate.prototype.year
+description: Validate result returned from calendar year() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [Infinity, RangeError],
+ [-Infinity, RangeError],
+ [Symbol("foo"), TypeError],
+ [7n, TypeError],
+ [NaN, RangeError],
+ ["string", TypeError],
+ [{}, TypeError],
+ [null, TypeError],
+ [true, TypeError],
+ [false, TypeError],
+ [7.1, RangeError],
+ [-0.1, RangeError],
+ ["7", TypeError],
+ ["7.5", TypeError],
+ [{valueOf() { return 7; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ year() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainDate(1981, 12, 15, calendar);
+ assert.throws(error, () => instance.year, `${typeof result} ${String(result)} not converted to integer`);
+});
+
+const preservedResults = [
+ -7,
+];
+
+preservedResults.forEach(result => {
+ const calendar = new class extends Temporal.Calendar {
+ year() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainDate(1981, 12, 15, calendar);
+ assert.sameValue(instance.year, result, `${typeof result} ${String(result)} preserved`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/yearOfWeek/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/yearOfWeek/basic.js
new file mode 100644
index 0000000000..f5ef96ddb5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/yearOfWeek/basic.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-get-temporal.plaindate.prototype.yearofweek
+description: Basic tests for yearOfWeek().
+features: [Temporal]
+---*/
+
+for (let i = 29; i <= 31; ++i) {
+ const plainDate = new Temporal.PlainDate(1975, 12, i);
+ assert.sameValue(plainDate.yearOfWeek, 1976, `${plainDate} should be in yearOfWeek 1976`);
+}
+for (let i = 1; i <= 11; ++i) {
+ const plainDate = new Temporal.PlainDate(1976, 1, i);
+ assert.sameValue(plainDate.yearOfWeek, 1976, `${plainDate} should be in yearOfWeek 1976`);
+}
+for (let i = 20; i <= 31; ++i) {
+ const plainDate = new Temporal.PlainDate(1976, 12, i);
+ assert.sameValue(plainDate.yearOfWeek, 1976, `${plainDate} should be in yearOfWeek 1976`);
+}
+for (let i = 1; i <= 2; ++i) {
+ const plainDate = new Temporal.PlainDate(1977, 1, i);
+ assert.sameValue(plainDate.yearOfWeek, 1976, `${plainDate} should be in yearOfWeek 1976`);
+}
+
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/yearOfWeek/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/yearOfWeek/branding.js
new file mode 100644
index 0000000000..6ade02cfdd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/yearOfWeek/branding.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-get-temporal.plaindate.prototype.yearofweek
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const yearOfWeek = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "yearOfWeek").get;
+
+assert.sameValue(typeof yearOfWeek, "function");
+
+assert.throws(TypeError, () => yearOfWeek.call(undefined), "undefined");
+assert.throws(TypeError, () => yearOfWeek.call(null), "null");
+assert.throws(TypeError, () => yearOfWeek.call(true), "true");
+assert.throws(TypeError, () => yearOfWeek.call(""), "empty string");
+assert.throws(TypeError, () => yearOfWeek.call(Symbol()), "symbol");
+assert.throws(TypeError, () => yearOfWeek.call(1), "1");
+assert.throws(TypeError, () => yearOfWeek.call({}), "plain object");
+assert.throws(TypeError, () => yearOfWeek.call(Temporal.PlainDate), "Temporal.PlainDate");
+assert.throws(TypeError, () => yearOfWeek.call(Temporal.PlainDate.prototype), "Temporal.PlainDate.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/yearOfWeek/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/yearOfWeek/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/yearOfWeek/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/yearOfWeek/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/yearOfWeek/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..e9894eacf6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/yearOfWeek/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plaindate.prototype.yearofweek
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const yearOfWeekOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "yearOfWeek");
+Object.defineProperty(Temporal.Calendar.prototype, "yearOfWeek", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("yearOfWeek should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDate(2000, 5, 2, "iso8601");
+instance.yearOfWeek;
+
+Object.defineProperty(Temporal.Calendar.prototype, "yearOfWeek", yearOfWeekOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/yearOfWeek/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/yearOfWeek/custom.js
new file mode 100644
index 0000000000..bbcec99fa4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/yearOfWeek/custom.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-get-temporal.plaindate.prototype.yearofweek
+description: Custom calendar tests for yearOfWeek().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ yearOfWeek(...args) {
+ ++calls;
+ assert.compareArray(args, [pd], "yearOfWeek arguments");
+ return 7;
+ }
+}
+
+const calendar = new CustomCalendar();
+const pd = new Temporal.PlainDate(1830, 8, 25, calendar);
+const result = pd.yearOfWeek;
+assert.sameValue(result, 7, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/yearOfWeek/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/yearOfWeek/prop-desc.js
new file mode 100644
index 0000000000..1e0463d2c0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/yearOfWeek/prop-desc.js
@@ -0,0 +1,17 @@
+// |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-get-temporal.plaindate.prototype.yearofweek
+description: The "yearOfWeek" property of Temporal.PlainDate.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "yearOfWeek");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/yearOfWeek/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/yearOfWeek/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/yearOfWeek/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/yearOfWeek/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/yearOfWeek/validate-calendar-value.js
new file mode 100644
index 0000000000..ec291bd0ff
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/yearOfWeek/validate-calendar-value.js
@@ -0,0 +1,55 @@
+// |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-get-temporal.plaindate.prototype.yearofweek
+description: Validate result returned from calendar yearOfWeek() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [Infinity, RangeError],
+ [-Infinity, RangeError],
+ [Symbol("foo"), TypeError],
+ [7n, TypeError],
+ [NaN, RangeError],
+ ["string", TypeError],
+ [{}, TypeError],
+ [null, TypeError],
+ [true, TypeError],
+ [false, TypeError],
+ [7.1, RangeError],
+ [-0.1, RangeError],
+ [NaN, RangeError],
+ ["7", TypeError],
+ ["7.5", TypeError],
+ [{valueOf() { return 7; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ yearOfWeek() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainDate(1981, 12, 15, calendar);
+ assert.throws(error, () => instance.yearOfWeek, `${typeof result} ${String(result)} not converted to integer`);
+});
+
+const preservedResults = [
+ -7,
+];
+
+preservedResults.forEach(result => {
+ const calendar = new class extends Temporal.Calendar {
+ yearOfWeek() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainDate(1981, 12, 15, calendar);
+ assert.sameValue(instance.yearOfWeek, result, `${typeof result} ${String(result)} preserved`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/subclass.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/subclass.js
new file mode 100644
index 0000000000..a268bb672d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/subclass.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.plaindate
+description: Test for Temporal.PlainDate subclassing.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+class CustomPlainDate extends Temporal.PlainDate {
+}
+
+const instance = new CustomPlainDate(2000, 5, 2);
+TemporalHelpers.assertPlainDate(instance, 2000, 5, "M05", 2);
+assert.sameValue(Object.getPrototypeOf(instance), CustomPlainDate.prototype, "Instance of CustomPlainDate");
+assert(instance instanceof CustomPlainDate, "Instance of CustomPlainDate");
+assert(instance instanceof Temporal.PlainDate, "Instance of Temporal.PlainDate");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/builtin.js
new file mode 100644
index 0000000000..27b7011485
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/builtin.js
@@ -0,0 +1,30 @@
+// |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.plaindatetime
+description: Tests that Temporal.PlainDateTime meets the requirements for built-in objects
+info: |
+ 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.PlainDateTime),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime),
+ Function.prototype, "prototype");
+
+assert.sameValue(typeof Temporal.PlainDateTime.prototype,
+ "object", "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/calendar-case-insensitive.js
new file mode 100644
index 0000000000..1f03ee3a0b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/calendar-case-insensitive.js
@@ -0,0 +1,16 @@
+// |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.plaindatetime
+description: Calendar names are case-insensitive
+features: [Temporal]
+---*/
+
+const arg = "iSo8601";
+
+const result = new Temporal.PlainDateTime(2000, 5, 2, 15, 23, 30, 987, 654, 321, arg);
+assert.sameValue(result.calendarId, "iso8601", "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/calendar-number.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/calendar-number.js
new file mode 100644
index 0000000000..a9482df5bc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/calendar-number.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.plaindatetime
+description: A number is not allowed to be a calendar
+features: [Temporal]
+---*/
+
+const numbers = [
+ 1,
+ -19761118,
+ 19761118,
+ 1234567890,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => new Temporal.PlainDateTime(2000, 5, 2, 15, 23, 30, 987, 654, 321, arg),
+ "A number is not a valid ISO string for Calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/calendar-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/calendar-string.js
new file mode 100644
index 0000000000..0c2604006f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/calendar-string.js
@@ -0,0 +1,16 @@
+// |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.plaindatetime.constructor
+description: A calendar ID is valid input for Calendar
+features: [Temporal]
+---*/
+
+const arg = "iso8601";
+
+const result = new Temporal.PlainDateTime(2000, 5, 2, 15, 23, 30, 987, 654, 321, arg);
+assert.sameValue(result.getISOFields().calendar, "iso8601", `Calendar created from string "${arg}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/calendar-temporal-object.js
new file mode 100644
index 0000000000..60205456d5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/calendar-temporal-object.js
@@ -0,0 +1,41 @@
+// |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.plaindatetime
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal-totemporalcalendar step 1.b:
+ b. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const plainDate = new Temporal.PlainDate(2000, 5, 2);
+const plainDateTime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const plainMonthDay = new Temporal.PlainMonthDay(5, 2);
+const plainYearMonth = new Temporal.PlainYearMonth(2000, 5);
+const zonedDateTime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+
+[plainDate, plainDateTime, plainMonthDay, plainYearMonth, zonedDateTime].forEach((arg) => {
+ const actual = [];
+ const expected = [];
+
+ const calendar = arg.getISOFields().calendar;
+
+ Object.defineProperty(arg, "calendar", {
+ get() {
+ actual.push("get calendar");
+ return calendar;
+ },
+ });
+
+ const result = new Temporal.PlainDateTime(2000, 5, 2, 15, 23, 30, 987, 654, 321, arg);
+ assert.sameValue(result.getISOFields().calendar, calendar, "Temporal object coerced to calendar");
+
+ assert.compareArray(actual, expected, "calendar getter not called");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/calendar-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/calendar-undefined.js
new file mode 100644
index 0000000000..1ae70ea0ae
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/calendar-undefined.js
@@ -0,0 +1,25 @@
+// |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.plaindatetime
+description: Calendar argument defaults to the built-in ISO 8601 calendar
+features: [Temporal]
+---*/
+
+const dateTimeArgs = [2020, 12, 24, 12, 34, 56, 123, 456, 789];
+
+Object.defineProperty(Temporal.Calendar, "from", {
+ get() {
+ throw new Test262Error("Should not get Calendar.from");
+ },
+});
+
+const dateTimeExplicit = new Temporal.PlainDateTime(...dateTimeArgs, undefined);
+assert.sameValue(dateTimeExplicit.calendarId, "iso8601");
+
+const dateTimeImplicit = new Temporal.PlainDateTime(...dateTimeArgs);
+assert.sameValue(dateTimeImplicit.calendarId, "iso8601");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/calendar-wrong-type.js
new file mode 100644
index 0000000000..593a7007a9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/calendar-wrong-type.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.plaindatetime
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or object for Calendar
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const primitiveTests = [
+ [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,
+ () => new Temporal.PlainDateTime(2000, 5, 2, 15, 23, 30, 987, 654, 321, arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => new Temporal.PlainDateTime(2000, 5, 2, 15, 23, 30, 987, 654, 321, arg), `${description} is not a valid object and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..3f484cfd18
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-builtin-calendar-no-array-iteration.js
@@ -0,0 +1,24 @@
+// |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.plaindatetime.compare
+description: >
+ Calling the method with a property bag argument with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const arg = { year: 2000, month: 5, day: 2, hour: 21, minute: 43, second: 5, calendar: "iso8601" };
+Temporal.PlainDateTime.compare(arg, new Temporal.PlainDateTime(1976, 11, 18));
+Temporal.PlainDateTime.compare(new Temporal.PlainDateTime(1976, 11, 18), arg);
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-number.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-number.js
new file mode 100644
index 0000000000..a14005f153
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-number.js
@@ -0,0 +1,31 @@
+// |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.plaindatetime.compare
+description: A number cannot be used in place of a Temporal.PlainDateTime
+features: [Temporal]
+---*/
+
+const numbers = [
+ 1,
+ 19761118,
+ -19761118,
+ 1234567890,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => Temporal.PlainDateTime.compare(arg, new Temporal.PlainDateTime(1976, 11, 18)),
+ `A number (${arg}) is not a valid ISO string for PlainDateTime (first argument)`
+ );
+ assert.throws(
+ TypeError,
+ () => Temporal.PlainDateTime.compare(new Temporal.PlainDateTime(1976, 11, 18), arg),
+ `A number (${arg}) is not a valid ISO string for PlainDateTime (second argument)`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-object-insufficient-data.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-object-insufficient-data.js
new file mode 100644
index 0000000000..b8f944f95a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-object-insufficient-data.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.plaindatetime.compare
+description: Plain object arguments may throw if they do not contain sufficient information
+features: [Temporal]
+---*/
+
+const dt1 = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789);
+const dt2 = new Temporal.PlainDateTime(2019, 10, 29, 10, 46, 38, 271, 986, 102);
+
+assert.throws(
+ TypeError,
+ () => Temporal.PlainDateTime.compare({ year: 1976 }, dt2),
+ "object must contain at least the required properties (first arg)"
+);
+
+assert.throws(
+ TypeError,
+ () => Temporal.PlainDateTime.compare(dt1, { year: 2019 }),
+ "object must contain at least the required properties (second arg)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-plaindate.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-plaindate.js
new file mode 100644
index 0000000000..c5adfcf743
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-plaindate.js
@@ -0,0 +1,31 @@
+// |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.plaindatetime.compare
+description: Fast path for converting Temporal.PlainDate to Temporal.PlainDateTime by reading internal slots
+info: |
+ sec-temporal.plaindatetime.compare steps 1–2:
+ 1. Set _one_ to ? ToTemporalDateTime(_one_).
+ 2. Set _two_ to ? ToTemporalDateTime(_two_).
+ sec-temporal-totemporaldatetime step 2.b:
+ b. If _item_ has an [[InitializedTemporalDate]] internal slot, then
+ i. Return ? CreateTemporalDateTime(_item_.[[ISOYear]], _item_.[[ISOMonth]], _item_.[[ISODay]], 0, 0, 0, 0, 0, 0, _item_.[[Calendar]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+TemporalHelpers.checkToTemporalPlainDateTimeFastPath((date) => {
+ const result = Temporal.PlainDateTime.compare(date, datetime);
+ assert.sameValue(result, -1, "PlainDate is converted to midnight");
+});
+
+TemporalHelpers.checkToTemporalPlainDateTimeFastPath((date) => {
+ const result = Temporal.PlainDateTime.compare(datetime, date);
+ assert.sameValue(result, 1, "PlainDate is converted to midnight");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-propertybag-calendar-case-insensitive.js
new file mode 100644
index 0000000000..59b11ece54
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-propertybag-calendar-case-insensitive.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.plaindatetime.compare
+description: The calendar name is case-insensitive
+features: [Temporal]
+---*/
+
+const calendar = "IsO8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result1 = Temporal.PlainDateTime.compare(arg, new Temporal.PlainDateTime(1976, 11, 18));
+assert.sameValue(result1, 0, "Calendar is case-insensitive (first argument)");
+const result2 = Temporal.PlainDateTime.compare(new Temporal.PlainDateTime(1976, 11, 18), arg);
+assert.sameValue(result2, 0, "Calendar is case-insensitive (second argument)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-propertybag-calendar-leap-second.js
new file mode 100644
index 0000000000..90391d682a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-propertybag-calendar-leap-second.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.plaindatetime.compare
+description: Leap second is a valid ISO string for a calendar in a property bag
+features: [Temporal]
+---*/
+
+const calendar = "2016-12-31T23:59:60";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result1 = Temporal.PlainDateTime.compare(arg, new Temporal.PlainDateTime(1976, 11, 18));
+assert.sameValue(result1, 0, "leap second is a valid ISO string for calendar (first argument)");
+const result2 = Temporal.PlainDateTime.compare(new Temporal.PlainDateTime(1976, 11, 18), arg);
+assert.sameValue(result2, 0, "leap second is a valid ISO string for calendar (second argument)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-propertybag-calendar-number.js
new file mode 100644
index 0000000000..698f0cbb9f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-propertybag-calendar-number.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.plaindatetime.compare
+description: A number as calendar in a property bag is not accepted
+features: [Temporal]
+---*/
+
+const numbers = [
+ 1,
+ 19970327,
+ -19970327,
+ 1234567890,
+];
+
+for (const calendar of numbers) {
+ const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+ assert.throws(
+ TypeError,
+ () => Temporal.PlainDateTime.compare(arg, new Temporal.PlainDateTime(1976, 11, 18)),
+ "A number is not a valid ISO string for calendar (first argument)"
+ );
+ assert.throws(
+ TypeError,
+ () => Temporal.PlainDateTime.compare(new Temporal.PlainDateTime(1976, 11, 18), arg),
+ "A number is not a valid ISO string for calendar (second argument)"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-propertybag-calendar-string.js
new file mode 100644
index 0000000000..759c69ab8e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-propertybag-calendar-string.js
@@ -0,0 +1,33 @@
+// |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.plaindatetime.compare
+description: A calendar ID is valid input for Calendar
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dateFromFieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dateFromFields");
+Object.defineProperty(Temporal.Calendar.prototype, "dateFromFields", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("dateFromFields should not be looked up");
+ },
+});
+
+const calendar = "iso8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+
+const result1 = Temporal.PlainDateTime.compare(arg, new Temporal.PlainDateTime(1976, 11, 18));
+assert.sameValue(result1, 0, `Calendar created from string "${arg}" (first argument)`);
+
+const result2 = Temporal.PlainDateTime.compare(new Temporal.PlainDateTime(1976, 11, 18), arg);
+assert.sameValue(result2, 0, `Calendar created from string "${arg}" (second argument)`);
+
+Object.defineProperty(Temporal.Calendar.prototype, "dateFromFields", dateFromFieldsOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-propertybag-calendar-wrong-type.js
new file mode 100644
index 0000000000..7fc5b98699
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-propertybag-calendar-wrong-type.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.plaindatetime.compare
+description: >
+ Appropriate error thrown when a calendar property from a property bag cannot
+ be converted to a calendar object or string
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [calendar, description] of primitiveTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ typeof calendar === "string" ? RangeError : TypeError,
+ () => Temporal.PlainDateTime.compare(arg, new Temporal.PlainDateTime(1976, 11, 18)),
+ `${description} does not convert to a valid ISO string (first argument)`
+ );
+ assert.throws(
+ typeof calendar === "string" ? RangeError : TypeError,
+ () => Temporal.PlainDateTime.compare(new Temporal.PlainDateTime(1976, 11, 18), arg),
+ `${description} does not convert to a valid ISO string (second argument)`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+ [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields()
+];
+
+for (const [calendar, description] of typeErrorTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(TypeError, () => Temporal.PlainDateTime.compare(arg, new Temporal.PlainDateTime(1976, 11, 18)), `${description} is not a valid property bag and does not convert to a string (first argument)`);
+ assert.throws(TypeError, () => Temporal.PlainDateTime.compare(new Temporal.PlainDateTime(1976, 11, 18), arg), `${description} is not a valid property bag and does not convert to a string (second argument)`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-propertybag-calendar-year-zero.js
new file mode 100644
index 0000000000..88fc524ec8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-propertybag-calendar-year-zero.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.plaindatetime.compare
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T17:45",
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+01:00",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDateTime.compare(arg, new Temporal.PlainDateTime(1976, 11, 18)),
+ "reject minus zero as extended year (first argument)"
+ );
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDateTime.compare(new Temporal.PlainDateTime(1976, 11, 18), arg),
+ "reject minus zero as extended year (second argument)"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..2543b83cd4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-calendar-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.plaindatetime.compare
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["1976-11-18T15:23[u-ca=iso8601]", "without time zone"],
+ ["1976-11-18T15:23[UTC][u-ca=iso8601]", "with time zone"],
+ ["1976-11-18T15:23[!u-ca=iso8601]", "with ! and no time zone"],
+ ["1976-11-18T15:23[UTC][!u-ca=iso8601]", "with ! and time zone"],
+ ["1976-11-18T15:23[u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.PlainDateTime.compare(arg, arg);
+
+ assert.sameValue(
+ result,
+ 0,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..4a51bd9821
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-critical-unknown-annotation.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.plaindatetime.compare
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "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]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDateTime.compare(arg, new Temporal.PlainDateTime(1976, 11, 18)),
+ `reject unknown annotation with critical flag: ${arg} (first argument)`
+ );
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDateTime.compare(new Temporal.PlainDateTime(1976, 11, 18), arg),
+ `reject unknown annotation with critical flag: ${arg} (second argument)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..49431f979f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-date-with-utc-offset.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.plaindatetime.compare
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+---*/
+
+const validStrings = [
+ "1976-11-18T15:23+00:00",
+ "1976-11-18T15:23+00:00[UTC]",
+ "1976-11-18T15:23+00:00[!UTC]",
+ "1976-11-18T15:23-02:30[America/St_Johns]",
+];
+
+for (const arg of validStrings) {
+ const result = Temporal.PlainDateTime.compare(arg, arg);
+
+ assert.sameValue(
+ result,
+ 0,
+ `"${arg}" is a valid UTC offset with time for PlainDateTime`
+ );
+}
+
+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,
+ () => Temporal.PlainDateTime.compare(arg, new Temporal.PlainDateTime(1976, 11, 18)),
+ `"${arg}" UTC offset without time is not valid for PlainDateTime (first argument)`
+ );
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDateTime.compare(new Temporal.PlainDateTime(1976, 11, 18), arg),
+ `"${arg}" UTC offset without time is not valid for PlainDateTime (second argument)`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..0505be4dfd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-multiple-calendar.js
@@ -0,0 +1,37 @@
+// |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.plaindatetime.compare
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[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]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDateTime.compare(arg, new Temporal.PlainDateTime(1976, 11, 18)),
+ `reject more than one calendar annotation if any critical: ${arg} (first argument)`
+ );
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDateTime.compare(new Temporal.PlainDateTime(1976, 11, 18), arg),
+ `reject more than one calendar annotation if any critical: ${arg} (second argument)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..93f1d3262a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-multiple-time-zone.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.plaindatetime.compare
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "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]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDateTime.compare(arg, new Temporal.PlainDateTime(1976, 11, 18)),
+ `reject more than one time zone annotation: ${arg} (first argument)`
+ );
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDateTime.compare(new Temporal.PlainDateTime(1976, 11, 18), arg),
+ `reject more than one time zone annotation: ${arg} (second argument)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-time-separators.js
new file mode 100644
index 0000000000..3094da507c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/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.plaindatetime.compare
+description: Time separator in string argument can vary
+features: [Temporal]
+---*/
+
+const dateTime = new Temporal.PlainDateTime(1976, 11, 18, 15, 23);
+const tests = [
+ ["1976-11-18T15:23", "uppercase T"],
+ ["1976-11-18t15:23", "lowercase T"],
+ ["1976-11-18 15:23", "space between date and time"],
+];
+
+tests.forEach(([arg, description]) => {
+ assert.sameValue(
+ Temporal.PlainDateTime.compare(arg, dateTime),
+ 0,
+ `variant time separators (${description}), first argument`
+ );
+
+ assert.sameValue(
+ Temporal.PlainDateTime.compare(dateTime, arg),
+ 0,
+ `variant time separators (${description}), second argument`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..b58759c3cf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-time-zone-annotation.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.plaindatetime.compare
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["1976-11-18T15:23[Asia/Kolkata]", "named, with no offset"],
+ ["1976-11-18T15:23[!Europe/Vienna]", "named, with ! and no offset"],
+ ["1976-11-18T15:23[+00:00]", "numeric, with no offset"],
+ ["1976-11-18T15:23[!-02:30]", "numeric, with ! and no offset"],
+ ["1976-11-18T15:23+00:00[UTC]", "named, with offset"],
+ ["1976-11-18T15:23+00:00[!Africa/Abidjan]", "named, with offset and !"],
+ ["1976-11-18T15:23+00:00[+01:00]", "numeric, with offset"],
+ ["1976-11-18T15:23+00:00[!-08:00]", "numeric, with offset and !"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.PlainDateTime.compare(arg, arg);
+
+ assert.sameValue(
+ result,
+ 0,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..45ffdf03c3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-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.plaindatetime.compare
+description: Various forms of unknown annotation
+features: [Temporal]
+---*/
+
+const tests = [
+ ["1976-11-18T15:23[foo=bar]", "alone"],
+ ["1976-11-18T15:23[UTC][foo=bar]", "with time zone"],
+ ["1976-11-18T15:23[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["1976-11-18T15:23[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["1976-11-18T15:23[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.PlainDateTime.compare(arg, arg);
+
+ assert.sameValue(
+ result,
+ 0,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..35c5df333a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-with-utc-designator.js
@@ -0,0 +1,29 @@
+// |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.plaindatetime.compare
+description: RangeError thrown if a string with UTC designator is used as a PlainDateTime
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "2019-10-01T09:00:00Z",
+ "2019-10-01T09:00:00Z[UTC]",
+];
+const dateTime = new Temporal.PlainDateTime(2000, 5, 2);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDateTime.compare(arg, dateTime),
+ "String with UTC designator should not be valid as a PlainDateTime (first argument)"
+ );
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDateTime.compare(arg, dateTime),
+ "String with UTC designator should not be valid as a PlainDateTime (second argument)"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-wrong-type.js
new file mode 100644
index 0000000000..efee691db5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-wrong-type.js
@@ -0,0 +1,47 @@
+// |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.plaindatetime.compare
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainDateTime
+features: [BigInt, Symbol, Temporal]
+---*/
+
+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,
+ () => Temporal.PlainDateTime.compare(arg, new Temporal.PlainDateTime(1976, 11, 18)),
+ `${description} does not convert to a valid ISO string (first argument)`
+ );
+ assert.throws(
+ typeof arg === 'string' ? RangeError : TypeError,
+ () => Temporal.PlainDateTime.compare(new Temporal.PlainDateTime(1976, 11, 18), arg),
+ `${description} does not convert to a valid ISO string (second argument)`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.PlainDateTime, "Temporal.PlainDateTime, object"],
+ [Temporal.PlainDateTime.prototype, "Temporal.PlainDateTime.prototype, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => Temporal.PlainDateTime.compare(arg, new Temporal.PlainDateTime(1976, 11, 18)), `${description} is not a valid property bag and does not convert to a string (first argument)`);
+ assert.throws(TypeError, () => Temporal.PlainDateTime.compare(new Temporal.PlainDateTime(1976, 11, 18), arg), `${description} is not a valid property bag and does not convert to a string (second argument)`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-zoneddatetime-negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-zoneddatetime-negative-epochnanoseconds.js
new file mode 100644
index 0000000000..36f37fc751
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-zoneddatetime-negative-epochnanoseconds.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.plaindatetime.compare
+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]
+---*/
+
+const zoned = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC");
+const plain = new Temporal.PlainDateTime(1969, 7, 24, 16, 50, 35, 0, 0, 1);
+
+// This code path shows up anywhere we convert an exact time, before the Unix
+// epoch, with nonzero microseconds or nanoseconds, into a wall time.
+
+const result1 = Temporal.PlainDateTime.compare(plain, zoned);
+assert.sameValue(result1, 0);
+
+const result2 = Temporal.PlainDateTime.compare(zoned, plain);
+assert.sameValue(result2, 0);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..c67b2534f1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.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.plaindatetime.compare
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const plain = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+ assert.throws(RangeError, () => Temporal.PlainDateTime.compare(datetime, plain));
+ assert.throws(RangeError, () => Temporal.PlainDateTime.compare(plain, datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..0e209ef0e0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.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.plaindatetime.compare
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const plain = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => Temporal.PlainDateTime.compare(datetime, plain),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+ assert.throws(
+ TypeError,
+ () => Temporal.PlainDateTime.compare(plain, datetime),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..77f7867516
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.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.plaindatetime.compare
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const plain = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+ assert.throws(RangeError, () => Temporal.PlainDateTime.compare(datetime, plain));
+ assert.throws(RangeError, () => Temporal.PlainDateTime.compare(plain, datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..e88cdd726f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,30 @@
+// |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.plaindatetime.compare
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const plain = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+ assert.throws(TypeError, () => Temporal.PlainDateTime.compare(datetime, plain));
+ assert.throws(TypeError, () => Temporal.PlainDateTime.compare(plain, datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/basic.js
new file mode 100644
index 0000000000..75ffb1d628
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/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.plaindatetime.compare
+description: Checking a typical case (nothing undefined, no NaNs, does not throw, etc.)
+features: [Temporal]
+---*/
+
+const dt1 = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789);
+const dt2 = new Temporal.PlainDateTime(2019, 10, 29, 10, 46, 38, 271, 986, 102);
+const dt3 = new Temporal.PlainDateTime(2019, 10, 29, 10, 46, 38, 271, 986, 102);
+const dt4 = new Temporal.PlainDateTime(2019, 10, 29, 15, 23, 30, 123, 456, 789);
+const dt5 = new Temporal.PlainDateTime(1976, 11, 18, 10, 46, 38, 271, 986, 102);
+
+assert.sameValue(Temporal.PlainDateTime.compare(dt1, dt1), 0, "equal");
+assert.sameValue(Temporal.PlainDateTime.compare(dt1, dt2), -1, "smaller/larger");
+assert.sameValue(Temporal.PlainDateTime.compare(dt2, dt1), 1, "larger/smaller");
+assert.sameValue(Temporal.PlainDateTime.compare(dt2, dt3), 0, "equal different object");
+assert.sameValue(Temporal.PlainDateTime.compare(dt3, dt4), -1, "same date, earlier time");
+assert.sameValue(Temporal.PlainDateTime.compare(dt3, dt5), 1, "same time, later date");
+
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/builtin.js
new file mode 100644
index 0000000000..83800308ef
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/builtin.js
@@ -0,0 +1,33 @@
+// |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.plaindatetime.compare
+description: Tests that Temporal.PlainDateTime.compare meets the requirements for built-in objects
+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.PlainDateTime.compare),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.compare),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.compare),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.compare.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/calendar-datefromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..7d5c0a293f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/calendar-datefromfields-called-with-null-prototype-fields.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.plaindatetime.compare
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const arg1 = { year: 2000, month: 5, day: 2, calendar };
+const arg2 = new Temporal.PlainDateTime(1976, 11, 18);
+
+Temporal.PlainDateTime.compare(arg1, arg2);
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar (first argument)");
+
+calendar.dateFromFieldsCallCount = 0;
+
+Temporal.PlainDateTime.compare(arg2, arg1);
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar (second argument)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/calendar-fields-iterable.js
new file mode 100644
index 0000000000..33f0a9b7de
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/calendar-fields-iterable.js
@@ -0,0 +1,41 @@
+// |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.plaindatetime.compare
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plaindatetime.compare steps 1–2:
+ 1. Set _one_ to ? ToTemporalDateTime(_one_).
+ 2. Set _two_ to ? ToTemporalDateTime(_two_).
+ sec-temporal-totemporaldatetime step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"hour"*, *"microsecond"*, *"millisecond"*, *"minute"*, *"month"*, *"monthCode"*, *"nanosecond"*, *"second"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+Temporal.PlainDateTime.compare(
+ { year: 2000, month: 5, day: 2, calendar: calendar1 },
+ { year: 2001, month: 6, day: 3, calendar: calendar2 },
+);
+
+assert.sameValue(calendar1.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar1.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar1.iteratorExhausted[0], "iterated through the whole iterable");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/calendar-ignored.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/calendar-ignored.js
new file mode 100644
index 0000000000..1c533b58a3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/calendar-ignored.js
@@ -0,0 +1,23 @@
+// |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.plaindatetime.compare
+description: Calendar is not taken into account for the comparison.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar1 = TemporalHelpers.calendarThrowEverything();
+const calendar2 = TemporalHelpers.calendarThrowEverything();
+const dt1 = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789, calendar1);
+const dt2 = new Temporal.PlainDateTime(2019, 10, 29, 10, 46, 38, 271, 986, 102, calendar1);
+const dt3 = new Temporal.PlainDateTime(2019, 10, 29, 10, 46, 38, 271, 986, 102, calendar1);
+const dt4 = new Temporal.PlainDateTime(2019, 10, 29, 10, 46, 38, 271, 986, 102, calendar2);
+
+assert.sameValue(Temporal.PlainDateTime.compare(dt1, dt2), -1, "smaller");
+assert.sameValue(Temporal.PlainDateTime.compare(dt2, dt3), 0, "equal with same calendar");
+assert.sameValue(Temporal.PlainDateTime.compare(dt2, dt4), 0, "equal with different calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/calendar-temporal-object.js
new file mode 100644
index 0000000000..13b68d70e2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/calendar-temporal-object.js
@@ -0,0 +1,32 @@
+// |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.plaindatetime.compare
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plaindatetime.compare steps 1–2:
+ 1. Set _one_ to ? ToTemporalDateTime(_one_).
+ 2. Set _two_ to ? ToTemporalDateTime(_two_).
+ sec-temporal-totemporaldatetime step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ Temporal.PlainDateTime.compare(
+ { year: 2000, month: 5, day: 2, calendar: temporalObject },
+ { year: 2001, month: 6, day: 3, calendar: temporalObject },
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/cast.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/cast.js
new file mode 100644
index 0000000000..dfd83e2876
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/cast.js
@@ -0,0 +1,38 @@
+// |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.plaindatetime.compare
+description: Arguments may be casted (string, plain object)
+features: [Temporal]
+---*/
+
+const dt1 = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789);
+const dt2 = new Temporal.PlainDateTime(2019, 10, 29, 10, 46, 38, 271, 986, 102);
+
+assert.sameValue(
+ Temporal.PlainDateTime.compare({ year: 1976, month: 11, day: 18, hour: 15 }, dt2),
+ -1,
+ "casts first argument (plain object)"
+);
+
+assert.sameValue(
+ Temporal.PlainDateTime.compare("1976-11-18T15:23:30.123456789", dt2),
+ -1,
+ "casts first argument (string)"
+);
+
+assert.sameValue(
+ Temporal.PlainDateTime.compare(dt1, { year: 2019, month: 10, day: 29, hour: 10 }),
+ -1,
+ "casts second argument (plain object)"
+);
+
+assert.sameValue(
+ Temporal.PlainDateTime.compare(dt1, "2019-10-29T10:46:38.271986102"),
+ -1,
+ "casts second argument (string)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..a296792257
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/constructor-in-calendar-fields.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.plaindatetime.prototype.compare
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+
+assert.throws(RangeError, () => Temporal.PlainDateTime.compare(arg, new Temporal.PlainDateTime(1976, 11, 18)));
+assert.throws(RangeError, () => Temporal.PlainDateTime.compare(new Temporal.PlainDateTime(1976, 11, 18), arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/duplicate-calendar-fields.js
new file mode 100644
index 0000000000..7f573ce4d9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/duplicate-calendar-fields.js
@@ -0,0 +1,20 @@
+// |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.plaindatetime.prototype.compare
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+
+for (const extra_fields of [['foo', 'foo'], ['day'], ['hour'], ['microsecond'], ['millisecond'], ['minute'], ['month'], ['monthCode'], ['nanosecond'], ['second'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar };
+
+ assert.throws(RangeError, () => Temporal.PlainDateTime.compare(arg, new Temporal.PlainDateTime(1976, 11, 18)));
+ assert.throws(RangeError, () => Temporal.PlainDateTime.compare(new Temporal.PlainDateTime(1976, 11, 18), arg));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/exhaustive.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/exhaustive.js
new file mode 100644
index 0000000000..0be8ab8d5c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/exhaustive.js
@@ -0,0 +1,167 @@
+// |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.plaindatetime.compare
+description: Tests for compare() with each possible outcome
+features: [Temporal]
+---*/
+
+const cal1 = "iso8601";
+const cal2 = new (class extends Temporal.Calendar { id = "custom"; })("iso8601");
+
+assert.sameValue(
+ Temporal.PlainDateTime.compare(
+ new Temporal.PlainDateTime(2000, 5, 31, 12, 15, 45, 333, 777, 111, cal1),
+ new Temporal.PlainDateTime(1987, 5, 31, 12, 15, 45, 333, 777, 111, cal2)
+ ),
+ 1,
+ "year >"
+);
+assert.sameValue(
+ Temporal.PlainDateTime.compare(
+ new Temporal.PlainDateTime(1981, 12, 15, 6, 30, 15, 222, 444, 6, cal1),
+ new Temporal.PlainDateTime(2048, 12, 15, 6, 30, 15, 222, 444, 6, cal2)
+ ),
+ -1,
+ "year <"
+);
+assert.sameValue(
+ Temporal.PlainDateTime.compare(
+ new Temporal.PlainDateTime(2000, 5, 31, 12, 15, 45, 333, 777, 111, cal1),
+ new Temporal.PlainDateTime(2000, 3, 31, 12, 15, 45, 333, 777, 111, cal2)
+ ),
+ 1,
+ "month >"
+);
+assert.sameValue(
+ Temporal.PlainDateTime.compare(
+ new Temporal.PlainDateTime(1981, 4, 15, 6, 30, 15, 222, 444, 6, cal1),
+ new Temporal.PlainDateTime(1981, 12, 15, 6, 30, 15, 222, 444, 6, cal2)
+ ),
+ -1,
+ "month <"
+);
+assert.sameValue(
+ Temporal.PlainDateTime.compare(
+ new Temporal.PlainDateTime(2000, 5, 31, 12, 15, 45, 333, 777, 111, cal1),
+ new Temporal.PlainDateTime(2000, 5, 14, 12, 15, 45, 333, 777, 111, cal2)
+ ),
+ 1,
+ "day >"
+);
+assert.sameValue(
+ Temporal.PlainDateTime.compare(
+ new Temporal.PlainDateTime(1981, 4, 15, 6, 30, 15, 222, 444, 6, cal1),
+ new Temporal.PlainDateTime(1981, 4, 21, 6, 30, 15, 222, 444, 6, cal2)
+ ),
+ -1,
+ "day <"
+);
+assert.sameValue(
+ Temporal.PlainDateTime.compare(
+ new Temporal.PlainDateTime(2000, 5, 31, 12, 15, 45, 333, 777, 111, cal1),
+ new Temporal.PlainDateTime(2000, 5, 31, 6, 15, 45, 333, 777, 111, cal2)
+ ),
+ 1,
+ "hour >"
+);
+assert.sameValue(
+ Temporal.PlainDateTime.compare(
+ new Temporal.PlainDateTime(1981, 4, 15, 6, 30, 15, 222, 444, 6, cal1),
+ new Temporal.PlainDateTime(1981, 4, 15, 22, 30, 15, 222, 444, 6, cal2)
+ ),
+ -1,
+ "hour <"
+);
+assert.sameValue(
+ Temporal.PlainDateTime.compare(
+ new Temporal.PlainDateTime(2000, 5, 31, 12, 15, 45, 333, 777, 111, cal1),
+ new Temporal.PlainDateTime(2000, 5, 31, 12, 15, 22, 333, 777, 111, cal2)
+ ),
+ 1,
+ "minute >"
+);
+assert.sameValue(
+ Temporal.PlainDateTime.compare(
+ new Temporal.PlainDateTime(1981, 4, 15, 6, 30, 15, 222, 444, 6, cal1),
+ new Temporal.PlainDateTime(1981, 4, 15, 6, 57, 15, 222, 444, 6, cal2)
+ ),
+ -1,
+ "minute <"
+);
+assert.sameValue(
+ Temporal.PlainDateTime.compare(
+ new Temporal.PlainDateTime(2000, 5, 31, 12, 15, 6, 333, 777, 111, cal1),
+ new Temporal.PlainDateTime(2000, 5, 31, 12, 15, 5, 333, 777, 111, cal2)
+ ),
+ 1,
+ "second >"
+);
+assert.sameValue(
+ Temporal.PlainDateTime.compare(
+ new Temporal.PlainDateTime(1981, 4, 15, 6, 30, 3, 222, 444, 6, cal1),
+ new Temporal.PlainDateTime(1981, 4, 15, 6, 30, 4, 222, 444, 6, cal2)
+ ),
+ -1,
+ "second <"
+);
+assert.sameValue(
+ Temporal.PlainDateTime.compare(
+ new Temporal.PlainDateTime(2000, 5, 31, 12, 15, 45, 6, 777, 111, cal1),
+ new Temporal.PlainDateTime(2000, 5, 31, 12, 15, 45, 5, 777, 111, cal2)
+ ),
+ 1,
+ "millisecond >"
+);
+assert.sameValue(
+ Temporal.PlainDateTime.compare(
+ new Temporal.PlainDateTime(1981, 4, 15, 6, 30, 15, 3, 444, 6, cal1),
+ new Temporal.PlainDateTime(1981, 4, 15, 6, 30, 15, 4, 444, 6, cal2)
+ ),
+ -1,
+ "millisecond <"
+);
+assert.sameValue(
+ Temporal.PlainDateTime.compare(
+ new Temporal.PlainDateTime(2000, 5, 31, 12, 15, 45, 333, 6, 111, cal1),
+ new Temporal.PlainDateTime(2000, 5, 31, 12, 15, 45, 333, 5, 111, cal2)
+ ),
+ 1,
+ "microsecond >"
+);
+assert.sameValue(
+ Temporal.PlainDateTime.compare(
+ new Temporal.PlainDateTime(1981, 4, 15, 6, 30, 15, 222, 3, 6, cal1),
+ new Temporal.PlainDateTime(1981, 4, 15, 6, 30, 15, 222, 4, 6, cal2)
+ ),
+ -1,
+ "microsecond <"
+);
+assert.sameValue(
+ Temporal.PlainDateTime.compare(
+ new Temporal.PlainDateTime(2000, 5, 31, 12, 15, 45, 333, 777, 999, cal1),
+ new Temporal.PlainDateTime(2000, 5, 31, 12, 15, 45, 333, 777, 111, cal2)
+ ),
+ 1,
+ "nanosecond >"
+);
+assert.sameValue(
+ Temporal.PlainDateTime.compare(
+ new Temporal.PlainDateTime(1981, 4, 15, 6, 30, 15, 222, 444, 0, cal1),
+ new Temporal.PlainDateTime(1981, 4, 15, 6, 30, 15, 222, 444, 6, cal2)
+ ),
+ -1,
+ "nanosecond <"
+);
+assert.sameValue(
+ Temporal.PlainDateTime.compare(
+ new Temporal.PlainDateTime(2000, 5, 31, 12, 15, 45, 333, 777, 111, cal1),
+ new Temporal.PlainDateTime(2000, 5, 31, 12, 15, 45, 333, 777, 111, cal2)
+ ),
+ 0,
+ "="
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..5e6ea86d52
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/infinity-throws-rangeerror.js
@@ -0,0 +1,33 @@
+// |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.
+
+/*---
+description: Throws if any value in a property bag for either argument is Infinity or -Infinity
+esid: sec-temporal.plaindatetime.compare
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const other = new Temporal.PlainDateTime(2000, 5, 2, 15);
+const base = { year: 2000, month: 5, day: 2, hour: 15, minute: 30, second: 45, millisecond: 987, microsecond: 654, nanosecond: 321 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((prop) => {
+ assert.throws(RangeError, () => Temporal.PlainDateTime.compare({ ...base, [prop]: inf }, other), `${prop} property cannot be ${inf}`);
+
+ assert.throws(RangeError, () => Temporal.PlainDateTime.compare(other, { ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls1 = [];
+ const obj1 = TemporalHelpers.toPrimitiveObserver(calls1, inf, prop);
+ assert.throws(RangeError, () => Temporal.PlainDateTime.compare({ ...base, [prop]: obj1 }, other));
+ assert.compareArray(calls1, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+
+ const calls2 = [];
+ const obj2 = TemporalHelpers.toPrimitiveObserver(calls2, inf, prop);
+ assert.throws(RangeError, () => Temporal.PlainDateTime.compare(other, { ...base, [prop]: obj2 }));
+ assert.compareArray(calls2, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/leap-second.js
new file mode 100644
index 0000000000..fe8e047f09
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/leap-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.plaindatetime.compare
+description: Leap second is a valid ISO string for PlainDateTime
+features: [Temporal]
+---*/
+
+let arg = "2016-12-31T23:59:60";
+const result1 = Temporal.PlainDateTime.compare(arg, new Temporal.PlainDateTime(2016, 12, 31, 23, 59, 59));
+assert.sameValue(result1, 0, "leap second is a valid ISO string for PlainDateTime (first argument)");
+const result2 = Temporal.PlainDateTime.compare(new Temporal.PlainDateTime(2016, 12, 31, 23, 59, 59), arg);
+assert.sameValue(result2, 0, "leap second is a valid ISO string for PlainDateTime (second argument)");
+
+arg = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+
+const result3 = Temporal.PlainDateTime.compare(arg, new Temporal.PlainDateTime(2016, 12, 31, 23, 59, 59));
+assert.sameValue(result3, 0, "second: 60 is constrained in property bag for PlainDateTime (first argument)");
+const result4 = Temporal.PlainDateTime.compare(new Temporal.PlainDateTime(2016, 12, 31, 23, 59, 59), arg);
+assert.sameValue(result4, 0, "second: 60 is constrained in property bag for PlainDateTime (second argument)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/length.js
new file mode 100644
index 0000000000..6c31f059c0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/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.plaindatetime.compare
+description: Temporal.PlainDateTime.compare.length is 2
+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.PlainDateTime.compare, "length", {
+ value: 2,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/name.js
new file mode 100644
index 0000000000..0e55f30590
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/name.js
@@ -0,0 +1,26 @@
+// |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.plaindatetime.compare
+description: Temporal.PlainDateTime.compare.name is "compare"
+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.PlainDateTime.compare, "name", {
+ value: "compare",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/not-a-constructor.js
new file mode 100644
index 0000000000..995cc79eff
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/not-a-constructor.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.plaindatetime.compare
+description: Temporal.PlainDateTime.compare 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.PlainDateTime.compare();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.compare), false,
+ "isConstructor(Temporal.PlainDateTime.compare)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/prop-desc.js
new file mode 100644
index 0000000000..ecdfcdc3a3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/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.plaindatetime.compare
+description: The "compare" property of Temporal.PlainDateTime
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.compare,
+ "function",
+ "`typeof PlainDateTime.compare` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime, "compare", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/proto-in-calendar-fields.js
new file mode 100644
index 0000000000..694e3f2928
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/proto-in-calendar-fields.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.plaindatetime.prototype.compare
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+
+assert.throws(RangeError, () => Temporal.PlainDateTime.compare(arg, new Temporal.PlainDateTime(1976, 11, 18)));
+assert.throws(RangeError, () => Temporal.PlainDateTime.compare(new Temporal.PlainDateTime(1976, 11, 18), arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/read-time-fields-before-datefromfields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/read-time-fields-before-datefromfields.js
new file mode 100644
index 0000000000..37527a010e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/read-time-fields-before-datefromfields.js
@@ -0,0 +1,30 @@
+// |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.plaindatetime.compare
+description: The time fields are read from the object before being passed to dateFromFields().
+info: |
+ sec-temporal.plaindatetime.compare steps 1–2:
+ 1. Set _one_ to ? ToTemporalDateTime(_one_).
+ 2. Set _two_ to ? ToTemporalDateTime(_two_).
+ sec-temporal-totemporaldatetime step 2.e:
+ e. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+ sec-temporal-interprettemporaldatetimefields steps 1–2:
+ 1. Let _timeResult_ be ? ToTemporalTimeRecord(_fields_).
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarMakeInfinityTime();
+const result = Temporal.PlainDateTime.compare(
+ { year: 2000, month: 5, day: 2, hour: 12, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321, calendar },
+ { year: 2000, month: 5, day: 2, hour: 6, minute: 54, second: 32, millisecond: 123, microsecond: 456, nanosecond: 789, calendar },
+);
+
+// will be 0 if the time fields are coerced to their max values due to Infinity
+assert.sameValue(result, 1, "comparison result");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/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/PlainDateTime/compare/use-internal-slots.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/use-internal-slots.js
new file mode 100644
index 0000000000..8899f71885
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/use-internal-slots.js
@@ -0,0 +1,47 @@
+// |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-compareisodatetime
+description: compare() ignores the observable properties and uses internal slots
+features: [Temporal]
+---*/
+
+function CustomError() {}
+
+class AvoidGettersDateTime extends Temporal.PlainDateTime {
+ get year() {
+ throw new CustomError();
+ }
+ get month() {
+ throw new CustomError();
+ }
+ get day() {
+ throw new CustomError();
+ }
+ get hour() {
+ throw new CustomError();
+ }
+ get minute() {
+ throw new CustomError();
+ }
+ get second() {
+ throw new CustomError();
+ }
+ get millisecond() {
+ throw new CustomError();
+ }
+ get microsecond() {
+ throw new CustomError();
+ }
+ get nanosecond() {
+ throw new CustomError();
+ }
+}
+
+const one = new AvoidGettersDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const two = new AvoidGettersDateTime(2006, 3, 25, 6, 54, 32, 123, 456, 789);
+assert.sameValue(Temporal.PlainDateTime.compare(one, two), -1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/year-zero.js
new file mode 100644
index 0000000000..90ed2d8408
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/year-zero.js
@@ -0,0 +1,31 @@
+// |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.
+
+/*---
+description: Negative zero, as an extended year, fails
+esid: sec-temporal.plaindatetime.compare
+features: [Temporal]
+---*/
+
+const ok = new Temporal.PlainDateTime(2000, 5, 2, 15);
+const invalidStrings = [
+ "-000000-12-07",
+ "-000000-12-07T03:24:30",
+ "-000000-12-07T03:24:30+01:00",
+ "-000000-12-07T03:24:30+00:00[UTC]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(RangeError,
+ () => Temporal.PlainDateTime.compare(arg, ok),
+ "Cannot use minus zero as extended year (first argument)"
+ );
+
+ assert.throws(RangeError,
+ () => Temporal.PlainDateTime.compare(ok, arg),
+ "Cannot use minus zero as extended year (second argument)"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/constructor-full.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/constructor-full.js
new file mode 100644
index 0000000000..a0ff2ad2da
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/constructor-full.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.plaindatetime
+description: Checking an explicitly constructed instance with all arguments
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const calendar = Temporal.Calendar.from("iso8601");
+const datetime = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789, calendar);
+
+TemporalHelpers.assertPlainDateTime(datetime,
+ 1976, 11, "M11", 18, 15, 23, 30, 123, 456, 789,
+ "check instance (all arguments supplied)"
+);
+
+assert.sameValue(
+ datetime.getCalendar(),
+ calendar,
+ "calendar supplied in constructor can be extracted and is unchanged"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/constructor.js
new file mode 100644
index 0000000000..9041542e74
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/constructor.js
@@ -0,0 +1,15 @@
+// |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.plaindatetime
+description: Temporal.PlainDateTime constructor cannot be called as a function
+info: |
+ 1. If NewTarget is undefined, throw a TypeError exception.
+features: [Temporal]
+---*/
+
+assert.throws(TypeError, () => Temporal.PlainDateTime(1970, 1, 2, 12, 0, 0));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/datetime-math.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/datetime-math.js
new file mode 100644
index 0000000000..5b57f4bcbd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/datetime-math.js
@@ -0,0 +1,50 @@
+// |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.plaindatetime
+description: Testing combinations of since, until, add, subtract, and negated
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const earlier = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789);
+const later = new Temporal.PlainDateTime(2019, 10, 29, 10, 46, 38, 271, 986, 102);
+const units = ["years", "months", "weeks", "days", "hours", "minutes", "seconds"];
+
+units.forEach((largestUnit) => {
+ const diff = later.since(earlier, { largestUnit });
+ TemporalHelpers.assertDurationsEqual(
+ earlier.since(later, { largestUnit }),
+ diff.negated(),
+ `(${earlier}).since(${later}) == (${later}).since(${earlier}).negated()`
+ );
+ TemporalHelpers.assertDurationsEqual(
+ earlier.until(later, { largestUnit }),
+ diff,
+ `(${earlier}).until(${later}) == (${later}).since(${earlier})`
+ );
+ assert.sameValue(
+ earlier.add(diff).equals(later),
+ true,
+ `(${earlier}).add(${diff}) == (${later})`
+ );
+ assert.sameValue(
+ later.subtract(diff).equals(earlier),
+ true,
+ `(${later}).subtract(${diff}) == (${earlier})`
+ );
+ assert.sameValue(
+ earlier.subtract(diff.negated()).equals(later),
+ true,
+ "symmetrical with regard to negative durations (1)"
+ );
+ assert.sameValue(
+ later.add(diff.negated()).equals(earlier),
+ true,
+ "symmetrical with regard to negative durations (2)"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..c21adc332a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-builtin-calendar-no-array-iteration.js
@@ -0,0 +1,23 @@
+// |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.plaindatetime.from
+description: >
+ Calling the method with a property bag argument with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const arg = { year: 2000, month: 5, day: 2, hour: 21, minute: 43, second: 5, calendar: "iso8601" };
+Temporal.PlainDateTime.from(arg);
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-number.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-number.js
new file mode 100644
index 0000000000..52aa463b20
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-number.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.plaindatetime.from
+description: A number cannot be used in place of a Temporal.PlainDateTime
+features: [Temporal]
+---*/
+
+const numbers = [
+ 1,
+ 19761118,
+ -19761118,
+ 1234567890,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => Temporal.PlainDateTime.from(arg),
+ `A number (${arg}) is not a valid ISO string for PlainDateTime`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-object-month.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-object-month.js
new file mode 100644
index 0000000000..6370d394ed
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-object-month.js
@@ -0,0 +1,35 @@
+// |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.plaindatetime.from
+description: A plain object argument needs to specify a month
+features: [Temporal]
+---*/
+
+assert.throws(
+ RangeError,
+ () => Temporal.PlainDateTime.from({year: 1976, month: 11, monthCode: "M12", day: 18}),
+ "month and monthCode must agree"
+);
+
+assert.throws(
+ TypeError,
+ () => Temporal.PlainDateTime.from({year: 1976, month: undefined, monthCode: undefined, day: 18}),
+ "required prop undefined throws"
+);
+
+assert.throws(
+ TypeError,
+ () => Temporal.PlainDateTime.from({year: 1976, day: 18, hour: 15, minute: 23, second: 30, millisecond: 123}),
+ "required prop missing throws"
+);
+
+assert.throws(
+ TypeError,
+ () => Temporal.PlainDateTime.from({year: 1976, months: 11, day: 18}),
+ "plural \"months\" is not acceptable"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-object.js
new file mode 100644
index 0000000000..8c98885a34
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-object.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.plaindatetime.from
+description: Plain objects are acceptable
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from({year: 1976, month: 11, monthCode: "M11", day: 18}),
+ 1976, 11, "M11", 18, 0, 0, 0, 0, 0, 0,
+ "plain object with month & month code"
+);
+
+assert.throws(
+ TypeError,
+ () => Temporal.PlainDateTime.from({}),
+ "empty object throws"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from({year: 1976, month: 11, day: 18, millisecond: 123}),
+ 1976, 11, "M11", 18, 0, 0, 0, 123, 0, 0,
+ "plain object with month but not month code"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from({year: 1976, monthCode: "M09", day: 18, millisecond: 123}),
+ 1976, 9, "M09", 18, 0, 0, 0, 123, 0, 0,
+ "plain object with month code but not month"
+);
+
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from({year: 1976, month: 11, day: 18, hours: 12}),
+ 1976, 11, "M11", 18, 0, 0, 0, 0, 0, 0,
+ "incorrectly-spelled properties (e.g., plural \"hours\") are ignored"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-plaindate.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-plaindate.js
new file mode 100644
index 0000000000..1810a29a46
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-plaindate.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.plaindatetime.from
+description: Fast path for converting Temporal.PlainDate to Temporal.PlainDateTime by reading internal slots
+info: |
+ sec-temporal.plaindatetime.from step 3:
+ 3. Return ? ToTemporalDateTime(_item_, _options_).
+ sec-temporal-totemporaldatetime step 2.b:
+ b. If _item_ has an [[InitializedTemporalDate]] internal slot, then
+ i. Return ? CreateTemporalDateTime(_item_.[[ISOYear]], _item_.[[ISOMonth]], _item_.[[ISODay]], 0, 0, 0, 0, 0, 0, _item_.[[Calendar]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalPlainDateTimeFastPath((date, calendar) => {
+ const result = Temporal.PlainDateTime.from(date);
+ TemporalHelpers.assertPlainDateTime(result, 2000, 5, "M05", 2, 0, 0, 0, 0, 0, 0, "midnight is assumed");
+ assert.sameValue(result.getCalendar(), calendar, "calendar result");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-plaindatetime.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-plaindatetime.js
new file mode 100644
index 0000000000..62662de9f3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-plaindatetime.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.plaindatetime.from
+description: A PlainDateTime object is copied, not returned directly
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const orig = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 20, 123, 456, 789);
+const result = Temporal.PlainDateTime.from(orig);
+
+TemporalHelpers.assertPlainDateTime(
+ result,
+ 1976, 11, "M11", 18, 15, 23, 20, 123, 456, 789,
+ "PlainDateTime is copied"
+);
+
+assert.sameValue(result.getISOFields().calendar, orig.getISOFields().calendar, "Calendar is copied");
+
+assert.notSameValue(
+ result,
+ orig,
+ "When a PlainDateTime is given, the returned value is not the original PlainDateTime"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-propertybag-calendar-case-insensitive.js
new file mode 100644
index 0000000000..d09e414fc6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-propertybag-calendar-case-insensitive.js
@@ -0,0 +1,18 @@
+// |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.plaindatetime.from
+description: The calendar name is case-insensitive
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = "IsO8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = Temporal.PlainDateTime.from(arg);
+TemporalHelpers.assertPlainDateTime(result, 1976, 11, "M11", 18, 0, 0, 0, 0, 0, 0, "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-propertybag-calendar-leap-second.js
new file mode 100644
index 0000000000..bb4d829571
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-propertybag-calendar-leap-second.js
@@ -0,0 +1,22 @@
+// |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.plaindatetime.from
+description: Leap second is a valid ISO string for a calendar in a property bag
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = "2016-12-31T23:59:60";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = Temporal.PlainDateTime.from(arg);
+TemporalHelpers.assertPlainDateTime(
+ result,
+ 1976, 11, "M11", 18, 0, 0, 0, 0, 0, 0,
+ "leap second is a valid ISO string for calendar"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-propertybag-calendar-number.js
new file mode 100644
index 0000000000..7ed5518d75
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-propertybag-calendar-number.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.plaindatetime.from
+description: A number as calendar in a property bag is not accepted
+features: [Temporal]
+---*/
+
+const numbers = [
+ 1,
+ 19970327,
+ -19970327,
+ 1234567890,
+];
+
+for (const calendar of numbers) {
+ const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+ assert.throws(
+ TypeError,
+ () => Temporal.PlainDateTime.from(arg),
+ "Numbers cannot be used as a calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-propertybag-calendar-string.js
new file mode 100644
index 0000000000..2c86bf26b2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-propertybag-calendar-string.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.plaindatetime.from
+description: A calendar ID is valid input for Calendar
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = "iso8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = Temporal.PlainDateTime.from(arg);
+TemporalHelpers.assertPlainDateTime(result, 1976, 11, "M11", 18, 0, 0, 0, 0, 0, 0, `Calendar created from string "${calendar}"`);
+assert.sameValue(result.getISOFields().calendar, "iso8601", "calendar slot stores a string");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-propertybag-calendar-wrong-type.js
new file mode 100644
index 0000000000..24dac75fc2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-propertybag-calendar-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.plaindatetime.from
+description: >
+ Appropriate error thrown when a calendar property from a property bag cannot
+ be converted to a calendar object or string
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [calendar, description] of primitiveTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ typeof calendar === 'string' ? RangeError : TypeError,
+ () => Temporal.PlainDateTime.from(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+ [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields()
+];
+
+for (const [calendar, description] of typeErrorTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(TypeError, () => Temporal.PlainDateTime.from(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/PlainDateTime/from/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-propertybag-calendar-year-zero.js
new file mode 100644
index 0000000000..8e5bc48a68
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-propertybag-calendar-year-zero.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.plaindatetime.from
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T17:45",
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+01:00",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDateTime.from(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..a0d058a977
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-calendar-annotation.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.plaindatetime.from
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["1976-11-18T15:23[u-ca=iso8601]", "without time zone"],
+ ["1976-11-18T15:23[UTC][u-ca=iso8601]", "with time zone"],
+ ["1976-11-18T15:23[!u-ca=iso8601]", "with ! and no time zone"],
+ ["1976-11-18T15:23[UTC][!u-ca=iso8601]", "with ! and time zone"],
+ ["1976-11-18T15:23[u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.PlainDateTime.from(arg);
+
+ TemporalHelpers.assertPlainDateTime(
+ result,
+ 1976, 11, "M11", 18, 15, 23, 0, 0, 0, 0,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-comma-decimal-separator.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-comma-decimal-separator.js
new file mode 100644
index 0000000000..66d90e42a3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-comma-decimal-separator.js
@@ -0,0 +1,18 @@
+// |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.plaindatetime.from
+description: Comma may be used as a decimal separator
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from("1976-11-18T15:23:30,12"),
+ 1976, 11, "M11", 18, 15, 23, 30, 120, 0, 0,
+ "comma decimal separator"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..d8767b4a80
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-critical-unknown-annotation.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.plaindatetime.from
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "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]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDateTime.from(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..37ea330410
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-date-with-utc-offset.js
@@ -0,0 +1,47 @@
+// |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.plaindatetime.from
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const validStrings = [
+ "1976-11-18T15:23+00:00",
+ "1976-11-18T15:23+00:00[UTC]",
+ "1976-11-18T15:23+00:00[!UTC]",
+ "1976-11-18T15:23-02:30[America/St_Johns]",
+];
+
+for (const arg of validStrings) {
+ const result = Temporal.PlainDateTime.from(arg);
+
+ TemporalHelpers.assertPlainDateTime(
+ result,
+ 1976, 11, "M11", 18, 15, 23, 0, 0, 0, 0,
+ `"${arg}" is a valid UTC offset with time for PlainDateTime`
+ );
+}
+
+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,
+ () => Temporal.PlainDateTime.from(arg),
+ `"${arg}" UTC offset without time is not valid for PlainDateTime`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-invalid.js
new file mode 100644
index 0000000000..0d086de776
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-invalid.js
@@ -0,0 +1,62 @@
+// |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.plaindatetime.from
+description: Reject string argument if it cannot be parsed
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ // invalid ISO strings:
+ "",
+ "obviously invalid",
+ "2020-01-00",
+ "2020-01-32",
+ "2020-02-30",
+ "2021-02-29",
+ "2020-00-01",
+ "2020-13-01",
+ "2020-01-01T",
+ "2020-01-01T25:00:00",
+ "2020-01-01T01:60:00",
+ "2020-01-01T01:60:61",
+ "2020-01-01junk",
+ "2020-01-01T00:00:00junk",
+ "2020-01-01T00:00:00+00:00junk",
+ "2020-01-01T00:00:00+00:00[UTC]junk",
+ "2020-01-01T00:00:00+00:00[UTC][u-ca=iso8601]junk",
+ "02020-01-01",
+ "2020-001-01",
+ "2020-01-001",
+ "2020-01-01T001",
+ "2020-01-01T01:001",
+ "2020-01-01T01:01:001",
+ // valid, but forms not supported in Temporal:
+ "2020-W01-1",
+ "2020-001",
+ "+0002020-01-01",
+ // valid, but this calendar must not exist:
+ "2020-01-01[u-ca=notexist]",
+ // may be valid in other contexts, but insufficient information for PlainDateTime:
+ "2020-01",
+ "+002020-01",
+ "01-01",
+ "2020-W01",
+ "P1Y",
+ "-P12Y",
+ // valid, but outside the supported range:
+ "-999999-01-01",
+ "+999999-01-01",
+];
+
+invalidStrings.forEach((s) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDateTime.from(s),
+ `invalid date-time string (${s})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-minus-sign.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-minus-sign.js
new file mode 100644
index 0000000000..f2664a50cc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-minus-sign.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.plaindatetime.from
+description: Non-ASCII minus sign is acceptable
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from("1976-11-18T15:23:30.12\u221202:00"),
+ 1976, 11, "M11", 18, 15, 23, 30, 120, 0, 0,
+ "variant minus sign (offset)"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from("\u2212009999-11-18T15:23:30.12"),
+ -9999, 11, "M11", 18, 15, 23, 30, 120, 0, 0,
+ "variant minus sign (leading minus)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..7ac0aaa699
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/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.plaindatetime.from
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[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]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDateTime.from(arg),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..a50b38b586
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-multiple-time-zone.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.plaindatetime.from
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "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]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDateTime.from(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-offset.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-offset.js
new file mode 100644
index 0000000000..a302a19e9b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-offset.js
@@ -0,0 +1,39 @@
+// |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.plaindatetime.from
+description: Extended format may be used
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const expected = [1976, 11, "M11", 18, 15, 23, 30, 100, 0, 0];
+
+const strs = [
+ "1976-11-18T152330.1+00:00",
+ "19761118T15:23:30.1+00:00",
+ "1976-11-18T15:23:30.1+0000",
+ "1976-11-18T152330.1+0000",
+ "19761118T15:23:30.1+0000",
+ "19761118T152330.1+00:00",
+ "19761118T152330.1+0000",
+ "+001976-11-18T152330.1+00:00",
+ "+0019761118T15:23:30.1+00:00",
+ "+001976-11-18T15:23:30.1+0000",
+ "+001976-11-18T152330.1+0000",
+ "+0019761118T15:23:30.1+0000",
+ "+0019761118T152330.1+00:00",
+ "+0019761118T152330.1+0000"
+];
+
+strs.forEach((s) => {
+ TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from(s),
+ ...expected,
+ `mixture of basic and extended format (${s})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-optional-data.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-optional-data.js
new file mode 100644
index 0000000000..3eb56946e7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-optional-data.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.plaindatetime.from
+description: Some parts of a string argument may be omitted
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from("1976-11-18T15:23:30+00"),
+ 1976, 11, "M11", 18, 15, 23, 30, 0, 0, 0,
+ "optional parts (no minute after offset)"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from("1976-11-18T15"),
+ 1976, 11, "M11", 18, 15, 0, 0, 0, 0, 0,
+ "optional parts (no minute in time part)"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from("1976-11-18"),
+ 1976, 11, "M11", 18, 0, 0, 0, 0, 0, 0,
+ "optional parts (no time part)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-out-of-range.js
new file mode 100644
index 0000000000..756387cd55
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-out-of-range.js
@@ -0,0 +1,35 @@
+// |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.plaindatetime.from
+description: Invalid ISO string not acceptable even with overflow = constrain
+features: [Temporal]
+---*/
+
+assert.throws(
+ RangeError,
+ () => Temporal.PlainDateTime.from("2020-13-34T24:60", {}),
+ "constrain has no effect on invalid ISO string (empty options argument)"
+);
+
+assert.throws(
+ RangeError,
+ () => Temporal.PlainDateTime.from("2020-13-34T24:60", () => {}),
+ "constrain has no effect on invalid ISO string (nullary empty object function argument)"
+);
+
+assert.throws(
+ RangeError,
+ () => Temporal.PlainDateTime.from("2020-13-34T24:60", {overflow: "constrain"}),
+ "overflow = constrain has no effect on invalid ISO string"
+);
+
+assert.throws(
+ RangeError,
+ () => Temporal.PlainDateTime.from("2020-13-34T24:60", {overflow: "reject"}),
+ "overflow = reject has no effect on invalid ISO string"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-subsecond.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-subsecond.js
new file mode 100644
index 0000000000..8f44695463
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-subsecond.js
@@ -0,0 +1,72 @@
+// |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.plaindatetime.from
+description: Up to nine digits of sub-second precision are acceptable
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from("1976-11-18T15:23:30.1"),
+ 1976, 11, "M11", 18, 15, 23, 30, 100, 0, 0,
+ "various precisions are possible (one decimal digit)"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from("1976-11-18T15:23:30.12"),
+ 1976, 11, "M11", 18, 15, 23, 30, 120, 0, 0,
+ "various precisions are possible (two decimal digits)"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from("1976-11-18T15:23:30.123"),
+ 1976, 11, "M11", 18, 15, 23, 30, 123, 0, 0,
+ "various precisions are possible (three decimal digits)"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from("1976-11-18T15:23:30.1234"),
+ 1976, 11, "M11", 18, 15, 23, 30, 123, 400, 0,
+ "various precisions are possible (four decimal digits)"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from("1976-11-18T15:23:30.12345"),
+ 1976, 11, "M11", 18, 15, 23, 30, 123, 450, 0,
+ "various precisions are possible (five decimal digits)"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from("1976-11-18T15:23:30.123456"),
+ 1976, 11, "M11", 18, 15, 23, 30, 123, 456, 0,
+ "various precisions are possible (six decimal digits)"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from("1976-11-18T15:23:30.1234567"),
+ 1976, 11, "M11", 18, 15, 23, 30, 123, 456, 700,
+ "various precisions are possible (seven decimal digits)"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from("1976-11-18T15:23:30.12345678"),
+ 1976, 11, "M11", 18, 15, 23, 30, 123, 456, 780,
+ "various precisions are possible (eight decimal digits)"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from("1976-11-18T15:23:30.123456789"),
+ 1976, 11, "M11", 18, 15, 23, 30, 123, 456, 789,
+ "various precisions are possible (nine decimal digits)"
+);
+
+assert.throws(
+ RangeError,
+ () => Temporal.PlainDateTime.from("1976-11-18T15:23:30.1234567891"),
+ "ten decimal digits is too much"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-time-separators.js
new file mode 100644
index 0000000000..c9118d68a4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-time-separators.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.plaindatetime.from
+description: Time separator in string argument can vary
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["1976-11-18T15:23", "uppercase T"],
+ ["1976-11-18t15:23", "lowercase T"],
+ ["1976-11-18 15:23", "space between date and time"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.PlainDateTime.from(arg);
+
+ TemporalHelpers.assertPlainDateTime(
+ result,
+ 1976, 11, "M11", 18, 15, 23, 0, 0, 0, 0,
+ `variant time separators (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..fa321306a0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-time-zone-annotation.js
@@ -0,0 +1,33 @@
+// |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.plaindatetime.from
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["1976-11-18T15:23[Asia/Kolkata]", "named, with no offset"],
+ ["1976-11-18T15:23[!Europe/Vienna]", "named, with ! and no offset"],
+ ["1976-11-18T15:23[+00:00]", "numeric, with no offset"],
+ ["1976-11-18T15:23[!-02:30]", "numeric, with ! and no offset"],
+ ["1976-11-18T15:23+00:00[UTC]", "named, with offset"],
+ ["1976-11-18T15:23+00:00[!Africa/Abidjan]", "named, with offset and !"],
+ ["1976-11-18T15:23+00:00[+01:00]", "numeric, with offset"],
+ ["1976-11-18T15:23+00:00[!-08:00]", "numeric, with offset and !"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.PlainDateTime.from(arg);
+
+ TemporalHelpers.assertPlainDateTime(
+ result,
+ 1976, 11, "M11", 18, 15, 23, 0, 0, 0, 0,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-timezone.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-timezone.js
new file mode 100644
index 0000000000..5c5810dfa7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-timezone.js
@@ -0,0 +1,18 @@
+// |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.plaindatetime.from
+description: Timezone, if specified, is ignored
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from("2020-01-01T01:23:45[Asia/Kolkata]"),
+ 2020, 1, "M01", 1, 1, 23, 45, 0, 0, 0,
+ "ignores if a timezone is specified"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..0c115b79bf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-unknown-annotation.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.plaindatetime.from
+description: Various forms of unknown annotation
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["1976-11-18T15:23[foo=bar]", "alone"],
+ ["1976-11-18T15:23[UTC][foo=bar]", "with time zone"],
+ ["1976-11-18T15:23[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["1976-11-18T15:23[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["1976-11-18T15:23[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.PlainDateTime.from(arg);
+
+ TemporalHelpers.assertPlainDateTime(
+ result,
+ 1976, 11, "M11", 18, 15, 23, 0, 0, 0, 0,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..825d2da94b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-with-utc-designator.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.plaindatetime.from
+description: RangeError thrown if a string with UTC designator is used as a PlainDateTime
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "2019-10-01T09:00:00Z",
+ "2019-10-01T09:00:00Z[UTC]",
+];
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDateTime.from(arg),
+ "String with UTC designator should not be valid as a PlainDateTime"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string.js
new file mode 100644
index 0000000000..2addaccc70
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string.js
@@ -0,0 +1,36 @@
+// |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.plaindatetime.from
+description: String arguments are acceptable
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from("1976-11-18T15:23:30"),
+ 1976, 11, "M11", 18, 15, 23, 30, 0, 0, 0,
+ "date and time (no subseconds)"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from("1976-11-18T15:23:30.001"),
+ 1976, 11, "M11", 18, 15, 23, 30, 1, 0, 0,
+ "date and time (millisecond)"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from("1976-11-18T15:23:30.001123"),
+ 1976, 11, "M11", 18, 15, 23, 30, 1, 123, 0,
+ "date and time (microsecond)"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from("1976-11-18T15:23:30.001123456"),
+ 1976, 11, "M11", 18, 15, 23, 30, 1, 123, 456,
+ "date and time (nanosecond)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-wrong-type.js
new file mode 100644
index 0000000000..13deec81ce
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-wrong-type.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.plaindatetime.from
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainDateTime
+features: [BigInt, Symbol, Temporal]
+---*/
+
+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,
+ () => Temporal.PlainDateTime.from(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.PlainDateTime, "Temporal.PlainDateTime, object"],
+ [Temporal.PlainDateTime.prototype, "Temporal.PlainDateTime.prototype, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => Temporal.PlainDateTime.from(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/PlainDateTime/from/argument-zoneddatetime-balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-zoneddatetime-balance-negative-time-units.js
new file mode 100644
index 0000000000..0f839b62d9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/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.plaindatetime.from
+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-totemporaldatetime step 3.b:
+ b. If _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ ...
+ ii. 1. Return ? BuiltinTimeZoneGetPlainDateTimeFor(_item_.[[TimeZone]], _instant_, _item_.[[Calendar]]).
+ sec-temporal.plaindatetime.from step 3:
+ 3. Return ? ToTemporalDateTime(_temporalTime_).
+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 pdt = Temporal.PlainDateTime.from(datetime);
+
+TemporalHelpers.assertPlainDateTime(pdt, 1970, 1, "M01", 1, 1, 1, 1, 1, 0, 999);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-zoneddatetime-negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-zoneddatetime-negative-epochnanoseconds.js
new file mode 100644
index 0000000000..774cf3d994
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-zoneddatetime-negative-epochnanoseconds.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.plaindatetime.from
+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 result = Temporal.PlainDateTime.from(datetime);
+TemporalHelpers.assertPlainDateTime(result, 1969, 7, "M07", 24, 16, 50, 35, 0, 0, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..828f40a13b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,18 @@
+// |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.plaindatetime.from
+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].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => Temporal.PlainDateTime.from(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..f9e2bc0f90
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.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.plaindatetime.from
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => Temporal.PlainDateTime.from(datetime),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..cc4c3210eb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,18 @@
+// |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.plaindatetime.from
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => Temporal.PlainDateTime.from(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..8f3a854e1f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.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.plaindatetime.from
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => Temporal.PlainDateTime.from(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/builtin.js
new file mode 100644
index 0000000000..08e167da13
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/builtin.js
@@ -0,0 +1,33 @@
+// |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.plaindatetime.from
+description: Tests that Temporal.PlainDateTime.from meets the requirements for built-in objects
+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.PlainDateTime.from),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.from),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.from),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.from.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/calendar-datefromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..0345c38c27
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/calendar-datefromfields-called-with-null-prototype-fields.js
@@ -0,0 +1,18 @@
+// |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.plaindatetime.from
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const arg = { year: 2000, month: 5, day: 2, calendar };
+Temporal.PlainDateTime.from(arg);
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/calendar-fields-iterable.js
new file mode 100644
index 0000000000..bdf5cdc7ca
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/calendar-fields-iterable.js
@@ -0,0 +1,33 @@
+// |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.plaindatetime.from
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plaindatetime.from step 3:
+ 3. Return ? ToTemporalDateTime(_item_, _options_).
+ sec-temporal-totemporaldatetime step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"hour"*, *"microsecond"*, *"millisecond"*, *"minute"*, *"month"*, *"monthCode"*, *"nanosecond"*, *"second"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+Temporal.PlainDateTime.from({ year: 2000, month: 5, day: 2, calendar });
+
+assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/calendar-temporal-object.js
new file mode 100644
index 0000000000..f986d85777
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/calendar-temporal-object.js
@@ -0,0 +1,29 @@
+// |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.plaindatetime.from
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plaindatetime.from step 3:
+ 3. Return ? ToTemporalDateTime(_item_, _options_).
+ sec-temporal-totemporaldatetime step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject, calendar) => {
+ const result = Temporal.PlainDateTime.from({ year: 2000, month: 5, day: 2, calendar: temporalObject });
+ assert.sameValue(result.getCalendar(), calendar, "Temporal object coerced to calendar");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..4df5ca8f0d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/constructor-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |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.plaindatetime.prototype.from
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+
+assert.throws(RangeError, () => Temporal.PlainDateTime.from(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/duplicate-calendar-fields.js
new file mode 100644
index 0000000000..8ad203ebea
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/duplicate-calendar-fields.js
@@ -0,0 +1,19 @@
+// |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.plaindatetime.prototype.from
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+
+for (const extra_fields of [['foo', 'foo'], ['day'], ['hour'], ['microsecond'], ['millisecond'], ['minute'], ['month'], ['monthCode'], ['nanosecond'], ['second'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar };
+
+ assert.throws(RangeError, () => Temporal.PlainDateTime.from(arg));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..d35c081317
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/infinity-throws-rangeerror.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.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plaindatetime.from
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const base = { year: 2000, month: 5, day: 2, hour: 15, minute: 30, second: 45, millisecond: 987, microsecond: 654, nanosecond: 321 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((prop) => {
+ ["constrain", "reject"].forEach((overflow) => {
+ assert.throws(RangeError, () => Temporal.PlainDateTime.from({ ...base, [prop]: inf }, { overflow }), `${prop} property cannot be ${inf} (overflow ${overflow}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => Temporal.PlainDateTime.from({ ...base, [prop]: obj }, { overflow }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/leap-second.js
new file mode 100644
index 0000000000..efbcdd8cd6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/leap-second.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.plaindatetime.from
+description: Leap second is a valid ISO string for PlainDateTime
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+let arg = "2016-12-31T23:59:60";
+
+const result1 = Temporal.PlainDateTime.from(arg);
+TemporalHelpers.assertPlainDateTime(
+ result1,
+ 2016, 12, "M12", 31, 23, 59, 59, 0, 0, 0,
+ "leap second is a valid ISO string for PlainDateTime"
+);
+
+const result2 = Temporal.PlainDateTime.from(arg);
+TemporalHelpers.assertPlainDateTime(
+ result2,
+ 2016, 12, "M12", 31, 23, 59, 59, 0, 0, 0,
+ "leap second is a valid ISO string for PlainDateTime even with overflow: reject"
+);
+
+arg = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+
+const result3 = Temporal.PlainDateTime.from(arg);
+TemporalHelpers.assertPlainDateTime(
+ result3,
+ 2016, 12, "M12", 31, 23, 59, 59, 0, 0, 0,
+ "second: 60 is constrained in property bag for PlainDateTime"
+);
+
+assert.throws(
+ RangeError,
+ () => Temporal.PlainDateTime.from(arg, { overflow: "reject" }),
+ "second: 60 is rejected in property bag for PlainDateTime with overflow: reject"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/length.js
new file mode 100644
index 0000000000..f547f0c619
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/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.plaindatetime.from
+description: Temporal.PlainDateTime.from.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.PlainDateTime.from, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/limits.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/limits.js
new file mode 100644
index 0000000000..af997c2688
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/limits.js
@@ -0,0 +1,73 @@
+// |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.plaindatetime.from
+description: Checking limits of representable PlainDateTime
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+["reject", "constrain"].forEach((overflow) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDateTime.from({ year: -271821, month: 4, day: 19 }, { overflow }),
+ `negative out of bounds (plain object, overflow = ${overflow})`
+ );
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDateTime.from({ year: 275760, month: 9, day: 14 }, { overflow }),
+ `positive out of bounds (plain object, overflow = ${overflow})`
+ );
+});
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from({ year: -271821, month: 4, day: 19, nanosecond: 1 }),
+ -271821, 4, "M04", 19, 0, 0, 0, 0, 0, 1,
+ "construct from property bag (negative boundary)"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from(
+ {
+ year: 275760,
+ month: 9,
+ day: 13,
+ hour: 23,
+ minute: 59,
+ second: 59,
+ millisecond: 999,
+ microsecond: 999,
+ nanosecond: 999
+ }
+ ),
+ 275760, 9, "M09", 13, 23, 59, 59, 999, 999, 999,
+ "construct from property bag (positive boundary)"
+);
+
+assert.throws(
+ RangeError,
+ () => Temporal.PlainDateTime.from("-271821-04-19T00:00"),
+ "out-of-bounds ISO string (negative case)"
+);
+
+assert.throws(
+ RangeError,
+ () => Temporal.PlainDateTime.from("+275760-09-14T00:00"),
+ "out-of-bounds ISO string (positive case)"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from("-271821-04-19T00:00:00.000000001"),
+ -271821, 4, "M04", 19, 0, 0, 0, 0, 0, 1,
+ "boundary ISO string (negative case)"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from("+275760-09-13T23:59:59.999999999"),
+ 275760, 9, "M09", 13, 23, 59, 59, 999, 999, 999,
+ "boundary ISO string (positive case)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/name.js
new file mode 100644
index 0000000000..633e22a791
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/name.js
@@ -0,0 +1,26 @@
+// |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.plaindatetime.from
+description: Temporal.PlainDateTime.from.name is "from"
+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.PlainDateTime.from, "name", {
+ value: "from",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/not-a-constructor.js
new file mode 100644
index 0000000000..9ca650011d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/not-a-constructor.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.plaindatetime.from
+description: Temporal.PlainDateTime.from 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.PlainDateTime.from();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.from), false,
+ "isConstructor(Temporal.PlainDateTime.from)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/observable-get-overflow-argument-primitive.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/observable-get-overflow-argument-primitive.js
new file mode 100644
index 0000000000..5dcf84541d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/observable-get-overflow-argument-primitive.js
@@ -0,0 +1,37 @@
+// |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.plaindatetime.from
+description: overflow property is extracted with string argument.
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "ownKeys options",
+ "getOwnPropertyDescriptor options.overflow",
+ "get options.overflow",
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+];
+
+let actual = [];
+const options = TemporalHelpers.propertyBagObserver(actual, { overflow: "reject" }, "options");
+
+const result = Temporal.PlainDateTime.from("2021-05-17T12:34:56", options);
+assert.compareArray(actual, expected, "Successful call");
+TemporalHelpers.assertPlainDateTime(result, 2021, 5, "M05", 17, 12, 34, 56, 0, 0, 0);
+
+actual.splice(0); // empty it for the next check
+const failureExpected = [
+ "ownKeys options",
+ "getOwnPropertyDescriptor options.overflow",
+ "get options.overflow",
+];
+
+assert.throws(TypeError, () => Temporal.PlainDateTime.from(7, options));
+assert.compareArray(actual, failureExpected, "Failing call");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/observable-get-overflow-argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/observable-get-overflow-argument-string-invalid.js
new file mode 100644
index 0000000000..799c8e70ea
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/observable-get-overflow-argument-string-invalid.js
@@ -0,0 +1,24 @@
+// |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.plaindatetime.from
+description: overflow property is extracted with ISO-invalid string argument.
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "ownKeys options",
+ "getOwnPropertyDescriptor options.overflow",
+ "get options.overflow",
+];
+
+let actual = [];
+const options = TemporalHelpers.propertyBagObserver(actual, { overflow: "reject" }, "options");
+
+assert.throws(RangeError, () => Temporal.PlainDateTime.from("2020-13-34T25:60:60", options));
+assert.compareArray(actual, expected);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/options-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/options-object.js
new file mode 100644
index 0000000000..7824114ba5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/options-object.js
@@ -0,0 +1,22 @@
+// |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.plaindatetime.prototype.from
+description: Empty object may be used as options
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from({ year: 1976, month: 11, day: 18 }, {}), 1976, 11, "M11", 18, 0, 0, 0, 0, 0, 0,
+ "options may be an empty plain object"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from({ year: 1976, month: 11, day: 18 }, () => {}), 1976, 11, "M11", 18, 0, 0, 0, 0, 0, 0,
+ "options may be an empty function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/options-undefined.js
new file mode 100644
index 0000000000..61903bbced
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/options-undefined.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.plaindatetime.from
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const fields = { year: 2000, month: 13, day: 2 };
+
+const explicit = Temporal.PlainDateTime.from(fields, undefined);
+assert.sameValue(explicit.month, 12, "default overflow is constrain");
+
+const implicit = Temporal.PlainDateTime.from(fields);
+assert.sameValue(implicit.month, 12, "default overflow is constrain");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/options-wrong-type.js
new file mode 100644
index 0000000000..93b852dda0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/options-wrong-type.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.plaindatetime.from
+description: TypeError thrown when options argument is a primitive
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const badOptions = [
+ null,
+ true,
+ "some string",
+ Symbol(),
+ 1,
+ 2n,
+];
+
+for (const value of badOptions) {
+ assert.throws(TypeError, () => Temporal.PlainDateTime.from({ year: 1976, month: 11, day: 18 }, value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/order-of-operations.js
new file mode 100644
index 0000000000..8c40100fdb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/order-of-operations.js
@@ -0,0 +1,107 @@
+// |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.plaindatetime.from
+description: Properties on an object passed to from() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ // CopyDataProperties
+ "ownKeys options",
+ "getOwnPropertyDescriptor options.overflow",
+ "get options.overflow",
+ "getOwnPropertyDescriptor options.extra",
+ "get options.extra",
+ // GetTemporalCalendarSlotValueWithISODefault
+ "get fields.calendar",
+ "has fields.calendar.dateAdd",
+ "has fields.calendar.dateFromFields",
+ "has fields.calendar.dateUntil",
+ "has fields.calendar.day",
+ "has fields.calendar.dayOfWeek",
+ "has fields.calendar.dayOfYear",
+ "has fields.calendar.daysInMonth",
+ "has fields.calendar.daysInWeek",
+ "has fields.calendar.daysInYear",
+ "has fields.calendar.fields",
+ "has fields.calendar.id",
+ "has fields.calendar.inLeapYear",
+ "has fields.calendar.mergeFields",
+ "has fields.calendar.month",
+ "has fields.calendar.monthCode",
+ "has fields.calendar.monthDayFromFields",
+ "has fields.calendar.monthsInYear",
+ "has fields.calendar.weekOfYear",
+ "has fields.calendar.year",
+ "has fields.calendar.yearMonthFromFields",
+ "has fields.calendar.yearOfWeek",
+ // lookup
+ "get fields.calendar.dateFromFields",
+ "get fields.calendar.fields",
+ // CalendarFields
+ "call fields.calendar.fields",
+ // PrepareTemporalFields
+ "get fields.day",
+ "get fields.day.valueOf",
+ "call fields.day.valueOf",
+ "get fields.hour",
+ "get fields.hour.valueOf",
+ "call fields.hour.valueOf",
+ "get fields.microsecond",
+ "get fields.microsecond.valueOf",
+ "call fields.microsecond.valueOf",
+ "get fields.millisecond",
+ "get fields.millisecond.valueOf",
+ "call fields.millisecond.valueOf",
+ "get fields.minute",
+ "get fields.minute.valueOf",
+ "call fields.minute.valueOf",
+ "get fields.month",
+ "get fields.month.valueOf",
+ "call fields.month.valueOf",
+ "get fields.monthCode",
+ "get fields.monthCode.toString",
+ "call fields.monthCode.toString",
+ "get fields.nanosecond",
+ "get fields.nanosecond.valueOf",
+ "call fields.nanosecond.valueOf",
+ "get fields.second",
+ "get fields.second.valueOf",
+ "call fields.second.valueOf",
+ "get fields.year",
+ "get fields.year.valueOf",
+ "call fields.year.valueOf",
+ // InterpretTemporalDateTimeFields
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+ "call fields.calendar.dateFromFields",
+];
+const actual = [];
+
+const fields = TemporalHelpers.propertyBagObserver(actual, {
+ year: 1.7,
+ month: 1.7,
+ monthCode: "M01",
+ day: 1.7,
+ hour: 1.7,
+ minute: 1.7,
+ second: 1.7,
+ millisecond: 1.7,
+ microsecond: 1.7,
+ nanosecond: 1.7,
+ calendar: TemporalHelpers.calendarObserver(actual, "fields.calendar"),
+}, "fields");
+
+const options = TemporalHelpers.propertyBagObserver(actual, {
+ overflow: "constrain",
+ extra: "property",
+}, "options");
+
+Temporal.PlainDateTime.from(fields, options);
+assert.compareArray(actual, expected, "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/overflow-default-constrain.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/overflow-default-constrain.js
new file mode 100644
index 0000000000..ba6b9725d3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/overflow-default-constrain.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.plaindatetime.from
+description: By default, overflow = constrain
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const date = {year: 2019, month: 1, day: 32};
+const data = [2019, 1, "M01", 31, 0, 0, 0, 0, 0, 0];
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from(date),
+ ...data,
+ "by default, overflow is constrain (overflow options argument absent)"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from(date, {}),
+ ...data,
+ "by default, overflow is constrain (options argument is empty plain object)"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from(date, () => {}),
+ ...data,
+ "by default, overflow is constrain (options argument is empty function)"
+);
+
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from(date, {overflow: "constrain"}),
+ ...data,
+ "by default, overflow is constrain (overflow options argument present)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/overflow-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/overflow-invalid-string.js
new file mode 100644
index 0000000000..cd735ddd64
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/overflow-invalid-string.js
@@ -0,0 +1,49 @@
+// |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.plaindatetime.from
+description: RangeError thrown when overflow option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-interprettemporaldatetimefields steps 2–3:
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+ 3. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal-totemporaldatetime steps 2–3:
+ 2. If Type(_item_) is Object, then
+ ...
+ g. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+ 3. Else,
+ a. Perform ? ToTemporalOverflow(_options_).
+ sec-temporal.plaindatetime.from steps 2–3:
+ 2. If Type(_item_) is Object and _item_ has an [[InitializedTemporalDateTime]] internal slot, then
+ a. Perform ? ToTemporalOverflow(_options_).
+ b. Return ...
+ 3. Return ? ToTemporalDateTime(_item_, _options_).
+features: [Temporal]
+---*/
+
+const validValues = [
+ new Temporal.PlainDateTime(2000, 5, 2, 12),
+ new Temporal.PlainDate(2000, 5, 2),
+ new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"),
+ { year: 2000, month: 5, day: 2, hour: 12 },
+ "2000-05-02T12:00",
+];
+
+const badOverflows = ["", "CONSTRAIN", "balance", "other string", "constra\u0131n", "reject\0"];
+for (const value of validValues) {
+ for (const overflow of badOverflows) {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDateTime.from(value, { overflow }),
+ `invalid overflow ("${overflow}")`
+ );
+ }
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/overflow-reject.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/overflow-reject.js
new file mode 100644
index 0000000000..91f38ec3fe
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/overflow-reject.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.plaindatetime.from
+description: Possibly throw if overflow is reject
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+TemporalHelpers.assertPlainDateTime(
+ Temporal.PlainDateTime.from({year: 2019, month: 1, day: 31}, {overflow: "reject"}),
+ 2019, 1, "M01", 31, 0, 0, 0, 0, 0, 0,
+ "overflow reject, acceptable argument"
+);
+
+assert.throws(
+ RangeError,
+ () => Temporal.PlainDateTime.from({year: 2019, month: 1, day: 32}, {overflow: "reject"}),
+ "overflow reject, unacceptable argument"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/overflow-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/overflow-undefined.js
new file mode 100644
index 0000000000..0593ed9d41
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/overflow-undefined.js
@@ -0,0 +1,48 @@
+// |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.plaindatetime.from
+description: Fallback value for overflow option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-interprettemporaldatetimefields steps 2–3:
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+ 3. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal-totemporaldatetime steps 2–3:
+ 2. If Type(_item_) is Object, then
+ ...
+ g. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+ 3. Else,
+ a. Perform ? ToTemporalOverflow(_options_).
+ sec-temporal.plaindatetime.from steps 2–3:
+ 2. If Type(_item_) is Object and _item_ has an [[InitializedTemporalDateTime]] internal slot, then
+ a. Perform ? ToTemporalOverflow(_options_).
+ b. Return ...
+ 3. Return ? ToTemporalDateTime(_item_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const validValues = [
+ new Temporal.PlainDateTime(2000, 5, 2, 12),
+ "2000-05-02T12:00",
+];
+validValues.forEach((value) => {
+ const explicit = Temporal.PlainDateTime.from(value, { overflow: undefined });
+ TemporalHelpers.assertPlainDateTime(explicit, 2000, 5, "M05", 2, 12, 0, 0, 0, 0, 0, "overflow is ignored");
+ const implicit = Temporal.PlainDateTime.from(value, {});
+ TemporalHelpers.assertPlainDateTime(implicit, 2000, 5, "M05", 2, 12, 0, 0, 0, 0, 0, "overflow is ignored");
+});
+
+const propertyBag = { year: 2000, month: 13, day: 34, hour: 12 };
+const explicit = Temporal.PlainDateTime.from(propertyBag, { overflow: undefined });
+TemporalHelpers.assertPlainDateTime(explicit, 2000, 12, "M12", 31, 12, 0, 0, 0, 0, 0, "default overflow is constrain");
+const implicit = Temporal.PlainDateTime.from(propertyBag, {});
+TemporalHelpers.assertPlainDateTime(implicit, 2000, 12, "M12", 31, 12, 0, 0, 0, 0, 0, "default overflow is constrain");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/overflow-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/overflow-wrong-type.js
new file mode 100644
index 0000000000..cadb84e952
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/overflow-wrong-type.js
@@ -0,0 +1,65 @@
+// |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.plaindatetime.from
+description: Type conversions for overflow option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-interprettemporaldatetimefields steps 2–3:
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+ 3. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal-totemporaldatetime steps 2–3:
+ 2. If Type(_item_) is Object, then
+ ...
+ g. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+ 3. Else,
+ a. Perform ? ToTemporalOverflow(_options_).
+ sec-temporal.plaindatetime.from steps 2–3:
+ 2. If Type(_item_) is Object and _item_ has an [[InitializedTemporalDateTime]] internal slot, then
+ a. Perform ? ToTemporalOverflow(_options_).
+ b. Return ...
+ 3. Return ? ToTemporalDateTime(_item_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const validValues = [
+ new Temporal.PlainDateTime(2000, 5, 2, 12),
+ "2000-05-02T12:00",
+];
+validValues.forEach((value) => TemporalHelpers.checkStringOptionWrongType("overflow", "constrain",
+ (overflow) => Temporal.PlainDateTime.from(value, { overflow }),
+ (result, descr) => TemporalHelpers.assertPlainDateTime(result, 2000, 5, "M05", 2, 12, 0, 0, 0, 0, 0, descr),
+));
+
+// See TemporalHelpers.checkStringOptionWrongType(); this code path has
+// different expectations for observable calls
+const propertyBag = { year: 2000, month: 5, day: 2, hour: 12 };
+
+assert.throws(RangeError, () => Temporal.PlainDateTime.from(propertyBag, { overflow: null }), "null");
+assert.throws(RangeError, () => Temporal.PlainDateTime.from(propertyBag, { overflow: true }), "true");
+assert.throws(RangeError, () => Temporal.PlainDateTime.from(propertyBag, { overflow: false }), "false");
+assert.throws(TypeError, () => Temporal.PlainDateTime.from(propertyBag, { overflow: Symbol() }), "symbol");
+assert.throws(RangeError, () => Temporal.PlainDateTime.from(propertyBag, { overflow: 2 }), "number");
+assert.throws(RangeError, () => Temporal.PlainDateTime.from(propertyBag, { overflow: 2n }), "bigint");
+assert.throws(RangeError, () => Temporal.PlainDateTime.from(propertyBag, { overflow: {} }), "plain object");
+
+// toString property should only be read and converted to a string once, because
+// a copied object with the resulting string on it is passed to
+// Calendar.dateFromFields().
+const expected = [
+ "get overflow.toString",
+ "call overflow.toString",
+];
+const actual = [];
+const observer = TemporalHelpers.toPrimitiveObserver(actual, "constrain", "overflow");
+const result = Temporal.PlainDateTime.from(propertyBag, { overflow: observer });
+TemporalHelpers.assertPlainDateTime(result, 2000, 5, "M05", 2, 12, 0, 0, 0, 0, 0, "object with toString");
+assert.compareArray(actual, expected, "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/parser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/parser.js
new file mode 100644
index 0000000000..0ef6abb708
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/parser.js
@@ -0,0 +1,15 @@
+// |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.
+
+/*---
+description: Temporal.PlainDateTime.from accepts a custom timezone that starts with "c".
+esid: sec-temporal.plaindatetime.from
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dateTime = Temporal.PlainDateTime.from("2020-01-01T00:00:00+01:00[Custom]");
+TemporalHelpers.assertPlainDateTime(dateTime, 2020, 1, "M01", 1, 0, 0, 0, 0, 0, 0);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/prop-desc.js
new file mode 100644
index 0000000000..ff87eed71b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/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.plaindatetime.from
+description: The "from" property of Temporal.PlainDateTime
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.from,
+ "function",
+ "`typeof PlainDateTime.from` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime, "from", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/proto-in-calendar-fields.js
new file mode 100644
index 0000000000..43930f216f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/proto-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |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.plaindatetime.prototype.from
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+
+assert.throws(RangeError, () => Temporal.PlainDateTime.from(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/read-time-fields-before-datefromfields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/read-time-fields-before-datefromfields.js
new file mode 100644
index 0000000000..2ef43274bb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/read-time-fields-before-datefromfields.js
@@ -0,0 +1,30 @@
+// |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.plaindatetime.from
+description: The time fields are read from the object before being passed to dateFromFields().
+info: |
+ sec-temporal.plaindatetime.from step 3:
+ 3. Return ? ToTemporalDateTime(_item_, _options_).
+ sec-temporal-totemporaldatetime step 2.e:
+ e. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+ sec-temporal-interprettemporaldatetimefields steps 1–2:
+ 1. Let _timeResult_ be ? ToTemporalTimeRecord(_fields_).
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarMakeInfinityTime();
+const datetime = Temporal.PlainDateTime.from({ year: 2000, month: 5, day: 2, hour: 12, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321, calendar });
+
+assert.sameValue(datetime.hour, 12, "hour value");
+assert.sameValue(datetime.minute, 34, "minute value");
+assert.sameValue(datetime.second, 56, "second value");
+assert.sameValue(datetime.millisecond, 987, "millisecond value");
+assert.sameValue(datetime.microsecond, 654, "microsecond value");
+assert.sameValue(datetime.nanosecond, 321, "nanosecond value");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/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/PlainDateTime/from/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/subclassing-ignored.js
new file mode 100644
index 0000000000..8858428fc1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/subclassing-ignored.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.plaindatetime.from
+description: The receiver is never called when calling from()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnoredStatic(
+ Temporal.PlainDateTime,
+ "from",
+ ["2000-05-02T12:34:56.987654321"],
+ (result) => TemporalHelpers.assertPlainDateTime(result, 2000, 5, "M05", 2, 12, 34, 56, 987, 654, 321),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/year-zero.js
new file mode 100644
index 0000000000..60b900e160
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/year-zero.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.plaindatetime.from
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-12-07",
+ "-000000-12-07T03:24:30",
+ "-000000-12-07T03:24:30+01:00",
+ "-000000-12-07T03:24:30+00:00[UTC]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainDateTime.from(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/hour-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/hour-undefined.js
new file mode 100644
index 0000000000..468a88729b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/hour-undefined.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.plaindatetime
+description: Hour argument defaults to 0 if not given
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const args = [2000, 5, 2];
+
+TemporalHelpers.assertPlainDateTime(
+ new Temporal.PlainDateTime(...args, undefined),
+ 2000, 5, "M05", 2, 0, 0, 0, 0, 0, 0,
+ "hour default argument (argument present)"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ new Temporal.PlainDateTime(...args),
+ 2000, 5, "M05", 2, 0, 0, 0, 0, 0, 0,
+ "hour default argument (argument missing)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..86bdb7ec8b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/infinity-throws-rangeerror.js
@@ -0,0 +1,165 @@
+// |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.
+
+/*---
+description: Temporal.PlainDateTime throws a RangeError if any value is Infinity
+esid: sec-temporal.plaindatetime
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+assert.throws(RangeError, () => new Temporal.PlainDateTime(Infinity, 1, 1));
+assert.throws(RangeError, () => new Temporal.PlainDateTime(1970, Infinity, 1));
+assert.throws(RangeError, () => new Temporal.PlainDateTime(1970, 1, Infinity));
+assert.throws(RangeError, () => new Temporal.PlainDateTime(1970, 1, 1, Infinity));
+assert.throws(RangeError, () => new Temporal.PlainDateTime(1970, 1, 1, 0, Infinity));
+assert.throws(RangeError, () => new Temporal.PlainDateTime(1970, 1, 1, 0, 0, Infinity));
+assert.throws(RangeError, () => new Temporal.PlainDateTime(1970, 1, 1, 0, 0, 0, Infinity));
+assert.throws(RangeError, () => new Temporal.PlainDateTime(1970, 1, 1, 0, 0, 0, 0, Infinity));
+assert.throws(RangeError, () => new Temporal.PlainDateTime(1970, 1, 1, 0, 0, 0, 0, 0, Infinity));
+
+const O = (primitiveValue, propertyName) => (calls) => TemporalHelpers.toPrimitiveObserver(calls, primitiveValue, propertyName);
+const tests = [
+ [
+ "infinite year",
+ [O(Infinity, "year"), O(1, "month"), O(1, "day")],
+ ["get year.valueOf", "call year.valueOf"
+ ]
+ ],
+ [
+ "infinite month",
+ [O(2, "year"), O(Infinity, "month"), O(1, "day")],
+ ["get year.valueOf", "call year.valueOf", "get month.valueOf", "call month.valueOf"
+ ]
+ ],
+ [
+ "infinite day",
+ [O(2, "year"), O(1, "month"), O(Infinity, "day")],
+ ["get year.valueOf", "call year.valueOf", "get month.valueOf", "call month.valueOf", "get day.valueOf", "call day.valueOf"
+ ]
+ ],
+ [
+ "infinite hour",
+ [O(2, "year"), O(1, "month"), O(1, "day"), O(Infinity, "hour"), O(1, "minute"), O(1, "second"), O(1, "millisecond"), O(1, "microsecond"), O(1, "nanosecond")],
+ [
+ "get year.valueOf",
+ "call year.valueOf",
+ "get month.valueOf",
+ "call month.valueOf",
+ "get day.valueOf",
+ "call day.valueOf",
+ "get hour.valueOf",
+ "call hour.valueOf"
+ ]
+ ],
+ [
+ "infinite minute",
+ [O(2, "year"), O(1, "month"), O(1, "day"), O(1, "hour"), O(Infinity, "minute"), O(1, "second"), O(1, "millisecond"), O(1, "microsecond"), O(1, "nanosecond")],
+ [
+ "get year.valueOf",
+ "call year.valueOf",
+ "get month.valueOf",
+ "call month.valueOf",
+ "get day.valueOf",
+ "call day.valueOf",
+ "get hour.valueOf",
+ "call hour.valueOf",
+ "get minute.valueOf",
+ "call minute.valueOf"
+ ]
+ ],
+ [
+ "infinite second",
+ [O(2, "year"), O(1, "month"), O(1, "day"), O(1, "hour"), O(1, "minute"), O(Infinity, "second"), O(1, "millisecond"), O(1, "microsecond"), O(1, "nanosecond")],
+ [
+ "get year.valueOf",
+ "call year.valueOf",
+ "get month.valueOf",
+ "call month.valueOf",
+ "get day.valueOf",
+ "call day.valueOf",
+ "get hour.valueOf",
+ "call hour.valueOf",
+ "get minute.valueOf",
+ "call minute.valueOf",
+ "get second.valueOf",
+ "call second.valueOf"
+ ]
+ ],
+ [
+ "infinite millisecond",
+ [O(2, "year"), O(1, "month"), O(1, "day"), O(1, "hour"), O(1, "minute"), O(1, "second"), O(Infinity, "millisecond"), O(1, "microsecond"), O(1, "nanosecond")],
+ [
+ "get year.valueOf",
+ "call year.valueOf",
+ "get month.valueOf",
+ "call month.valueOf",
+ "get day.valueOf",
+ "call day.valueOf",
+ "get hour.valueOf",
+ "call hour.valueOf",
+ "get minute.valueOf",
+ "call minute.valueOf",
+ "get second.valueOf",
+ "call second.valueOf",
+ "get millisecond.valueOf",
+ "call millisecond.valueOf"
+ ]
+ ],
+ [
+ "infinite microsecond",
+ [O(2, "year"), O(1, "month"), O(1, "day"), O(1, "hour"), O(1, "minute"), O(1, "second"), O(1, "millisecond"), O(Infinity, "microsecond"), O(1, "nanosecond")],
+ [
+ "get year.valueOf",
+ "call year.valueOf",
+ "get month.valueOf",
+ "call month.valueOf",
+ "get day.valueOf",
+ "call day.valueOf",
+ "get hour.valueOf",
+ "call hour.valueOf",
+ "get minute.valueOf",
+ "call minute.valueOf",
+ "get second.valueOf",
+ "call second.valueOf",
+ "get millisecond.valueOf",
+ "call millisecond.valueOf",
+ "get microsecond.valueOf",
+ "call microsecond.valueOf"
+ ]
+ ],
+ [
+ "infinite nanosecond",
+ [O(2, "year"), O(1, "month"), O(1, "day"), O(1, "hour"), O(1, "minute"), O(1, "second"), O(1, "millisecond"), O(1, "microsecond"), O(Infinity, "nanosecond")],
+ [
+ "get year.valueOf",
+ "call year.valueOf",
+ "get month.valueOf",
+ "call month.valueOf",
+ "get day.valueOf",
+ "call day.valueOf",
+ "get hour.valueOf",
+ "call hour.valueOf",
+ "get minute.valueOf",
+ "call minute.valueOf",
+ "get second.valueOf",
+ "call second.valueOf",
+ "get millisecond.valueOf",
+ "call millisecond.valueOf",
+ "get microsecond.valueOf",
+ "call microsecond.valueOf",
+ "get nanosecond.valueOf",
+ "call nanosecond.valueOf"
+ ]
+ ],
+];
+
+for (const [description, args, expected] of tests) {
+ const actual = [];
+ const args_ = args.map((o) => o(actual));
+ assert.throws(RangeError, () => new Temporal.PlainDateTime(...args_), description);
+ assert.compareArray(actual, expected, `${description} order of operations`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/length.js
new file mode 100644
index 0000000000..af987a5cf0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/length.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.plaindatetime
+description: Temporal.PlainDateTime.length is 3
+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.PlainDateTime, "length", {
+ value: 3,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/limits.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/limits.js
new file mode 100644
index 0000000000..a02ad333df
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/limits.js
@@ -0,0 +1,22 @@
+// |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.plaindatetime
+description: Checking limits of representable PlainDateTime
+features: [Temporal]
+---*/
+
+assert.throws(
+ RangeError,
+ () => new Temporal.PlainDateTime(-271821, 4, 19, 0, 0, 0, 0, 0, 0),
+ "negative year out of bounds"
+);
+assert.throws(
+ RangeError,
+ () => new Temporal.PlainDateTime(275760, 9, 14, 0, 0, 0, 0, 0, 0),
+ "positive year out of bounds"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/microsecond-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/microsecond-undefined.js
new file mode 100644
index 0000000000..84238ad1db
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/microsecond-undefined.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.plaindatetime
+description: Microsecond argument defaults to 0 if not given
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const args = [2000, 5, 2, 12, 34, 56, 123];
+
+TemporalHelpers.assertPlainDateTime(
+ new Temporal.PlainDateTime(...args, undefined),
+ 2000, 5, "M05", 2, 12, 34, 56, 123, 0, 0,
+ "microsecond default argument (argument present)"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ new Temporal.PlainDateTime(...args),
+ 2000, 5, "M05", 2, 12, 34, 56, 123, 0, 0,
+ "microsecond default argument (argument missing)"
+);
+
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/millisecond-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/millisecond-undefined.js
new file mode 100644
index 0000000000..4b36f39d5f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/millisecond-undefined.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.plaindatetime
+description: Millisecond argument defaults to 0 if not given
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const args = [2000, 5, 2, 12, 34, 56];
+
+TemporalHelpers.assertPlainDateTime(
+ new Temporal.PlainDateTime(...args, undefined),
+ 2000, 5, "M05", 2, 12, 34, 56, 0, 0, 0,
+ "millisecond default argument (argument present)"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ new Temporal.PlainDateTime(...args),
+ 2000, 5, "M05", 2, 12, 34, 56, 0, 0, 0,
+ "millisecond default argument (argument missing)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/minute-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/minute-undefined.js
new file mode 100644
index 0000000000..ac6cfa5869
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/minute-undefined.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.plaindatetime
+description: Minute argument defaults to 0 if not given
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const args = [2000, 5, 2, 12];
+
+TemporalHelpers.assertPlainDateTime(
+ new Temporal.PlainDateTime(...args, undefined),
+ 2000, 5, "M05", 2, 12, 0, 0, 0, 0, 0,
+ "minute default argument (argument present)"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ new Temporal.PlainDateTime(...args),
+ 2000, 5, "M05", 2, 12, 0, 0, 0, 0, 0,
+ "minute default argument (argument missing)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/missing-arguments.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/missing-arguments.js
new file mode 100644
index 0000000000..a87d6ebe4c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/missing-arguments.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.plaindatetime
+description: RangeError thrown after processing given args when invoked without all required args
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "valueOf year",
+ "valueOf month",
+];
+const actual = [];
+const args = [
+ { valueOf() { actual.push("valueOf year"); return 1; } },
+ { valueOf() { actual.push("valueOf month"); return 1; } },
+];
+
+assert.throws(RangeError, () => new Temporal.PlainDateTime(...args));
+assert.compareArray(actual, expected, "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/name.js
new file mode 100644
index 0000000000..0ba2c7917d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/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.plaindatetime
+description: Temporal.PlainDateTime.name is "PlainDateTime"
+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.PlainDateTime, "name", {
+ value: "PlainDateTime",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/nanosecond-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/nanosecond-undefined.js
new file mode 100644
index 0000000000..89d9061c78
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/nanosecond-undefined.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.plaindatetime
+description: Nanosecond argument defaults to 0 if not given
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const args = [2000, 5, 2, 12, 34, 56, 123, 456];
+
+TemporalHelpers.assertPlainDateTime(
+ new Temporal.PlainDateTime(...args, undefined),
+ 2000, 5, "M05", 2, 12, 34, 56, 123, 456, 0,
+ "nanosecond default argument (argument present)"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ new Temporal.PlainDateTime(...args),
+ 2000, 5, "M05", 2, 12, 34, 56, 123, 456, 0,
+ "nanosecond default argument (argument missing)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/negative-infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/negative-infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..938d56bd5c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/negative-infinity-throws-rangeerror.js
@@ -0,0 +1,165 @@
+// |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.
+
+/*---
+description: Temporal.PlainDateTime throws a RangeError if any value is -Infinity
+esid: sec-temporal.plaindatetime
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+assert.throws(RangeError, () => new Temporal.PlainDateTime(-Infinity, 1, 1));
+assert.throws(RangeError, () => new Temporal.PlainDateTime(1970, -Infinity, 1));
+assert.throws(RangeError, () => new Temporal.PlainDateTime(1970, 1, -Infinity));
+assert.throws(RangeError, () => new Temporal.PlainDateTime(1970, 1, 1, -Infinity));
+assert.throws(RangeError, () => new Temporal.PlainDateTime(1970, 1, 1, 0, -Infinity));
+assert.throws(RangeError, () => new Temporal.PlainDateTime(1970, 1, 1, 0, 0, -Infinity));
+assert.throws(RangeError, () => new Temporal.PlainDateTime(1970, 1, 1, 0, 0, 0, -Infinity));
+assert.throws(RangeError, () => new Temporal.PlainDateTime(1970, 1, 1, 0, 0, 0, 0, -Infinity));
+assert.throws(RangeError, () => new Temporal.PlainDateTime(1970, 1, 1, 0, 0, 0, 0, 0, -Infinity));
+
+const O = (primitiveValue, propertyName) => (calls) => TemporalHelpers.toPrimitiveObserver(calls, primitiveValue, propertyName);
+const tests = [
+ [
+ "infinite year",
+ [O(-Infinity, "year"), O(1, "month"), O(1, "day")],
+ ["get year.valueOf", "call year.valueOf"
+ ]
+ ],
+ [
+ "infinite month",
+ [O(2, "year"), O(-Infinity, "month"), O(1, "day")],
+ ["get year.valueOf", "call year.valueOf", "get month.valueOf", "call month.valueOf"
+ ]
+ ],
+ [
+ "infinite day",
+ [O(2, "year"), O(1, "month"), O(-Infinity, "day")],
+ ["get year.valueOf", "call year.valueOf", "get month.valueOf", "call month.valueOf", "get day.valueOf", "call day.valueOf"
+ ]
+ ],
+ [
+ "infinite hour",
+ [O(2, "year"), O(1, "month"), O(1, "day"), O(-Infinity, "hour"), O(1, "minute"), O(1, "second"), O(1, "millisecond"), O(1, "microsecond"), O(1, "nanosecond")],
+ [
+ "get year.valueOf",
+ "call year.valueOf",
+ "get month.valueOf",
+ "call month.valueOf",
+ "get day.valueOf",
+ "call day.valueOf",
+ "get hour.valueOf",
+ "call hour.valueOf"
+ ]
+ ],
+ [
+ "infinite minute",
+ [O(2, "year"), O(1, "month"), O(1, "day"), O(1, "hour"), O(-Infinity, "minute"), O(1, "second"), O(1, "millisecond"), O(1, "microsecond"), O(1, "nanosecond")],
+ [
+ "get year.valueOf",
+ "call year.valueOf",
+ "get month.valueOf",
+ "call month.valueOf",
+ "get day.valueOf",
+ "call day.valueOf",
+ "get hour.valueOf",
+ "call hour.valueOf",
+ "get minute.valueOf",
+ "call minute.valueOf"
+ ]
+ ],
+ [
+ "infinite second",
+ [O(2, "year"), O(1, "month"), O(1, "day"), O(1, "hour"), O(1, "minute"), O(-Infinity, "second"), O(1, "millisecond"), O(1, "microsecond"), O(1, "nanosecond")],
+ [
+ "get year.valueOf",
+ "call year.valueOf",
+ "get month.valueOf",
+ "call month.valueOf",
+ "get day.valueOf",
+ "call day.valueOf",
+ "get hour.valueOf",
+ "call hour.valueOf",
+ "get minute.valueOf",
+ "call minute.valueOf",
+ "get second.valueOf",
+ "call second.valueOf"
+ ]
+ ],
+ [
+ "infinite millisecond",
+ [O(2, "year"), O(1, "month"), O(1, "day"), O(1, "hour"), O(1, "minute"), O(1, "second"), O(-Infinity, "millisecond"), O(1, "microsecond"), O(1, "nanosecond")],
+ [
+ "get year.valueOf",
+ "call year.valueOf",
+ "get month.valueOf",
+ "call month.valueOf",
+ "get day.valueOf",
+ "call day.valueOf",
+ "get hour.valueOf",
+ "call hour.valueOf",
+ "get minute.valueOf",
+ "call minute.valueOf",
+ "get second.valueOf",
+ "call second.valueOf",
+ "get millisecond.valueOf",
+ "call millisecond.valueOf"
+ ]
+ ],
+ [
+ "infinite microsecond",
+ [O(2, "year"), O(1, "month"), O(1, "day"), O(1, "hour"), O(1, "minute"), O(1, "second"), O(1, "millisecond"), O(-Infinity, "microsecond"), O(1, "nanosecond")],
+ [
+ "get year.valueOf",
+ "call year.valueOf",
+ "get month.valueOf",
+ "call month.valueOf",
+ "get day.valueOf",
+ "call day.valueOf",
+ "get hour.valueOf",
+ "call hour.valueOf",
+ "get minute.valueOf",
+ "call minute.valueOf",
+ "get second.valueOf",
+ "call second.valueOf",
+ "get millisecond.valueOf",
+ "call millisecond.valueOf",
+ "get microsecond.valueOf",
+ "call microsecond.valueOf"
+ ]
+ ],
+ [
+ "infinite nanosecond",
+ [O(2, "year"), O(1, "month"), O(1, "day"), O(1, "hour"), O(1, "minute"), O(1, "second"), O(1, "millisecond"), O(1, "microsecond"), O(-Infinity, "nanosecond")],
+ [
+ "get year.valueOf",
+ "call year.valueOf",
+ "get month.valueOf",
+ "call month.valueOf",
+ "get day.valueOf",
+ "call day.valueOf",
+ "get hour.valueOf",
+ "call hour.valueOf",
+ "get minute.valueOf",
+ "call minute.valueOf",
+ "get second.valueOf",
+ "call second.valueOf",
+ "get millisecond.valueOf",
+ "call millisecond.valueOf",
+ "get microsecond.valueOf",
+ "call microsecond.valueOf",
+ "get nanosecond.valueOf",
+ "call nanosecond.valueOf"
+ ]
+ ],
+];
+
+for (const [description, args, expected] of tests) {
+ const actual = [];
+ const args_ = args.map((o) => o(actual));
+ assert.throws(RangeError, () => new Temporal.PlainDateTime(...args_), description);
+ assert.compareArray(actual, expected, `${description} order of operations`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/order-of-operations.js
new file mode 100644
index 0000000000..ce518f86f8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/order-of-operations.js
@@ -0,0 +1,42 @@
+// |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.plaindatetime
+description: Arguments are converted to primitives in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const expected = [
+ "get (argument 0).valueOf",
+ "call (argument 0).valueOf",
+ "get (argument 1).valueOf",
+ "call (argument 1).valueOf",
+ "get (argument 2).valueOf",
+ "call (argument 2).valueOf",
+ "get (argument 3).valueOf",
+ "call (argument 3).valueOf",
+ "get (argument 4).valueOf",
+ "call (argument 4).valueOf",
+ "get (argument 5).valueOf",
+ "call (argument 5).valueOf",
+ "get (argument 6).valueOf",
+ "call (argument 6).valueOf",
+ "get (argument 7).valueOf",
+ "call (argument 7).valueOf",
+ "get (argument 8).valueOf",
+ "call (argument 8).valueOf",
+];
+
+const dateTimeArgs = [2020, 12, 24, 12, 34, 56, 123, 456, 789].map((value, idx) =>
+ TemporalHelpers.toPrimitiveObserver(actual, value, `(argument ${idx})`));
+
+const dateTime = new Temporal.PlainDateTime(...dateTimeArgs, "iso8601");
+assert.compareArray(actual, expected);
+
+TemporalHelpers.assertPlainDateTime(dateTime, 2020, 12, "M12", 24, 12, 34, 56, 123, 456, 789);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prop-desc.js
new file mode 100644
index 0000000000..f595d1e968
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/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.plaindatetime
+description: The "PlainDateTime" property of Temporal
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime,
+ "function",
+ "`typeof PlainDateTime` is `function`"
+);
+
+verifyProperty(Temporal, "PlainDateTime", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/ambiguous-date.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/ambiguous-date.js
new file mode 100644
index 0000000000..d05823e739
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/ambiguous-date.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.plaindatetime.prototype.add
+description: Ambiguous addition is handled according to the overflow option
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const jan31 = new Temporal.PlainDateTime(2020, 1, 31, 15, 0);
+
+TemporalHelpers.assertPlainDateTime(
+ jan31.add({ months: 1 }),
+ 2020, 2, "M02", 29, 15, 0, 0, 0, 0, 0,
+ "constrain when ambiguous result (overflow options not supplied)"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ jan31.add({ months: 1 }, { overflow: "constrain" }),
+ 2020, 2, "M02", 29, 15, 0, 0, 0, 0, 0,
+ "constrain when ambiguous result (overflow options supplied)"
+);
+
+assert.throws(
+ RangeError,
+ () => jan31.add({ months: 1 }, { overflow: "reject" }),
+ "throw when ambiguous result with reject"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-duration-max.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-duration-max.js
new file mode 100644
index 0000000000..9093b72e2d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-duration-max.js
@@ -0,0 +1,58 @@
+// |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.plaindatetime.prototype.add
+description: Maximum allowed duration
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(1970, 1, 1);
+
+const maxCases = [
+ ["P273790Y8M12DT23H59M59.999999999S", "string with max years"],
+ [{ years: 273790, months: 8, days: 12, nanoseconds: 86399999999999 }, "property bag with max years"],
+ ["P3285488M12DT23H59M59.999999999S", "string with max months"],
+ [{ months: 3285488, days: 12, nanoseconds: 86399999999999 }, "property bag with max months"],
+ ["P14285714W2DT23H59M59.999999999S", "string with max weeks"],
+ [{ weeks: 14285714, days: 2, nanoseconds: 86399999999999 }, "property bag with max weeks"],
+ ["P100000000DT23H59M59.999999999S", "string with max days"],
+ [{ days: 100000000, nanoseconds: 86399999999999 }, "property bag with max days"],
+ ["PT2400000023H59M59.999999999S", "string with max hours"],
+ [{ hours: 2400000023, nanoseconds: 3599999999999 }, "property bag with max hours"],
+ ["PT144000001439M59.999999999S", "string with max minutes"],
+ [{ minutes: 144000001439, nanoseconds: 59999999999 }, "property bag with max minutes"],
+ ["PT8640000086399.999999999S", "string with max seconds"],
+ [{ seconds: 8640000086399, nanoseconds: 999999999 }, "property bag with max seconds"],
+];
+
+for (const [arg, descr] of maxCases) {
+ const result = instance.add(arg);
+ TemporalHelpers.assertPlainDateTime(result, 275760, 9, "M09", 13, 23, 59, 59, 999, 999, 999, `operation succeeds with ${descr}`);
+}
+
+const minCases = [
+ ["-P273790Y8M11DT23H59M59.999999999S", "string with min years"],
+ [{ years: -273790, months: -8, days: -11, nanoseconds: -86399999999999 }, "property bag with min years"],
+ ["-P3285488M11DT23H59M59.999999999S", "string with min months"],
+ [{ months: -3285488, days: -11, nanoseconds: -86399999999999 }, "property bag with min months"],
+ ["-P14285714W2DT23H59M59.999999999S", "string with min weeks"],
+ [{ weeks: -14285714, days: -2, nanoseconds: -86399999999999 }, "property bag with min weeks"],
+ ["-P100000000DT23H59M59.999999999S", "string with min days"],
+ [{ days: -100000000, nanoseconds: -86399999999999 }, "property bag with min days"],
+ ["-PT2400000023H59M59.999999999S", "string with min hours"],
+ [{ hours: -2400000023, nanoseconds: -3599999999999 }, "property bag with min hours"],
+ ["-PT144000001439M59.999999999S", "string with min minutes"],
+ [{ minutes: -144000001439, nanoseconds: -59999999999 }, "property bag with min minutes"],
+ ["-PT8640000086399.999999999S", "string with min seconds"],
+ [{ seconds: -8640000086399, nanoseconds: -999999999 }, "property bag with min seconds"],
+];
+
+for (const [arg, descr] of minCases) {
+ const result = instance.add(arg);
+ TemporalHelpers.assertPlainDateTime(result, -271821, 4, "M04", 19, 0, 0, 0, 0, 0, 1, `operation succeeds with ${descr}`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-duration-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-duration-out-of-range.js
new file mode 100644
index 0000000000..19e7edbb84
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-duration-out-of-range.js
@@ -0,0 +1,75 @@
+// |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.plaindatetime.prototype.add
+description: Duration-like argument that is out of range
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(1970, 1, 1);
+
+const cases = [
+ // 2^32 = 4294967296
+ ["P4294967296Y", "string with years > max"],
+ [{ years: 4294967296 }, "property bag with years > max"],
+ ["-P4294967296Y", "string with years < min"],
+ [{ years: -4294967296 }, "property bag with years < min"],
+ ["P4294967296M", "string with months > max"],
+ [{ months: 4294967296 }, "property bag with months > max"],
+ ["-P4294967296M", "string with months < min"],
+ [{ months: -4294967296 }, "property bag with months < min"],
+ ["P4294967296W", "string with weeks > max"],
+ [{ weeks: 4294967296 }, "property bag with weeks > max"],
+ ["-P4294967296W", "string with weeks < min"],
+ [{ weeks: -4294967296 }, "property bag with weeks < min"],
+
+ // ceil(max safe integer / 86400) = 104249991375
+ ["P104249991375D", "string with days > max"],
+ [{ days: 104249991375 }, "property bag with days > max"],
+ ["P104249991374DT24H", "string where hours balance into days > max"],
+ [{ days: 104249991374, hours: 24 }, "property bag where hours balance into days > max"],
+ ["-P104249991375D", "string with days < min"],
+ [{ days: -104249991375 }, "property bag with days < min"],
+ ["-P104249991374DT24H", "string where hours balance into days < min"],
+ [{ days: -104249991374, hours: -24 }, "property bag where hours balance into days < min"],
+
+ // ceil(max safe integer / 3600) = 2501999792984
+ ["PT2501999792984H", "string with hours > max"],
+ [{ hours: 2501999792984 }, "property bag with hours > max"],
+ ["PT2501999792983H60M", "string where minutes balance into hours > max"],
+ [{ hours: 2501999792983, minutes: 60 }, "property bag where minutes balance into hours > max"],
+ ["-PT2501999792984H", "string with hours < min"],
+ [{ hours: -2501999792984 }, "property bag with hours < min"],
+ ["-PT2501999792983H60M", "string where minutes balance into hours < min"],
+ [{ hours: -2501999792983, minutes: -60 }, "property bag where minutes balance into hours < min"],
+
+ // ceil(max safe integer / 60) = 150119987579017
+ ["PT150119987579017M", "string with minutes > max"],
+ [{ minutes: 150119987579017 }, "property bag with minutes > max"],
+ ["PT150119987579016M60S", "string where seconds balance into minutes > max"],
+ [{ minutes: 150119987579016, seconds: 60 }, "property bag where seconds balance into minutes > max"],
+ ["-PT150119987579017M", "string with minutes < min"],
+ [{ minutes: -150119987579017 }, "property bag with minutes < min"],
+ ["-PT150119987579016M60S", "string where seconds balance into minutes < min"],
+ [{ minutes: -150119987579016, seconds: -60 }, "property bag where seconds balance into minutes < min"],
+
+ // 2^53 = 9007199254740992
+ ["PT9007199254740992S", "string with seconds > max"],
+ [{ seconds: 9007199254740992 }, "property bag with seconds > max"],
+ [{ seconds: 9007199254740991, milliseconds: 1000 }, "property bag where milliseconds balance into seconds > max"],
+ [{ seconds: 9007199254740991, microseconds: 1000000 }, "property bag where microseconds balance into seconds > max"],
+ [{ seconds: 9007199254740991, nanoseconds: 1000000000 }, "property bag where nanoseconds balance into seconds > max"],
+ ["-PT9007199254740992S", "string with seconds < min"],
+ [{ seconds: -9007199254740992 }, "property bag with seconds < min"],
+ [{ seconds: -9007199254740991, milliseconds: -1000 }, "property bag where milliseconds balance into seconds < min"],
+ [{ seconds: -9007199254740991, microseconds: -1000000 }, "property bag where microseconds balance into seconds < min"],
+ [{ seconds: -9007199254740991, nanoseconds: -1000000000 }, "property bag where nanoseconds balance into seconds < min"],
+];
+
+for (const [arg, descr] of cases) {
+ assert.throws(RangeError, () => instance.add(arg), `${descr} is out of range`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-duration.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-duration.js
new file mode 100644
index 0000000000..0a7bab206c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-duration.js
@@ -0,0 +1,20 @@
+// |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.plaindatetime.prototype.add
+description: Duration object arguments are handled
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const jan31 = new Temporal.PlainDateTime(2020, 1, 31, 15, 0);
+
+TemporalHelpers.assertPlainDateTime(
+ jan31.add(Temporal.Duration.from("P1MT1S")),
+ 2020, 2, "M02", 29, 15, 0, 1, 0, 0, 0,
+ "Duration argument"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-invalid-property.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-invalid-property.js
new file mode 100644
index 0000000000..19f1fe193a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-invalid-property.js
@@ -0,0 +1,31 @@
+// |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.plaindatetime.prototype.add
+description: temporalDurationLike object must contain at least one correctly spelled property
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 15, 30, 45, 987, 654, 321);
+
+assert.throws(
+ TypeError,
+ () => instance.add({}),
+ "Throws TypeError if no property is present"
+);
+
+assert.throws(
+ TypeError,
+ () => instance.add({ nonsense: true }),
+ "Throws TypeError if no recognized property is present"
+);
+
+assert.throws(
+ TypeError,
+ () => instance.add({ sign: 1 }),
+ "Sign property is not recognized"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-mixed-sign.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-mixed-sign.js
new file mode 100644
index 0000000000..c2d45bd77d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-mixed-sign.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.plaindatetime.prototype.add
+description: Positive and negative values in the temporalDurationLike argument are not acceptable
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 15, 30, 45, 987, 654, 321);
+
+["constrain", "reject"].forEach((overflow) => {
+ assert.throws(
+ RangeError,
+ () => instance.add({ hours: 1, minutes: -30 }, { overflow }),
+ `mixed positive and negative values always throw (overflow = "${overflow}")`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-not-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-not-object.js
new file mode 100644
index 0000000000..cd5eb71e7d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-not-object.js
@@ -0,0 +1,22 @@
+// |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.plaindatetime.prototype.add
+description: Passing a primitive other than string to add() throws
+features: [Symbol, Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 15, 30, 45, 987, 654, 321);
+assert.throws(TypeError, () => instance.add(undefined), "undefined");
+assert.throws(TypeError, () => instance.add(null), "null");
+assert.throws(TypeError, () => instance.add(true), "boolean");
+assert.throws(RangeError, () => instance.add(""), "empty string");
+assert.throws(TypeError, () => instance.add(Symbol()), "Symbol");
+assert.throws(TypeError, () => instance.add(7), "number");
+assert.throws(TypeError, () => instance.add(7n), "bigint");
+assert.throws(TypeError, () => instance.add([]), "array");
+assert.throws(TypeError, () => instance.add(() => {}), "function");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-singular-properties.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-singular-properties.js
new file mode 100644
index 0000000000..9ceaeb21c3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-singular-properties.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.plaindatetime.prototype.add
+description: Singular properties in the property bag are always ignored
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 15, 30, 45, 987, 654, 321);
+
+[
+ { year: 1 },
+ { month: 2 },
+ { week: 3 },
+ { day: 4 },
+ { hour: 5 },
+ { minute: 6 },
+ { second: 7 },
+ { millisecond: 8 },
+ { microsecond: 9 },
+ { nanosecond: 10 },
+].forEach((badObject) => {
+ assert.throws(TypeError, () => instance.add(badObject),
+ "Throw TypeError if temporalDurationLike is not valid");
+});
+
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-string-fractional-units-rounding-mode.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-string-fractional-units-rounding-mode.js
new file mode 100644
index 0000000000..cd03de8f67
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-string-fractional-units-rounding-mode.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.plaindatetime.prototype.add
+description: Strings with fractional duration units are rounded with the correct rounding mode
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2);
+
+TemporalHelpers.assertPlainDateTime(datetime.add("PT1.03125H"), 2000, 5, "M05", 2, 1, 1, 52, 500, 0, 0,
+ "positive fractional units rounded with correct rounding mode");
+TemporalHelpers.assertPlainDateTime(datetime.add("-PT1.03125H"), 2000, 5, "M05", 1, 22, 58, 7, 500, 0, 0,
+ "negative fractional units rounded with correct rounding mode");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-string-negative-fractional-units.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-string-negative-fractional-units.js
new file mode 100644
index 0000000000..56f8979c29
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-string-negative-fractional-units.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.plaindatetime.prototype.add
+description: Strings with fractional duration units are treated with the correct sign
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2);
+
+const resultHours = instance.add("-PT24.567890123H");
+TemporalHelpers.assertPlainDateTime(resultHours, 2000, 4, "M04", 30, 23, 25, 55, 595, 557, 200, "negative fractional hours");
+
+const resultMinutes = instance.add("-PT1440.567890123M");
+TemporalHelpers.assertPlainDateTime(resultMinutes, 2000, 4, "M04", 30, 23, 59, 25, 926, 592, 620, "negative fractional minutes");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-string.js
new file mode 100644
index 0000000000..e2cd1bde41
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-string.js
@@ -0,0 +1,16 @@
+// |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.plaindatetime.prototype.add
+description: A string is parsed into the correct object when passed as the argument
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = Temporal.PlainDateTime.from({ year: 2000, month: 5, day: 2, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321 });
+const result = instance.add("P3D");
+TemporalHelpers.assertPlainDateTime(result, 2000, 5, "M05", 5, 0, 34, 56, 987, 654, 321);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/balance-negative-time-units.js
new file mode 100644
index 0000000000..cfc9691634
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/balance-negative-time-units.js
@@ -0,0 +1,52 @@
+// |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.plaindatetime.prototype.add
+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-addtime step 8:
+ 8. Return ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_).
+ sec-temporal-adddatetime step 1:
+ 1. Let _timeResult_ be ? AddTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_, _hours_, _minutes_, _seconds_, _milliseconds_, _microseconds_, _nanoseconds_).
+ sec-temporal.plaindatetime.prototype.add step 5:
+ 5. Let _result_ be ? AddDateTime(_dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], _dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], _dateTime_.[[Calendar]], _duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(1996, 5, 2, 1, 1, 1, 1, 1, 1);
+
+const result1 = datetime.add(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, -2));
+TemporalHelpers.assertPlainDateTime(result1, 1996, 5, "M05", 2, 1, 1, 1, 1, 0, 999, "nanoseconds balance");
+
+const result2 = datetime.add(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, -2));
+TemporalHelpers.assertPlainDateTime(result2, 1996, 5, "M05", 2, 1, 1, 1, 0, 999, 1, "microseconds balance");
+
+const result3 = datetime.add(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, -2));
+TemporalHelpers.assertPlainDateTime(result3, 1996, 5, "M05", 2, 1, 1, 0, 999, 1, 1, "milliseconds balance");
+
+const result4 = datetime.add(new Temporal.Duration(0, 0, 0, 0, 0, 0, -2));
+TemporalHelpers.assertPlainDateTime(result4, 1996, 5, "M05", 2, 1, 0, 59, 1, 1, 1, "seconds balance");
+
+const result5 = datetime.add(new Temporal.Duration(0, 0, 0, 0, 0, -2));
+TemporalHelpers.assertPlainDateTime(result5, 1996, 5, "M05", 2, 0, 59, 1, 1, 1, 1, "minutes balance");
+
+const result6 = datetime.add(new Temporal.Duration(0, 0, 0, 0, -2));
+TemporalHelpers.assertPlainDateTime(result6, 1996, 5, "M05", 1, 23, 1, 1, 1, 1, 1, "hours balance");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/branding.js
new file mode 100644
index 0000000000..7c97373b75
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/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.plaindatetime.prototype.add
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const add = Temporal.PlainDateTime.prototype.add;
+
+assert.sameValue(typeof add, "function");
+
+const args = [new Temporal.Duration(5)];
+
+assert.throws(TypeError, () => add.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => add.apply(null, args), "null");
+assert.throws(TypeError, () => add.apply(true, args), "true");
+assert.throws(TypeError, () => add.apply("", args), "empty string");
+assert.throws(TypeError, () => add.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => add.apply(1, args), "1");
+assert.throws(TypeError, () => add.apply({}, args), "plain object");
+assert.throws(TypeError, () => add.apply(Temporal.PlainDateTime, args), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => add.apply(Temporal.PlainDateTime.prototype, args), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..90cd85ab31
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plaindatetime.prototype.add
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dateAddOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dateAdd");
+Object.defineProperty(Temporal.Calendar.prototype, "dateAdd", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("dateAdd should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, "iso8601");
+instance.add(new Temporal.Duration(1));
+
+Object.defineProperty(Temporal.Calendar.prototype, "dateAdd", dateAddOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/builtin.js
new file mode 100644
index 0000000000..b095e5d926
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/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.plaindatetime.prototype.add
+description: >
+ Tests that Temporal.PlainDateTime.prototype.add
+ 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.PlainDateTime.prototype.add),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.prototype.add),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.prototype.add),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.prototype.add.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/calendar-dateadd.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/calendar-dateadd.js
new file mode 100644
index 0000000000..5870fac724
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/calendar-dateadd.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.plaindatetime.prototype.add
+description: PlainDateTime.prototype.add should call dateAdd with the appropriate values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ dateAdd(plainDate, duration, options) {
+ ++calls;
+ TemporalHelpers.assertPlainDate(plainDate, 2020, 3, "M03", 14, "plainDate argument");
+ TemporalHelpers.assertDuration(duration, 0, 10, 0, 1, 0, 0, 0, 0, 0, 0, "duration argument");
+ assert.sameValue(typeof options, "object", "options argument: type");
+ assert.sameValue(Object.getPrototypeOf(options), null, "options argument: prototype");
+ return super.dateAdd(plainDate, duration, options);
+ }
+}
+
+const plainDateTime = new Temporal.PlainDateTime(2020, 3, 14, 12, 34, 56, 987, 654, 321, new CustomCalendar());
+const result = plainDateTime.add({ months: 10, hours: 14 });
+TemporalHelpers.assertPlainDateTime(result, 2021, 1, "M01", 15, 2, 34, 56, 987, 654, 321);
+assert.sameValue(calls, 1, "should have called dateAdd");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/hour-overflow.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/hour-overflow.js
new file mode 100644
index 0000000000..4d6b28f57e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/hour-overflow.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.plaindatetime.prototype.add
+description: Testing overflow hours (adding hours that push one to the next/previous day)
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2020, 5, 31, 23, 12, 38, 271, 986, 102);
+
+TemporalHelpers.assertPlainDateTime(
+ earlier.add({ hours: 2 }),
+ 2020, 6, "M06", 1, 1, 12, 38, 271, 986, 102,
+ "hours overflow (push to next day)"
+);
+
+const later = new Temporal.PlainDateTime(2019, 10, 29, 10, 46, 38, 271, 986, 102);
+
+TemporalHelpers.assertPlainDateTime(
+ later.add({ hours: -12 }),
+ 2019, 10, "M10", 28, 22, 46, 38, 271, 986, 102,
+ "hours overflow (push to previous day)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..d9e9e8ce14
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/infinity-throws-rangeerror.js
@@ -0,0 +1,38 @@
+// |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.
+
+/*---
+description: Temporal.PlainDateTime.prototype.add throws a RangeError if any value in a property bag is Infinity
+esid: sec-temporal.plaindatetime.prototype.add
+features: [Temporal]
+---*/
+
+const overflows = ["constrain", "reject"];
+const fields = ["years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"];
+
+const instance = Temporal.PlainDateTime.from({ year: 2000, month: 5, day: 2, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321 });
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.add({ [field]: Infinity }, { overflow }));
+ });
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return Infinity;
+ }
+};
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => instance.add({ [field]: obj }, { overflow }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/length.js
new file mode 100644
index 0000000000..97c4956693
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/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.plaindatetime.prototype.add
+description: Temporal.PlainDateTime.prototype.add.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.PlainDateTime.prototype.add, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/limits.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/limits.js
new file mode 100644
index 0000000000..6c9178f9e3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/limits.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.plaindatetime.prototype.add
+description: Checking limits of representable PlainDateTime
+features: [Temporal]
+---*/
+
+const min = new Temporal.PlainDateTime(-271821, 4, 19, 0, 0, 0, 0, 0, 1);
+const max = new Temporal.PlainDateTime(275760, 9, 13, 23, 59, 59, 999, 999, 999);
+
+["reject", "constrain"].forEach((overflow) => {
+ assert.throws(
+ RangeError,
+ () => max.add({ nanoseconds: 1 }, { overflow }),
+ `adding 1 nanosecond beyond maximum limit (overflow = ${overflow})`
+ );
+ assert.throws(
+ RangeError,
+ () => min.add({ nanoseconds: -1 }, { overflow }),
+ `adding -1 nanosecond beyond minimum limit (overflow = ${overflow})`
+ );
+
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/name.js
new file mode 100644
index 0000000000..5e3bad4a9c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/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.plaindatetime.prototype.add
+description: Temporal.PlainDateTime.prototype.add.name is "add".
+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.PlainDateTime.prototype.add, "name", {
+ value: "add",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/negative-duration.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/negative-duration.js
new file mode 100644
index 0000000000..826224ef34
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/negative-duration.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.plaindatetime.prototype.add
+description: Negative durations can be supplied
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const jan31 = new Temporal.PlainDateTime(2020, 1, 31, 15, 0);
+
+TemporalHelpers.assertPlainDateTime(
+ jan31.add({ minutes: -30 }),
+ 2020, 1, "M01", 31, 14, 30, 0, 0, 0, 0,
+ "negative minutes"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ jan31.add({ seconds: -30 }),
+ 2020, 1, "M01", 31, 14, 59, 30, 0, 0, 0,
+ "negative seconds"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/negative-infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/negative-infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..97b08ed520
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/negative-infinity-throws-rangeerror.js
@@ -0,0 +1,38 @@
+// |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.
+
+/*---
+description: Temporal.PlainDateTime.prototype.add throws a RangeError if any value in a property bag is -Infinity
+esid: sec-temporal.plaindatetime.prototype.add
+features: [Temporal]
+---*/
+
+const overflows = ["constrain", "reject"];
+const fields = ["years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"];
+
+const instance = Temporal.PlainDateTime.from({ year: 2000, month: 5, day: 2, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321 });
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.add({ [field]: -Infinity }, { overflow }));
+ });
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return -Infinity;
+ }
+};
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => instance.add({ [field]: obj }, { overflow }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/non-integer-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/non-integer-throws-rangeerror.js
new file mode 100644
index 0000000000..24ce0d5391
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/non-integer-throws-rangeerror.js
@@ -0,0 +1,29 @@
+// |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.plaindatetime.prototype.add
+description: A non-integer value for any recognized property in the property bag, throws a RangeError
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 15, 30, 45, 987, 654, 321);
+const fields = [
+ "years",
+ "months",
+ "weeks",
+ "days",
+ "hours",
+ "minutes",
+ "seconds",
+ "milliseconds",
+ "microseconds",
+ "nanoseconds",
+];
+fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.add({ [field]: 1.5 }));
+ assert.throws(RangeError, () => instance.add({ [field]: -1.5 }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/not-a-constructor.js
new file mode 100644
index 0000000000..13c3ca718d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/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.plaindatetime.prototype.add
+description: >
+ Temporal.PlainDateTime.prototype.add 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.PlainDateTime.prototype.add();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.prototype.add), false,
+ "isConstructor(Temporal.PlainDateTime.prototype.add)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/options-empty.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/options-empty.js
new file mode 100644
index 0000000000..e45efaaaae
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/options-empty.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.plaindatetime.prototype.add
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const jan31 = new Temporal.PlainDateTime(2020, 1, 31, 15, 0);
+
+TemporalHelpers.assertPlainDateTime(
+ jan31.add({ months: 1 }, {}),
+ 2020, 2, "M02", 29, 15, 0, 0, 0, 0, 0,
+ "options may be empty object"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ jan31.add({ months: 1 }, () => {}),
+ 2020, 2, "M02", 29, 15, 0, 0, 0, 0, 0,
+ "options may be function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/options-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/options-invalid.js
new file mode 100644
index 0000000000..31a98f2787
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/options-invalid.js
@@ -0,0 +1,23 @@
+// |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.plaindatetime.prototype.add
+description: Various invalid (wrong type) values for options argument
+features: [Temporal, Symbol]
+---*/
+
+const jan31 = new Temporal.PlainDateTime(2020, 1, 31, 15, 0);
+
+const badOptions = [null, 1, 'hello', true, Symbol('foo'), 1n];
+
+badOptions.forEach((bad) => {
+ assert.throws(
+ TypeError,
+ () => jan31.add({ years: 1 }, bad),
+ `invalid options (${typeof bad})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/options-undefined.js
new file mode 100644
index 0000000000..410972a985
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/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.plaindatetime.prototype.add
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 1, 31, 12, 34, 56, 987, 654, 321);
+const duration = { months: 1 };
+
+const explicit = datetime.add(duration, undefined);
+assert.sameValue(explicit.month, 2, "default overflow is constrain");
+assert.sameValue(explicit.day, 29, "default overflow is constrain");
+
+const implicit = datetime.add(duration);
+assert.sameValue(implicit.month, 2, "default overflow is constrain");
+assert.sameValue(implicit.day, 29, "default overflow is constrain");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/options-wrong-type.js
new file mode 100644
index 0000000000..30da2b6095
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/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.plaindatetime.prototype.add
+description: TypeError thrown when options argument is a primitive
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const badOptions = [
+ null,
+ true,
+ "some string",
+ Symbol(),
+ 1,
+ 2n,
+];
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2);
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.add({ months: 1 }, value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/order-of-operations.js
new file mode 100644
index 0000000000..11024f653b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/order-of-operations.js
@@ -0,0 +1,127 @@
+// |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.plaindatetime.prototype.add
+description: Properties on an object passed to add() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ // ToTemporalDurationRecord
+ "get fields.days",
+ "get fields.days.valueOf",
+ "call fields.days.valueOf",
+ "get fields.hours",
+ "get fields.hours.valueOf",
+ "call fields.hours.valueOf",
+ "get fields.microseconds",
+ "get fields.microseconds.valueOf",
+ "call fields.microseconds.valueOf",
+ "get fields.milliseconds",
+ "get fields.milliseconds.valueOf",
+ "call fields.milliseconds.valueOf",
+ "get fields.minutes",
+ "get fields.minutes.valueOf",
+ "call fields.minutes.valueOf",
+ "get fields.months",
+ "get fields.months.valueOf",
+ "call fields.months.valueOf",
+ "get fields.nanoseconds",
+ "get fields.nanoseconds.valueOf",
+ "call fields.nanoseconds.valueOf",
+ "get fields.seconds",
+ "get fields.seconds.valueOf",
+ "call fields.seconds.valueOf",
+ "get fields.weeks",
+ "get fields.weeks.valueOf",
+ "call fields.weeks.valueOf",
+ "get fields.years",
+ "get fields.years.valueOf",
+ "call fields.years.valueOf",
+ // AddDateTime -> AddDate
+ "get this.calendar.dateAdd",
+ "call this.calendar.dateAdd",
+ // inside Calendar.p.dateAdd
+ "get options.overflow",
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+];
+const actual = [];
+
+const calendar = TemporalHelpers.calendarObserver(actual, "this.calendar");
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+// clear observable operations that occurred during the constructor call
+actual.splice(0);
+
+const fields = TemporalHelpers.propertyBagObserver(actual, {
+ years: 1,
+ months: 1,
+ weeks: 1,
+ days: 1,
+ hours: 1,
+ minutes: 1,
+ seconds: 1,
+ milliseconds: 1,
+ microseconds: 1,
+ nanoseconds: 1,
+}, "fields");
+
+const options = TemporalHelpers.propertyBagObserver(actual, { overflow: "constrain" }, "options");
+
+instance.add(fields, options);
+assert.compareArray(actual, expected, "order of operations");
+
+actual.splice(0); // clear
+
+const noCalendarExpected = [
+ // ToTemporalDurationRecord
+ "get fields.days",
+ "get fields.days.valueOf",
+ "call fields.days.valueOf",
+ "get fields.hours",
+ "get fields.hours.valueOf",
+ "call fields.hours.valueOf",
+ "get fields.microseconds",
+ "get fields.microseconds.valueOf",
+ "call fields.microseconds.valueOf",
+ "get fields.milliseconds",
+ "get fields.milliseconds.valueOf",
+ "call fields.milliseconds.valueOf",
+ "get fields.minutes",
+ "get fields.minutes.valueOf",
+ "call fields.minutes.valueOf",
+ "get fields.months",
+ "get fields.nanoseconds",
+ "get fields.nanoseconds.valueOf",
+ "call fields.nanoseconds.valueOf",
+ "get fields.seconds",
+ "get fields.seconds.valueOf",
+ "call fields.seconds.valueOf",
+ "get fields.weeks",
+ "get fields.years",
+ "get this.calendar.dateAdd",
+ // AddDateTime -> AddDate
+ "get options.overflow",
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+];
+
+const noCalendarFields = TemporalHelpers.propertyBagObserver(actual, {
+ days: 1,
+ hours: 1,
+ minutes: 1,
+ seconds: 1,
+ milliseconds: 1,
+ microseconds: 1,
+ nanoseconds: 1,
+}, "fields");
+
+instance.add(noCalendarFields, options);
+assert.compareArray(actual, noCalendarExpected, "order of operations with no calendar operation");
+
+actual.splice(0); // clear
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/overflow-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/overflow-invalid-string.js
new file mode 100644
index 0000000000..e45665d393
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/overflow-invalid-string.js
@@ -0,0 +1,35 @@
+// |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.plaindatetime.prototype.add
+description: RangeError thrown when overflow option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal.calendar.prototype.dateadd step 7:
+ 7. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal-adddatetime step 4:
+ 4. Let _addedDate_ be ? CalendarDateAdd(_calendar_, _datePart_, _dateDuration_, _options_).
+ sec-temporal.plaindatetime.prototype.add step 5:
+ 5. Let _result_ be ? AddDateTime(_dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], _dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], _dateTime_.[[Calendar]], _duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], _options_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12);
+const duration = new Temporal.Duration(3, 3, 0, 3, 3);
+
+
+const badOverflows = ["", "CONSTRAIN", "balance", "other string", "constra\u0131n", "reject\0"];
+for (const overflow of badOverflows) {
+ assert.throws(
+ RangeError,
+ () => datetime.add({ months: 1 }, { overflow }),
+ `invalid overflow ("${overflow}")`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/overflow-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/overflow-undefined.js
new file mode 100644
index 0000000000..d874734c28
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/overflow-undefined.js
@@ -0,0 +1,31 @@
+// |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.plaindatetime.prototype.add
+description: Fallback value for overflow option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal.calendar.prototype.dateadd step 7:
+ 7. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal-adddatetime step 4:
+ 4. Let _addedDate_ be ? CalendarDateAdd(_calendar_, _datePart_, _dateDuration_, _options_).
+ sec-temporal.plaindatetime.prototype.add step 5:
+ 5. Let _result_ be ? AddDateTime(_dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], _dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], _dateTime_.[[Calendar]], _duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 31, 12);
+const duration = new Temporal.Duration(3, 1);
+
+const explicit = datetime.add(duration, { overflow: undefined });
+TemporalHelpers.assertPlainDateTime(explicit, 2003, 6, "M06", 30, 12, 0, 0, 0, 0, 0, "default overflow is constrain");
+const implicit = datetime.add(duration, {});
+TemporalHelpers.assertPlainDateTime(implicit, 2003, 6, "M06", 30, 12, 0, 0, 0, 0, 0, "default overflow is constrain");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/overflow-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/overflow-wrong-type.js
new file mode 100644
index 0000000000..f9be928b8d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/overflow-wrong-type.js
@@ -0,0 +1,30 @@
+// |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.plaindatetime.prototype.add
+description: Type conversions for overflow option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal.calendar.prototype.dateadd step 7:
+ 7. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal-adddatetime step 4:
+ 4. Let _addedDate_ be ? CalendarDateAdd(_calendar_, _datePart_, _dateDuration_, _options_).
+ sec-temporal.plaindatetime.prototype.add step 5:
+ 5. Let _result_ be ? AddDateTime(_dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], _dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], _dateTime_.[[Calendar]], _duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12);
+const duration = new Temporal.Duration(3, 3, 0, 3, 3);
+TemporalHelpers.checkStringOptionWrongType("overflow", "constrain",
+ (overflow) => datetime.add(duration, { overflow }),
+ (result, descr) => TemporalHelpers.assertPlainDateTime(result, 2003, 8, "M08", 5, 15, 0, 0, 0, 0, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/prop-desc.js
new file mode 100644
index 0000000000..6934b827f9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/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.plaindatetime.prototype.add
+description: The "add" property of Temporal.PlainDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.prototype.add,
+ "function",
+ "`typeof PlainDateTime.prototype.add` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime.prototype, "add", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/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/PlainDateTime/prototype/add/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/subclassing-ignored.js
new file mode 100644
index 0000000000..8e42154531
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/subclassing-ignored.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.plaindatetime.prototype.add
+description: Objects of a subclass are never created as return values for add()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.PlainDateTime,
+ [2000, 5, 2, 12, 34, 56, 987, 654, 321],
+ "add",
+ [{ nanoseconds: 1 }],
+ (result) => TemporalHelpers.assertPlainDateTime(result, 2000, 5, "M05", 2, 12, 34, 56, 987, 654, 322),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/calendarId/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/calendarId/branding.js
new file mode 100644
index 0000000000..f6208fa688
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/calendarId/branding.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-get-temporal.plaindatetime.prototype.calendarid
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const calendarId = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "calendarId").get;
+
+assert.sameValue(typeof calendarId, "function");
+
+assert.throws(TypeError, () => calendarId.call(undefined), "undefined");
+assert.throws(TypeError, () => calendarId.call(null), "null");
+assert.throws(TypeError, () => calendarId.call(true), "true");
+assert.throws(TypeError, () => calendarId.call(""), "empty string");
+assert.throws(TypeError, () => calendarId.call(Symbol()), "symbol");
+assert.throws(TypeError, () => calendarId.call(1), "1");
+assert.throws(TypeError, () => calendarId.call({}), "plain object");
+assert.throws(TypeError, () => calendarId.call(Temporal.PlainDateTime), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => calendarId.call(Temporal.PlainDateTime.prototype), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/calendarId/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/calendarId/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/calendarId/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/calendarId/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/calendarId/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..51cdaea3c3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/calendarId/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plaindatetime.prototype.calendarid
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const idOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "id");
+Object.defineProperty(Temporal.Calendar.prototype, "id", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("id should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, "iso8601");
+instance.calendarId;
+
+Object.defineProperty(Temporal.Calendar.prototype, "id", idOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/calendarId/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/calendarId/prop-desc.js
new file mode 100644
index 0000000000..7b190782ca
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/calendarId/prop-desc.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-get-temporal.plaindatetime.prototype.calendarid
+description: The "calendarId" property of Temporal.PlainDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "calendarId");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/calendarId/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/calendarId/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/calendarId/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/constructor.js
new file mode 100644
index 0000000000..f9ba1858d6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/constructor.js
@@ -0,0 +1,20 @@
+// |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.plaindatetime.prototype.constructor
+description: Test for Temporal.PlainDateTime.prototype.constructor.
+info: The initial value of Temporal.PlainDateTime.prototype.constructor is %Temporal.PlainDateTime%.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype, "constructor", {
+ value: Temporal.PlainDateTime,
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/day/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/day/branding.js
new file mode 100644
index 0000000000..ecf7156ae5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/day/branding.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-get-temporal.plaindatetime.prototype.day
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const day = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "day").get;
+
+assert.sameValue(typeof day, "function");
+
+assert.throws(TypeError, () => day.call(undefined), "undefined");
+assert.throws(TypeError, () => day.call(null), "null");
+assert.throws(TypeError, () => day.call(true), "true");
+assert.throws(TypeError, () => day.call(""), "empty string");
+assert.throws(TypeError, () => day.call(Symbol()), "symbol");
+assert.throws(TypeError, () => day.call(1), "1");
+assert.throws(TypeError, () => day.call({}), "plain object");
+assert.throws(TypeError, () => day.call(Temporal.PlainDateTime), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => day.call(Temporal.PlainDateTime.prototype), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/day/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/day/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/day/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/day/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/day/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..d2a691a269
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/day/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plaindatetime.prototype.day
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dayOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "day");
+Object.defineProperty(Temporal.Calendar.prototype, "day", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("day should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, "iso8601");
+instance.day;
+
+Object.defineProperty(Temporal.Calendar.prototype, "day", dayOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/day/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/day/custom.js
new file mode 100644
index 0000000000..c06146b056
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/day/custom.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-get-temporal.plaindatetime.prototype.day
+description: Custom calendar tests for day().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ day(...args) {
+ ++calls;
+ assert.compareArray(args, [pdt], "day arguments");
+ return 7;
+ }
+}
+
+const calendar = new CustomCalendar();
+const pdt = new Temporal.PlainDateTime(1830, 8, 25, 20, 0, 0, 0, 0, 0, calendar);
+const result = pdt.day;
+assert.sameValue(result, 7, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/day/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/day/prop-desc.js
new file mode 100644
index 0000000000..d2a6f079e7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/day/prop-desc.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-get-temporal.plaindatetime.prototype.day
+description: The "day" property of Temporal.PlainDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "day");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/day/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/day/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/day/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/day/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/day/validate-calendar-value.js
new file mode 100644
index 0000000000..f029012c5d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/day/validate-calendar-value.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-get-temporal.plaindatetime.prototype.day
+description: Validate result returned from calendar day() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [null, TypeError],
+ [false, TypeError],
+ [Infinity, RangeError],
+ [-Infinity, RangeError],
+ [NaN, RangeError],
+ [-7, RangeError],
+ [-0.1, RangeError],
+ ["string", TypeError],
+ [Symbol("foo"), TypeError],
+ [7n, TypeError],
+ [{}, TypeError],
+ [true, TypeError],
+ [7.1, RangeError],
+ ["7", TypeError],
+ ["7.5", TypeError],
+ [{valueOf() { return 7; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ day() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainDateTime(1981, 12, 15, 14, 15, 45, 987, 654, 321, calendar);
+ assert.throws(error, () => instance.day, `${typeof result} ${String(result)} not converted to positive integer`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/basic.js
new file mode 100644
index 0000000000..29c5ae481c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/basic.js
@@ -0,0 +1,15 @@
+// |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-get-temporal.plaindatetime.prototype.dayofweek
+description: Checking day of week for a "normal" case (non-undefined, non-boundary case, etc.)
+features: [Temporal]
+---*/
+
+const calendar = Temporal.Calendar.from("iso8601");
+const datetime = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789, calendar);
+assert.sameValue(datetime.dayOfWeek, 4, "check day of week information");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/branding.js
new file mode 100644
index 0000000000..ff2d862576
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/branding.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-get-temporal.plaindatetime.prototype.dayofweek
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const dayOfWeek = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "dayOfWeek").get;
+
+assert.sameValue(typeof dayOfWeek, "function");
+
+assert.throws(TypeError, () => dayOfWeek.call(undefined), "undefined");
+assert.throws(TypeError, () => dayOfWeek.call(null), "null");
+assert.throws(TypeError, () => dayOfWeek.call(true), "true");
+assert.throws(TypeError, () => dayOfWeek.call(""), "empty string");
+assert.throws(TypeError, () => dayOfWeek.call(Symbol()), "symbol");
+assert.throws(TypeError, () => dayOfWeek.call(1), "1");
+assert.throws(TypeError, () => dayOfWeek.call({}), "plain object");
+assert.throws(TypeError, () => dayOfWeek.call(Temporal.PlainDateTime), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => dayOfWeek.call(Temporal.PlainDateTime.prototype), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..670601adc2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plaindatetime.prototype.dayofweek
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dayOfWeekOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dayOfWeek");
+Object.defineProperty(Temporal.Calendar.prototype, "dayOfWeek", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("dayOfWeek should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, "iso8601");
+instance.dayOfWeek;
+
+Object.defineProperty(Temporal.Calendar.prototype, "dayOfWeek", dayOfWeekOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/custom.js
new file mode 100644
index 0000000000..4bb559fd3f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/custom.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-get-temporal.plaindatetime.prototype.dayofweek
+description: Custom calendar tests for dayOfWeek().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ dayOfWeek(...args) {
+ ++calls;
+ assert.compareArray(args, [pdt], "dayOfWeek arguments");
+ return 7;
+ }
+}
+
+const calendar = new CustomCalendar();
+const pdt = new Temporal.PlainDateTime(1830, 8, 25, 20, 0, 0, 0, 0, 0, calendar);
+const result = pdt.dayOfWeek;
+assert.sameValue(result, 7, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/prop-desc.js
new file mode 100644
index 0000000000..8045da6228
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/prop-desc.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-get-temporal.plaindatetime.prototype.dayofweek
+description: The "dayOfWeek" property of Temporal.PlainDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "dayOfWeek");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/validate-calendar-value.js
new file mode 100644
index 0000000000..2612ef18f0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/validate-calendar-value.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-get-temporal.plaindatetime.prototype.dayofweek
+description: Validate result returned from calendar dayOfWeek() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [null, TypeError],
+ [false, TypeError],
+ [Infinity, RangeError],
+ [-Infinity, RangeError],
+ [NaN, RangeError],
+ [-7, RangeError],
+ [-0.1, RangeError],
+ ["string", TypeError],
+ [Symbol("foo"), TypeError],
+ [7n, TypeError],
+ [{}, TypeError],
+ [true, TypeError],
+ [7.1, RangeError],
+ ["7", TypeError],
+ ["7.5", TypeError],
+ [{valueOf() { return 7; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ dayOfWeek() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainDateTime(1981, 12, 15, 14, 15, 45, 987, 654, 321, calendar);
+ assert.throws(error, () => instance.dayOfWeek, `${typeof result} ${String(result)} not converted to positive integer`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/basic.js
new file mode 100644
index 0000000000..0cbbe195fe
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/basic.js
@@ -0,0 +1,15 @@
+// |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-get-temporal.plaindatetime.prototype.dayofyear
+description: Checking day of year for a "normal" case (non-undefined, non-boundary case, etc.)
+features: [Temporal]
+---*/
+
+const calendar = Temporal.Calendar.from("iso8601");
+const datetime = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789, calendar);
+assert.sameValue(datetime.dayOfYear, 323, "check day of year information");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/branding.js
new file mode 100644
index 0000000000..27970c2b80
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/branding.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-get-temporal.plaindatetime.prototype.dayofyear
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const dayOfYear = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "dayOfYear").get;
+
+assert.sameValue(typeof dayOfYear, "function");
+
+assert.throws(TypeError, () => dayOfYear.call(undefined), "undefined");
+assert.throws(TypeError, () => dayOfYear.call(null), "null");
+assert.throws(TypeError, () => dayOfYear.call(true), "true");
+assert.throws(TypeError, () => dayOfYear.call(""), "empty string");
+assert.throws(TypeError, () => dayOfYear.call(Symbol()), "symbol");
+assert.throws(TypeError, () => dayOfYear.call(1), "1");
+assert.throws(TypeError, () => dayOfYear.call({}), "plain object");
+assert.throws(TypeError, () => dayOfYear.call(Temporal.PlainDateTime), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => dayOfYear.call(Temporal.PlainDateTime.prototype), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..51a03fc9a3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plaindatetime.prototype.dayofyear
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dayOfYearOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dayOfYear");
+Object.defineProperty(Temporal.Calendar.prototype, "dayOfYear", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("dayOfYear should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, "iso8601");
+instance.dayOfYear;
+
+Object.defineProperty(Temporal.Calendar.prototype, "dayOfYear", dayOfYearOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/custom.js
new file mode 100644
index 0000000000..589aa29003
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/custom.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-get-temporal.plaindatetime.prototype.dayofyear
+description: Custom calendar tests for dayOfYear().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ dayOfYear(...args) {
+ ++calls;
+ assert.compareArray(args, [pdt], "dayOfYear arguments");
+ return 7;
+ }
+}
+
+const calendar = new CustomCalendar();
+const pdt = new Temporal.PlainDateTime(1830, 8, 25, 20, 0, 0, 0, 0, 0, calendar);
+const result = pdt.dayOfYear;
+assert.sameValue(result, 7, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/prop-desc.js
new file mode 100644
index 0000000000..b0db9e144f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/prop-desc.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-get-temporal.plaindatetime.prototype.dayofyear
+description: The "dayOfYear" property of Temporal.PlainDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "dayOfYear");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/validate-calendar-value.js
new file mode 100644
index 0000000000..86dc374962
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/validate-calendar-value.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-get-temporal.plaindatetime.prototype.dayofyear
+description: Validate result returned from calendar dayOfYear() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [null, TypeError],
+ [false, TypeError],
+ [Infinity, RangeError],
+ [-Infinity, RangeError],
+ [NaN, RangeError],
+ [-7, RangeError],
+ [-0.1, RangeError],
+ ["string", TypeError],
+ [Symbol("foo"), TypeError],
+ [7n, TypeError],
+ [{}, TypeError],
+ [true, TypeError],
+ [7.1, RangeError],
+ ["7", TypeError],
+ ["7.5", TypeError],
+ [{valueOf() { return 7; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ dayOfYear() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainDateTime(1981, 12, 15, 14, 15, 45, 987, 654, 321, calendar);
+ assert.throws(error, () => instance.dayOfYear, `${typeof result} ${String(result)} not converted to positive integer`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/basic.js
new file mode 100644
index 0000000000..bfa4e7afc4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/basic.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-get-temporal.plaindatetime.prototype.daysinmonth
+description: Checking days in month for a "normal" case (non-undefined, non-boundary case, etc.)
+features: [Temporal]
+---*/
+
+const tests = [
+ [new Temporal.PlainDateTime(1976, 2, 18, 15, 23, 30, 123, 456, 789), 29],
+ [new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789), 30],
+ [new Temporal.PlainDateTime(1976, 12, 18, 15, 23, 30, 123, 456, 789), 31],
+ [new Temporal.PlainDateTime(1977, 2, 18, 15, 23, 30, 123, 456, 789), 28],
+];
+for (const [plainDateTime, expected] of tests) {
+ assert.sameValue(plainDateTime.daysInMonth, expected, `${expected} days in the month of ${plainDateTime}`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/branding.js
new file mode 100644
index 0000000000..df0de408b3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/branding.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-get-temporal.plaindatetime.prototype.daysinmonth
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const daysInMonth = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "daysInMonth").get;
+
+assert.sameValue(typeof daysInMonth, "function");
+
+assert.throws(TypeError, () => daysInMonth.call(undefined), "undefined");
+assert.throws(TypeError, () => daysInMonth.call(null), "null");
+assert.throws(TypeError, () => daysInMonth.call(true), "true");
+assert.throws(TypeError, () => daysInMonth.call(""), "empty string");
+assert.throws(TypeError, () => daysInMonth.call(Symbol()), "symbol");
+assert.throws(TypeError, () => daysInMonth.call(1), "1");
+assert.throws(TypeError, () => daysInMonth.call({}), "plain object");
+assert.throws(TypeError, () => daysInMonth.call(Temporal.PlainDateTime), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => daysInMonth.call(Temporal.PlainDateTime.prototype), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..62e01f3615
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plaindatetime.prototype.daysinmonth
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const daysInMonthOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "daysInMonth");
+Object.defineProperty(Temporal.Calendar.prototype, "daysInMonth", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("daysInMonth should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, "iso8601");
+instance.daysInMonth;
+
+Object.defineProperty(Temporal.Calendar.prototype, "daysInMonth", daysInMonthOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/custom.js
new file mode 100644
index 0000000000..b5a9807cc2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/custom.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-get-temporal.plaindatetime.prototype.daysinmonth
+description: Custom calendar tests for daysInMonth().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ daysInMonth(...args) {
+ ++calls;
+ assert.compareArray(args, [pdt], "daysInMonth arguments");
+ return 7;
+ }
+}
+
+const calendar = new CustomCalendar();
+const pdt = new Temporal.PlainDateTime(1830, 8, 25, 20, 0, 0, 0, 0, 0, calendar);
+const result = pdt.daysInMonth;
+assert.sameValue(result, 7, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/prop-desc.js
new file mode 100644
index 0000000000..3beeb3ffac
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/prop-desc.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-get-temporal.plaindatetime.prototype.daysinmonth
+description: The "daysInMonth" property of Temporal.PlainDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "daysInMonth");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/validate-calendar-value.js
new file mode 100644
index 0000000000..2ddd63adb8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/validate-calendar-value.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-get-temporal.plaindatetime.prototype.daysinmonth
+description: Validate result returned from calendar daysInMonth() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [null, TypeError],
+ [false, TypeError],
+ [Infinity, RangeError],
+ [-Infinity, RangeError],
+ [NaN, RangeError],
+ [-7, RangeError],
+ [-0.1, RangeError],
+ ["string", TypeError],
+ [Symbol("foo"), TypeError],
+ [7n, TypeError],
+ [{}, TypeError],
+ [true, TypeError],
+ [7.1, RangeError],
+ ["7", TypeError],
+ ["7.5", TypeError],
+ [{valueOf() { return 7; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ daysInMonth() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainDateTime(1981, 12, 15, 14, 15, 45, 987, 654, 321, calendar);
+ assert.throws(error, () => instance.daysInMonth, `${typeof result} ${String(result)} not converted to positive integer`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/basic.js
new file mode 100644
index 0000000000..9fa4721d75
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/basic.js
@@ -0,0 +1,20 @@
+// |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-get-temporal.plaindatetime.prototype.daysinweek
+description: Checking days in week for a "normal" case (non-undefined, non-boundary case, etc.)
+features: [Temporal]
+---*/
+
+const tests = [
+ new Temporal.PlainDateTime(1976, 1, 1, 15, 23, 30, 123, 456, 789),
+ new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789),
+ new Temporal.PlainDateTime(1976, 12, 31, 15, 23, 30, 123, 456, 789),
+];
+for (const plainDateTime of tests) {
+ assert.sameValue(plainDateTime.daysInWeek, 7, `Seven days in the week of ${plainDateTime}`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/branding.js
new file mode 100644
index 0000000000..5983749761
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/branding.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-get-temporal.plaindatetime.prototype.daysinweek
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const daysInWeek = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "daysInWeek").get;
+
+assert.sameValue(typeof daysInWeek, "function");
+
+assert.throws(TypeError, () => daysInWeek.call(undefined), "undefined");
+assert.throws(TypeError, () => daysInWeek.call(null), "null");
+assert.throws(TypeError, () => daysInWeek.call(true), "true");
+assert.throws(TypeError, () => daysInWeek.call(""), "empty string");
+assert.throws(TypeError, () => daysInWeek.call(Symbol()), "symbol");
+assert.throws(TypeError, () => daysInWeek.call(1), "1");
+assert.throws(TypeError, () => daysInWeek.call({}), "plain object");
+assert.throws(TypeError, () => daysInWeek.call(Temporal.PlainDateTime), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => daysInWeek.call(Temporal.PlainDateTime.prototype), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..7dec28473b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plaindatetime.prototype.daysinweek
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const daysInWeekOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "daysInWeek");
+Object.defineProperty(Temporal.Calendar.prototype, "daysInWeek", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("daysInWeek should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, "iso8601");
+instance.daysInWeek;
+
+Object.defineProperty(Temporal.Calendar.prototype, "daysInWeek", daysInWeekOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/custom.js
new file mode 100644
index 0000000000..ce3af6a208
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/custom.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-get-temporal.plaindatetime.prototype.daysinweek
+description: Custom calendar tests for daysInWeek().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ daysInWeek(...args) {
+ ++calls;
+ assert.compareArray(args, [pdt], "daysInWeek arguments");
+ return 7;
+ }
+}
+
+const calendar = new CustomCalendar();
+const pdt = new Temporal.PlainDateTime(1830, 8, 25, 20, 0, 0, 0, 0, 0, calendar);
+const result = pdt.daysInWeek;
+assert.sameValue(result, 7, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/prop-desc.js
new file mode 100644
index 0000000000..cf29852b48
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/prop-desc.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-get-temporal.plaindatetime.prototype.daysinweek
+description: The "daysInWeek" property of Temporal.PlainDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "daysInWeek");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/validate-calendar-value.js
new file mode 100644
index 0000000000..db73c9ca69
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/validate-calendar-value.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-get-temporal.plaindatetime.prototype.daysinweek
+description: Validate result returned from calendar daysInWeek() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [null, TypeError],
+ [false, TypeError],
+ [Infinity, RangeError],
+ [-Infinity, RangeError],
+ [NaN, RangeError],
+ [-7, RangeError],
+ [-0.1, RangeError],
+ ["string", TypeError],
+ [Symbol("foo"), TypeError],
+ [7n, TypeError],
+ [{}, TypeError],
+ [true, TypeError],
+ [7.1, RangeError],
+ ["7", TypeError],
+ ["7.5", TypeError],
+ [{valueOf() { return 7; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ daysInWeek() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainDateTime(1981, 12, 15, 14, 15, 45, 987, 654, 321, calendar);
+ assert.throws(error, () => instance.daysInWeek, `${typeof result} ${String(result)} not converted to positive integer`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/basic.js
new file mode 100644
index 0000000000..de422d729b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/basic.js
@@ -0,0 +1,16 @@
+// |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-get-temporal.plaindatetime.prototype.daysinyear
+description: Checking days in year for a "normal" case (non-undefined, non-boundary case, etc.)
+features: [Temporal]
+---*/
+
+assert.sameValue((new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789)).daysInYear,
+ 366, "leap year");
+assert.sameValue((new Temporal.PlainDateTime(1977, 11, 18, 15, 23, 30, 123, 456, 789)).daysInYear,
+ 365, "non-leap year");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/branding.js
new file mode 100644
index 0000000000..3a102e63d9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/branding.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-get-temporal.plaindatetime.prototype.daysinyear
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const daysInYear = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "daysInYear").get;
+
+assert.sameValue(typeof daysInYear, "function");
+
+assert.throws(TypeError, () => daysInYear.call(undefined), "undefined");
+assert.throws(TypeError, () => daysInYear.call(null), "null");
+assert.throws(TypeError, () => daysInYear.call(true), "true");
+assert.throws(TypeError, () => daysInYear.call(""), "empty string");
+assert.throws(TypeError, () => daysInYear.call(Symbol()), "symbol");
+assert.throws(TypeError, () => daysInYear.call(1), "1");
+assert.throws(TypeError, () => daysInYear.call({}), "plain object");
+assert.throws(TypeError, () => daysInYear.call(Temporal.PlainDateTime), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => daysInYear.call(Temporal.PlainDateTime.prototype), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..70cbdd66f8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plaindatetime.prototype.daysinyear
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const daysInYearOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "daysInYear");
+Object.defineProperty(Temporal.Calendar.prototype, "daysInYear", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("daysInYear should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, "iso8601");
+instance.daysInYear;
+
+Object.defineProperty(Temporal.Calendar.prototype, "daysInYear", daysInYearOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/custom.js
new file mode 100644
index 0000000000..ec28f93a12
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/custom.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-get-temporal.plaindatetime.prototype.daysinyear
+description: Custom calendar tests for daysInYear().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ daysInYear(...args) {
+ ++calls;
+ assert.compareArray(args, [pdt], "daysInYear arguments");
+ return 7;
+ }
+}
+
+const calendar = new CustomCalendar();
+const pdt = new Temporal.PlainDateTime(1830, 8, 25, 20, 0, 0, 0, 0, 0, calendar);
+const result = pdt.daysInYear;
+assert.sameValue(result, 7, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/prop-desc.js
new file mode 100644
index 0000000000..54c466d351
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/prop-desc.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-get-temporal.plaindatetime.prototype.daysinyear
+description: The "daysInYear" property of Temporal.PlainDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "daysInYear");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/validate-calendar-value.js
new file mode 100644
index 0000000000..c6fd25287b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/validate-calendar-value.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-get-temporal.plaindatetime.prototype.daysinyear
+description: Validate result returned from calendar daysInYear() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [null, TypeError],
+ [false, TypeError],
+ [Infinity, RangeError],
+ [-Infinity, RangeError],
+ [NaN, RangeError],
+ [-7, RangeError],
+ [-0.1, RangeError],
+ ["string", TypeError],
+ [Symbol("foo"), TypeError],
+ [7n, TypeError],
+ [{}, TypeError],
+ [true, TypeError],
+ [7.1, RangeError],
+ ["7", TypeError],
+ ["7.5", TypeError],
+ [{valueOf() { return 7; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ daysInYear() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainDateTime(1981, 12, 15, 14, 15, 45, 987, 654, 321, calendar);
+ assert.throws(error, () => instance.daysInYear, `${typeof result} ${String(result)} not converted to positive integer`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..1afe3dc313
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-builtin-calendar-no-array-iteration.js
@@ -0,0 +1,24 @@
+// |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.plaindatetime.prototype.equals
+description: >
+ Calling the method with a property bag argument with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const arg = { year: 2000, month: 5, day: 2, hour: 21, minute: 43, second: 5, calendar: "iso8601" };
+instance.equals(arg);
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-number.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-number.js
new file mode 100644
index 0000000000..bf97cf78d8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/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.plaindatetime.prototype.equals
+description: A number cannot be used in place of a Temporal.PlainDateTime
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18);
+
+const numbers = [
+ 1,
+ 19761118,
+ -19761118,
+ 1234567890,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => instance.equals(arg),
+ `A number (${arg}) is not a valid ISO string for PlainDateTime`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-object-insufficient-data.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-object-insufficient-data.js
new file mode 100644
index 0000000000..cd663443f1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-object-insufficient-data.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.plaindatetime.prototype.equals
+description: If argument is an object, it must contain sufficient information
+features: [Temporal]
+---*/
+
+const dt = new Temporal.PlainDateTime(2019, 10, 29, 10, 46, 38, 271, 986, 102);
+
+assert.throws(
+ TypeError,
+ () => dt.equals({ year: 1976 }),
+ "object must contain required properties"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-plaindate.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-plaindate.js
new file mode 100644
index 0000000000..54eb0ee4aa
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-plaindate.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.plaindatetime.prototype.equals
+description: Fast path for converting Temporal.PlainDate to Temporal.PlainDateTime by reading internal slots
+info: |
+ sec-temporal.plaindatetime.prototype.equals step 3:
+ 3. Set _other_ to ? ToTemporalDateTime(_other_).
+ sec-temporal-totemporaldatetime step 2.b:
+ b. If _item_ has an [[InitializedTemporalDate]] internal slot, then
+ i. Return ? CreateTemporalDateTime(_item_.[[ISOYear]], _item_.[[ISOMonth]], _item_.[[ISODay]], 0, 0, 0, 0, 0, 0, _item_.[[Calendar]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalPlainDateTimeFastPath((date, calendar) => {
+ const datetime = new Temporal.PlainDateTime(2000, 5, 2, 0, 0, 0, 0, 0, 0, calendar);
+ assert(datetime.equals(date));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-propertybag-calendar-case-insensitive.js
new file mode 100644
index 0000000000..d25cf80551
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-propertybag-calendar-case-insensitive.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.plaindatetime.prototype.equals
+description: The calendar name is case-insensitive
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18);
+
+const calendar = "IsO8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.equals(arg);
+assert.sameValue(result, true, "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-propertybag-calendar-leap-second.js
new file mode 100644
index 0000000000..7ff0a1b655
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-propertybag-calendar-leap-second.js
@@ -0,0 +1,23 @@
+// |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.plaindatetime.prototype.equals
+description: Leap second is a valid ISO string for a calendar in a property bag
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18);
+
+const calendar = "2016-12-31T23:59:60";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.equals(arg);
+assert.sameValue(
+ result,
+ true,
+ "leap second is a valid ISO string for calendar"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-propertybag-calendar-number.js
new file mode 100644
index 0000000000..e532ba1fb1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-propertybag-calendar-number.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.plaindatetime.prototype.equals
+description: A number as calendar in a property bag is not accepted
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18);
+
+const numbers = [
+ 1,
+ 19970327,
+ -19970327,
+ 1234567890,
+];
+
+for (const calendar of numbers) {
+ const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+ assert.throws(
+ TypeError,
+ () => instance.equals(arg),
+ "Numbers cannot be used as a calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-propertybag-calendar-string.js
new file mode 100644
index 0000000000..9715239429
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-propertybag-calendar-string.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.plaindatetime.prototype.equals
+description: A calendar ID is valid input for Calendar
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18);
+
+const calendar = "iso8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.equals(arg);
+assert.sameValue(result, true, `Calendar created from string "${calendar}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-propertybag-calendar-wrong-type.js
new file mode 100644
index 0000000000..ba0f49ec64
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-propertybag-calendar-wrong-type.js
@@ -0,0 +1,46 @@
+// |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.plaindatetime.prototype.equals
+description: >
+ Appropriate error thrown when a calendar property from a property bag cannot
+ be converted to a calendar object or string
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [calendar, description] of primitiveTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ typeof calendar === 'string' ? RangeError : TypeError,
+ () => instance.equals(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+ [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields()
+];
+
+for (const [calendar, description] of typeErrorTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(TypeError, () => instance.equals(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/PlainDateTime/prototype/equals/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-propertybag-calendar-year-zero.js
new file mode 100644
index 0000000000..967839b97e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-propertybag-calendar-year-zero.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.plaindatetime.prototype.equals
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T17:45",
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+01:00",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..067490424a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-calendar-annotation.js
@@ -0,0 +1,31 @@
+// |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.plaindatetime.prototype.equals
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["1976-11-18T15:23[u-ca=iso8601]", "without time zone"],
+ ["1976-11-18T15:23[UTC][u-ca=iso8601]", "with time zone"],
+ ["1976-11-18T15:23[!u-ca=iso8601]", "with ! and no time zone"],
+ ["1976-11-18T15:23[UTC][!u-ca=iso8601]", "with ! and time zone"],
+ ["1976-11-18T15:23[u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 15, 23);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.equals(arg);
+
+ assert.sameValue(
+ result,
+ true,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..f47dbb00ff
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-critical-unknown-annotation.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.plaindatetime.prototype.equals
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "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.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..b83a73f0f0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-date-with-utc-offset.js
@@ -0,0 +1,48 @@
+// |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.plaindatetime.prototype.equals
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 15, 23);
+
+const validStrings = [
+ "1976-11-18T15:23+00:00",
+ "1976-11-18T15:23+00:00[UTC]",
+ "1976-11-18T15:23+00:00[!UTC]",
+ "1976-11-18T15:23-02:30[America/St_Johns]",
+];
+
+for (const arg of validStrings) {
+ const result = instance.equals(arg);
+
+ assert.sameValue(
+ result,
+ true,
+ `"${arg}" is a valid UTC offset with time for PlainDateTime`
+ );
+}
+
+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.equals(arg),
+ `"${arg}" UTC offset without time is not valid for PlainDateTime`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..cd069f5857
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/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.plaindatetime.prototype.equals
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[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.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..42a7ade474
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-multiple-time-zone.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.plaindatetime.prototype.equals
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "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.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-time-separators.js
new file mode 100644
index 0000000000..bef88b1f4f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-time-separators.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.plaindatetime.prototype.equals
+description: Time separator in string argument can vary
+features: [Temporal]
+---*/
+
+const tests = [
+ ["1976-11-18T15:23", "uppercase T"],
+ ["1976-11-18t15:23", "lowercase T"],
+ ["1976-11-18 15:23", "space between date and time"],
+];
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 15, 23);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.equals(arg);
+
+ assert.sameValue(
+ result,
+ true,
+ `variant time separators (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..f1c038171e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-time-zone-annotation.js
@@ -0,0 +1,34 @@
+// |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.plaindatetime.prototype.equals
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["1976-11-18T15:23[Asia/Kolkata]", "named, with no offset"],
+ ["1976-11-18T15:23[!Europe/Vienna]", "named, with ! and no offset"],
+ ["1976-11-18T15:23[+00:00]", "numeric, with no offset"],
+ ["1976-11-18T15:23[!-02:30]", "numeric, with ! and no offset"],
+ ["1976-11-18T15:23+00:00[UTC]", "named, with offset"],
+ ["1976-11-18T15:23+00:00[!Africa/Abidjan]", "named, with offset and !"],
+ ["1976-11-18T15:23+00:00[+01:00]", "numeric, with offset"],
+ ["1976-11-18T15:23+00:00[!-08:00]", "numeric, with offset and !"],
+];
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 15, 23);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.equals(arg);
+
+ assert.sameValue(
+ result,
+ true,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..1a128c2671
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-unknown-annotation.js
@@ -0,0 +1,31 @@
+// |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.plaindatetime.prototype.equals
+description: Various forms of unknown annotation
+features: [Temporal]
+---*/
+
+const tests = [
+ ["1976-11-18T15:23[foo=bar]", "alone"],
+ ["1976-11-18T15:23[UTC][foo=bar]", "with time zone"],
+ ["1976-11-18T15:23[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["1976-11-18T15:23[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["1976-11-18T15:23[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 15, 23);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.equals(arg);
+
+ assert.sameValue(
+ result,
+ true,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..e1efc0c0ad
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-with-utc-designator.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.plaindatetime.prototype.equals
+description: RangeError thrown if a string with UTC designator is used as a PlainDateTime
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "2019-10-01T09:00:00Z",
+ "2019-10-01T09:00:00Z[UTC]",
+];
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ "String with UTC designator should not be valid as a PlainDateTime"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-wrong-type.js
new file mode 100644
index 0000000000..25a644b4ec
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/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.plaindatetime.prototype.equals
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainDateTime
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 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.equals(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.PlainDateTime, "Temporal.PlainDateTime, object"],
+ [Temporal.PlainDateTime.prototype, "Temporal.PlainDateTime.prototype, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.equals(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/PlainDateTime/prototype/equals/argument-zoneddatetime-balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-zoneddatetime-balance-negative-time-units.js
new file mode 100644
index 0000000000..4e26be85b3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-zoneddatetime-balance-negative-time-units.js
@@ -0,0 +1,44 @@
+// |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.plaindatetime.prototype.equals
+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-totemporaldatetime step 3.b:
+ b. If _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ ...
+ ii. 1. Return ? BuiltinTimeZoneGetPlainDateTimeFor(_item_.[[TimeZone]], _instant_, _item_.[[Calendar]]).
+ sec-temporal.plaindatetime.prototype.until step 3:
+ 3. Set _other_ ? ToTemporalDateTime(_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);
+
+assert(new Temporal.PlainDateTime(1970, 1, 1, 1, 1, 1, 1, 0, 999).equals(datetime));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-zoneddatetime-negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-zoneddatetime-negative-epochnanoseconds.js
new file mode 100644
index 0000000000..73f5790fea
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-zoneddatetime-negative-epochnanoseconds.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.plaindatetime.prototype.equals
+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]
+---*/
+
+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.PlainDateTime(1969, 7, 24, 16, 50, 35, 0, 0, 1);
+const result = instance.equals(datetime);
+assert.sameValue(result, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..e02afefd3b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/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.plaindatetime.prototype.equals
+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 plain = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ const zoned = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => plain.equals(zoned));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..b2fe1b47df
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/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.plaindatetime.prototype.equals
+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 plain = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ const zoned = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => plain.equals(zoned),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..1570b82c02
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/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.plaindatetime.prototype.equals
+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 plain = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ const zoned = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => plain.equals(zoned));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..06940015b9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/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.plaindatetime.prototype.equals
+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 plain = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ const zoned = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => plain.equals(zoned));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/basic.js
new file mode 100644
index 0000000000..6788f4f879
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/basic.js
@@ -0,0 +1,23 @@
+// |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.plaindatetime.prototype.equals
+description: Checking a typical case (everything defined, no NaNs, nothing throws, etc.)
+features: [Temporal]
+---*/
+
+const dt1 = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789);
+const dt2 = new Temporal.PlainDateTime(2019, 10, 29, 10, 46, 38, 271, 986, 102);
+const dt3 = new Temporal.PlainDateTime(2019, 10, 29, 10, 46, 38, 271, 986, 102);
+const dt4 = new Temporal.PlainDateTime(2019, 10, 29, 15, 23, 30, 123, 456, 789);
+const dt5 = new Temporal.PlainDateTime(1976, 11, 18, 10, 46, 38, 271, 986, 102);
+
+assert.sameValue(dt1.equals(dt1), true, "equal");
+assert.sameValue(dt1.equals(dt2), false, "unequal");
+assert.sameValue(dt2.equals(dt3), true, "equal with different objects");
+assert.sameValue(dt2.equals(dt4), false, "same date, different time");
+assert.sameValue(dt2.equals(dt5), false, "same time, different date");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/branding.js
new file mode 100644
index 0000000000..701540f5a2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/branding.js
@@ -0,0 +1,27 @@
+// |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.plaindatetime.prototype.equals
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const equals = Temporal.PlainDateTime.prototype.equals;
+
+assert.sameValue(typeof equals, "function");
+
+const args = [new Temporal.PlainDateTime(2022, 6, 22)];
+
+assert.throws(TypeError, () => equals.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => equals.apply(null, args), "null");
+assert.throws(TypeError, () => equals.apply(true, args), "true");
+assert.throws(TypeError, () => equals.apply("", args), "empty string");
+assert.throws(TypeError, () => equals.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => equals.apply(1, args), "1");
+assert.throws(TypeError, () => equals.apply({}, args), "plain object");
+assert.throws(TypeError, () => equals.apply(Temporal.PlainDateTime, args), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => equals.apply(Temporal.PlainDateTime.prototype, args), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..1c3f13d96d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plaindatetime.prototype.equals
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const idOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "id");
+Object.defineProperty(Temporal.Calendar.prototype, "id", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("id should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, "iso8601");
+instance.equals(new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321));
+
+Object.defineProperty(Temporal.Calendar.prototype, "id", idOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/builtin.js
new file mode 100644
index 0000000000..f443c49906
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/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.plaindatetime.prototype.equals
+description: >
+ Tests that Temporal.PlainDateTime.prototype.equals
+ 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.PlainDateTime.prototype.equals),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.prototype.equals),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.prototype.equals),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.prototype.equals.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/calendar-checked.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/calendar-checked.js
new file mode 100644
index 0000000000..28e8d34147
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/calendar-checked.js
@@ -0,0 +1,112 @@
+// |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.plaindatetime.prototype.equals
+description: Calendar is taken into account if the ISO data is equal
+includes: [compareArray.js,temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+
+function makeCalendar(id, objectName) {
+ const calendar = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+ };
+ TemporalHelpers.observeProperty(actual, calendar, "id", id, objectName);
+ return calendar;
+}
+
+const calendar1 = makeCalendar("A", "calendar1");
+const calendar2 = makeCalendar("A", "calendar2");
+const calendar3 = makeCalendar("B", "calendar3");
+const dt1 = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789, calendar1);
+const dt1b = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789, calendar1);
+const dt2 = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789, calendar2);
+const dt3 = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789, calendar3);
+actual.splice(0); // disregard the HasProperty checks done in the constructor
+
+assert.sameValue(dt1.equals(dt1b), true, "same calendar object");
+assert.compareArray(actual, []);
+
+assert.sameValue(dt1.equals(dt2), true, "same calendar string");
+assert.compareArray(actual, ["get calendar1.id", "get calendar2.id"]);
+
+actual.splice(0); // empty it for the next check
+assert.sameValue(dt1.equals(dt3), false, "different calendar string");
+assert.compareArray(actual, ["get calendar1.id", "get calendar3.id"]);
+
+const calendar4 = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ get id() { TemporalHelpers.assertUnreachable('should not get calendar4.id'); },
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+const calendar5 = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ get id() { TemporalHelpers.assertUnreachable('should not get calendar5.id'); },
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+const dt4 = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789, calendar4);
+const dt5 = new Temporal.PlainDateTime(2019, 10, 29, 10, 46, 38, 271, 986, 102, calendar4);
+const dt6 = new Temporal.PlainDateTime(2019, 10, 29, 10, 46, 38, 271, 986, 102, calendar5);
+assert.sameValue(dt4.equals(dt5), false, "not equal same calendar");
+assert.sameValue(dt4.equals(dt6), false, "not equal different calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/calendar-datefromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..a6af1e4f24
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/calendar-datefromfields-called-with-null-prototype-fields.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.plaindatetime.prototype.equals
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const arg = { year: 2000, month: 5, day: 2, calendar };
+instance.equals(arg);
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/calendar-fields-iterable.js
new file mode 100644
index 0000000000..c8c128aed3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/calendar-fields-iterable.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.plaindatetime.prototype.equals
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plaindatetime.prototype.equals step 3:
+ 3. Set _other_ to ? ToTemporalDateTime(_other_).
+ sec-temporal-totemporaldatetime step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"hour"*, *"microsecond"*, *"millisecond"*, *"minute"*, *"month"*, *"monthCode"*, *"nanosecond"*, *"second"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar1);
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+datetime.equals({ year: 2005, month: 6, day: 2, calendar: calendar2 });
+
+assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/calendar-temporal-object.js
new file mode 100644
index 0000000000..3b42636d2d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/calendar-temporal-object.js
@@ -0,0 +1,29 @@
+// |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.plaindatetime.prototype.equals
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plaindatetime.prototype.equals step 3:
+ 3. Set _other_ to ? ToTemporalDateTime(_other_).
+ sec-temporal-totemporaldatetime step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, temporalObject);
+ datetime.equals({ year: 2005, month: 6, day: 2, calendar: temporalObject });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/cast.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/cast.js
new file mode 100644
index 0000000000..5ff5d4baf1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/cast.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.plaindatetime.prototype.equals
+description: Argument may be cast
+features: [Temporal]
+---*/
+
+const dt1 = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789);
+const dt2 = new Temporal.PlainDateTime(2019, 10, 29, 10, 46, 38, 271, 986, 102);
+
+assert.sameValue(
+ dt1.equals({
+ year: 1976,
+ month: 11,
+ day: 18,
+ hour: 15,
+ minute: 23,
+ second: 30,
+ millisecond: 123,
+ microsecond: 456,
+ nanosecond: 789
+ }),
+ true,
+ "casts argument (plain object, positive)"
+);
+
+
+assert.sameValue(
+ dt2.equals({ year: 1976, month: 11, day: 18, hour: 15 }),
+ false,
+ "casts argument (plain object, negative)"
+);
+
+assert.sameValue(
+ dt1.equals("1976-11-18T15:23:30.123456789"),
+ true,
+ "casts argument (string, positive)"
+);
+
+assert.sameValue(
+ dt2.equals("1976-11-18T15:23:30.123456789"),
+ false,
+ "casts argument (string, negative)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..3a444f6002
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/constructor-in-calendar-fields.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.plaindatetime.prototype.equals
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+assert.throws(RangeError, () => instance.equals(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/duplicate-calendar-fields.js
new file mode 100644
index 0000000000..f8900be8ce
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/duplicate-calendar-fields.js
@@ -0,0 +1,19 @@
+// |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.plaindatetime.prototype.equals
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['day'], ['hour'], ['microsecond'], ['millisecond'], ['minute'], ['month'], ['monthCode'], ['nanosecond'], ['second'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar };
+ const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+ assert.throws(RangeError, () => instance.equals(arg));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..ba5cde17c8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/infinity-throws-rangeerror.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.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plaindatetime.prototype.equals
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 15);
+const base = { year: 2000, month: 5, day: 2, hour: 15, minute: 30, second: 45, millisecond: 987, microsecond: 654, nanosecond: 321 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.equals({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.equals({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/leap-second.js
new file mode 100644
index 0000000000..523ab77e2f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/leap-second.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.plaindatetime.prototype.equals
+description: Leap second is a valid ISO string for PlainDateTime
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2016, 12, 31, 23, 59, 59);
+
+let arg = "2016-12-31T23:59:60";
+const result1 = instance.equals(arg);
+assert.sameValue(
+ result1,
+ true,
+ "leap second is a valid ISO string for PlainDateTime"
+);
+
+arg = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+const result2 = instance.equals(arg);
+assert.sameValue(
+ result2,
+ true,
+ "second: 60 is ignored in property bag for PlainDateTime"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/length.js
new file mode 100644
index 0000000000..496ec4fe63
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/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.plaindatetime.prototype.equals
+description: Temporal.PlainDateTime.prototype.equals.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.PlainDateTime.prototype.equals, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/name.js
new file mode 100644
index 0000000000..4903a11e40
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/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.plaindatetime.prototype.equals
+description: Temporal.PlainDateTime.prototype.equals.name is "equals".
+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.PlainDateTime.prototype.equals, "name", {
+ value: "equals",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/not-a-constructor.js
new file mode 100644
index 0000000000..ab15972cee
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/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.plaindatetime.prototype.equals
+description: >
+ Temporal.PlainDateTime.prototype.equals 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.PlainDateTime.prototype.equals();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.prototype.equals), false,
+ "isConstructor(Temporal.PlainDateTime.prototype.equals)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/prop-desc.js
new file mode 100644
index 0000000000..f7be4f549c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/prop-desc.js
@@ -0,0 +1,24 @@
+// |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.plaindatetime.prototype.equals
+description: The "equals" property of Temporal.PlainDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.prototype.equals,
+ "function",
+ "`typeof PlainDateTime.prototype.equals` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime.prototype, "equals", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/proto-in-calendar-fields.js
new file mode 100644
index 0000000000..fd3064caed
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/proto-in-calendar-fields.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.plaindatetime.prototype.equals
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+assert.throws(RangeError, () => instance.equals(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/read-time-fields-before-datefromfields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/read-time-fields-before-datefromfields.js
new file mode 100644
index 0000000000..cd9066eb8f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/read-time-fields-before-datefromfields.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.plaindatetime.prototype.equals
+description: The time fields are read from the object before being passed to dateFromFields().
+info: |
+ sec-temporal.plaindatetime.prototype.equals step 3:
+ 3. Set _other_ to ? ToTemporalDateTime(_other_).
+ sec-temporal-totemporaldatetime step 2.e:
+ e. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+ sec-temporal-interprettemporaldatetimefields steps 1–2:
+ 1. Let _timeResult_ be ? ToTemporalTimeRecord(_fields_).
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarMakeInfinityTime();
+const datetime = new Temporal.PlainDateTime(2021, 3, 31, 12, 34, 56, 987, 654, 321, calendar);
+const result = datetime.equals({ year: 2021, month: 3, day: 31, hour: 12, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321, calendar });
+
+assert(result, "time fields are not modified");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/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/PlainDateTime/prototype/equals/year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/year-zero.js
new file mode 100644
index 0000000000..d8fe8e7b82
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/year-zero.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.plaindatetime.prototype.equals
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-12-07",
+ "-000000-12-07T03:24:30",
+ "-000000-12-07T03:24:30+01:00",
+ "-000000-12-07T03:24:30+00:00[UTC]",
+];
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getCalendar/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getCalendar/branding.js
new file mode 100644
index 0000000000..b0a9322aac
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getCalendar/branding.js
@@ -0,0 +1,25 @@
+// |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.plaindatetime.prototype.getcalendar
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const getCalendar = Temporal.PlainDateTime.prototype.getCalendar;
+
+assert.sameValue(typeof getCalendar, "function");
+
+assert.throws(TypeError, () => getCalendar.call(undefined), "undefined");
+assert.throws(TypeError, () => getCalendar.call(null), "null");
+assert.throws(TypeError, () => getCalendar.call(true), "true");
+assert.throws(TypeError, () => getCalendar.call(""), "empty string");
+assert.throws(TypeError, () => getCalendar.call(Symbol()), "symbol");
+assert.throws(TypeError, () => getCalendar.call(1), "1");
+assert.throws(TypeError, () => getCalendar.call({}), "plain object");
+assert.throws(TypeError, () => getCalendar.call(Temporal.PlainDateTime), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => getCalendar.call(Temporal.PlainDateTime.prototype), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getCalendar/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getCalendar/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getCalendar/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getCalendar/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getCalendar/builtin.js
new file mode 100644
index 0000000000..593d4112f1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getCalendar/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.plaindatetime.prototype.getcalendar
+description: >
+ Tests that Temporal.PlainDateTime.prototype.getCalendar
+ 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.PlainDateTime.prototype.getCalendar),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.prototype.getCalendar),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.prototype.getCalendar),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.prototype.getCalendar.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getCalendar/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getCalendar/length.js
new file mode 100644
index 0000000000..bf0fd6d852
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getCalendar/length.js
@@ -0,0 +1,28 @@
+// |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.plaindatetime.prototype.getcalendar
+description: Temporal.PlainDateTime.prototype.getCalendar.length is 0
+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.PlainDateTime.prototype.getCalendar, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getCalendar/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getCalendar/name.js
new file mode 100644
index 0000000000..c9ec696b05
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getCalendar/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.plaindatetime.prototype.getcalendar
+description: Temporal.PlainDateTime.prototype.getCalendar.name is "getCalendar".
+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.PlainDateTime.prototype.getCalendar, "name", {
+ value: "getCalendar",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getCalendar/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getCalendar/not-a-constructor.js
new file mode 100644
index 0000000000..8e35262749
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getCalendar/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.plaindatetime.prototype.getcalendar
+description: >
+ Temporal.PlainDateTime.prototype.getCalendar 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.PlainDateTime.prototype.getCalendar();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.prototype.getCalendar), false,
+ "isConstructor(Temporal.PlainDateTime.prototype.getCalendar)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getCalendar/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getCalendar/prop-desc.js
new file mode 100644
index 0000000000..cdfecc5979
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getCalendar/prop-desc.js
@@ -0,0 +1,24 @@
+// |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.plaindatetime.prototype.getcalendar
+description: The "getCalendar" property of Temporal.PlainDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.prototype.getCalendar,
+ "function",
+ "`typeof PlainDateTime.prototype.getCalendar` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime.prototype, "getCalendar", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getCalendar/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getCalendar/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getCalendar/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/PlainDateTime/prototype/getISOFields/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/branding.js
new file mode 100644
index 0000000000..a1c1a6d9e3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/branding.js
@@ -0,0 +1,25 @@
+// |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.plaindatetime.prototype.getisofields
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const getISOFields = Temporal.PlainDateTime.prototype.getISOFields;
+
+assert.sameValue(typeof getISOFields, "function");
+
+assert.throws(TypeError, () => getISOFields.call(undefined), "undefined");
+assert.throws(TypeError, () => getISOFields.call(null), "null");
+assert.throws(TypeError, () => getISOFields.call(true), "true");
+assert.throws(TypeError, () => getISOFields.call(""), "empty string");
+assert.throws(TypeError, () => getISOFields.call(Symbol()), "symbol");
+assert.throws(TypeError, () => getISOFields.call(1), "1");
+assert.throws(TypeError, () => getISOFields.call({}), "plain object");
+assert.throws(TypeError, () => getISOFields.call(Temporal.PlainDateTime), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => getISOFields.call(Temporal.PlainDateTime.prototype), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/builtin.js
new file mode 100644
index 0000000000..c6dc7cb134
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/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.plaindatetime.prototype.getisofields
+description: >
+ Tests that Temporal.PlainDateTime.prototype.getISOFields
+ 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.PlainDateTime.prototype.getISOFields),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.prototype.getISOFields),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.prototype.getISOFields),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.prototype.getISOFields.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/custom.js
new file mode 100644
index 0000000000..dda36e26f9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/custom.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.plaindatetime.prototype.getisofields
+description: getISOFields does not call into user code.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarThrowEverything();
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+const result = instance.getISOFields();
+
+assert.sameValue(result.isoYear, 2000, "isoYear result");
+assert.sameValue(result.isoMonth, 5, "isoMonth result");
+assert.sameValue(result.isoDay, 2, "isoDay result");
+assert.sameValue(result.isoHour, 12, "isoHour result");
+assert.sameValue(result.isoMinute, 34, "isoMinute result");
+assert.sameValue(result.isoSecond, 56, "isoSecond result");
+assert.sameValue(result.isoMillisecond, 987, "isoMillisecond result");
+assert.sameValue(result.isoMicrosecond, 654, "isoMicrosecond result");
+assert.sameValue(result.isoNanosecond, 321, "isoNanosecond result");
+assert.sameValue(result.calendar, calendar, "calendar result");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/field-names.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/field-names.js
new file mode 100644
index 0000000000..7e0af96024
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/field-names.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.plaindatetime.prototype.getisofields
+description: Correct field names on the object returned from getISOFields
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+const result = datetime.getISOFields();
+assert.sameValue(result.isoYear, 2000, "isoYear result");
+assert.sameValue(result.isoMonth, 5, "isoMonth result");
+assert.sameValue(result.isoDay, 2, "isoDay result");
+assert.sameValue(result.isoHour, 12, "isoHour result");
+assert.sameValue(result.isoMinute, 34, "isoMinute result");
+assert.sameValue(result.isoSecond, 56, "isoSecond result");
+assert.sameValue(result.isoMillisecond, 987, "isoMillisecond result");
+assert.sameValue(result.isoMicrosecond, 654, "isoMicrosecond result");
+assert.sameValue(result.isoNanosecond, 321, "isoNanosecond result");
+assert.sameValue(result.calendar, "iso8601", "calendar result");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/field-prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/field-prop-desc.js
new file mode 100644
index 0000000000..41e6b2d1e6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/field-prop-desc.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.plaindatetime.prototype.getisofields
+description: Properties on the returned object have the correct descriptor
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "calendar",
+ "isoDay",
+ "isoHour",
+ "isoMicrosecond",
+ "isoMillisecond",
+ "isoMinute",
+ "isoMonth",
+ "isoNanosecond",
+ "isoSecond",
+ "isoYear",
+];
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const result = datetime.getISOFields();
+
+for (const property of expected) {
+ verifyProperty(result, property, {
+ writable: true,
+ enumerable: true,
+ configurable: true,
+ });
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/field-traversal-order.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/field-traversal-order.js
new file mode 100644
index 0000000000..6d1f676264
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/field-traversal-order.js
@@ -0,0 +1,30 @@
+// |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.plaindatetime.prototype.getisofields
+description: Properties added in correct order to object returned from getISOFields
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "calendar",
+ "isoDay",
+ "isoHour",
+ "isoMicrosecond",
+ "isoMillisecond",
+ "isoMinute",
+ "isoMonth",
+ "isoNanosecond",
+ "isoSecond",
+ "isoYear",
+];
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const result = datetime.getISOFields();
+
+assert.compareArray(Object.keys(result), expected);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/length.js
new file mode 100644
index 0000000000..3580c33e00
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/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.plaindatetime.prototype.getisofields
+description: Temporal.PlainDateTime.prototype.getISOFields.length is 0
+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.PlainDateTime.prototype.getISOFields, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/name.js
new file mode 100644
index 0000000000..2f6a0b0691
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/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.plaindatetime.prototype.getisofields
+description: Temporal.PlainDateTime.prototype.getISOFields.name is "getISOFields".
+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.PlainDateTime.prototype.getISOFields, "name", {
+ value: "getISOFields",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/not-a-constructor.js
new file mode 100644
index 0000000000..745bfb8a9a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/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.plaindatetime.prototype.getisofields
+description: >
+ Temporal.PlainDateTime.prototype.getISOFields 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.PlainDateTime.prototype.getISOFields();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.prototype.getISOFields), false,
+ "isConstructor(Temporal.PlainDateTime.prototype.getISOFields)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/prop-desc.js
new file mode 100644
index 0000000000..8addd7338d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/prop-desc.js
@@ -0,0 +1,24 @@
+// |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.plaindatetime.prototype.getisofields
+description: The "getISOFields" property of Temporal.PlainDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.prototype.getISOFields,
+ "function",
+ "`typeof PlainDateTime.prototype.getISOFields` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime.prototype, "getISOFields", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/prototype.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/prototype.js
new file mode 100644
index 0000000000..9b2fe2de33
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/prototype.js
@@ -0,0 +1,15 @@
+// |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.plaindatetime.prototype.getisofields
+description: Correct prototype on the object returned from getISOFields
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const result = instance.getISOFields();
+assert.sameValue(Object.getPrototypeOf(result), Object.prototype, "prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/getISOFields/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/PlainDateTime/prototype/hour/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/hour/branding.js
new file mode 100644
index 0000000000..7f25864f22
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/hour/branding.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-get-temporal.plaindatetime.prototype.hour
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const hour = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "hour").get;
+
+assert.sameValue(typeof hour, "function");
+
+assert.throws(TypeError, () => hour.call(undefined), "undefined");
+assert.throws(TypeError, () => hour.call(null), "null");
+assert.throws(TypeError, () => hour.call(true), "true");
+assert.throws(TypeError, () => hour.call(""), "empty string");
+assert.throws(TypeError, () => hour.call(Symbol()), "symbol");
+assert.throws(TypeError, () => hour.call(1), "1");
+assert.throws(TypeError, () => hour.call({}), "plain object");
+assert.throws(TypeError, () => hour.call(Temporal.PlainDateTime), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => hour.call(Temporal.PlainDateTime.prototype), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/hour/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/hour/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/hour/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/hour/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/hour/prop-desc.js
new file mode 100644
index 0000000000..be1f170bea
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/hour/prop-desc.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-get-temporal.plaindatetime.prototype.hour
+description: The "hour" property of Temporal.PlainDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "hour");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/hour/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/hour/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/hour/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/basic.js
new file mode 100644
index 0000000000..a6b88825c3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/basic.js
@@ -0,0 +1,16 @@
+// |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-get-temporal.plaindatetime.prototype.inleapyear
+description: Basic test for inLeapYear
+features: [Temporal]
+---*/
+
+assert.sameValue((new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789)).inLeapYear,
+ true, "leap year");
+assert.sameValue((new Temporal.PlainDateTime(1977, 11, 18, 15, 23, 30, 123, 456, 789)).inLeapYear,
+ false, "non-leap year");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/branding.js
new file mode 100644
index 0000000000..476c99fa05
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/branding.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-get-temporal.plaindatetime.prototype.inleapyear
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const inLeapYear = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "inLeapYear").get;
+
+assert.sameValue(typeof inLeapYear, "function");
+
+assert.throws(TypeError, () => inLeapYear.call(undefined), "undefined");
+assert.throws(TypeError, () => inLeapYear.call(null), "null");
+assert.throws(TypeError, () => inLeapYear.call(true), "true");
+assert.throws(TypeError, () => inLeapYear.call(""), "empty string");
+assert.throws(TypeError, () => inLeapYear.call(Symbol()), "symbol");
+assert.throws(TypeError, () => inLeapYear.call(1), "1");
+assert.throws(TypeError, () => inLeapYear.call({}), "plain object");
+assert.throws(TypeError, () => inLeapYear.call(Temporal.PlainDateTime), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => inLeapYear.call(Temporal.PlainDateTime.prototype), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..122539fc57
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plaindatetime.prototype.inleapyear
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const inLeapYearOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "inLeapYear");
+Object.defineProperty(Temporal.Calendar.prototype, "inLeapYear", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("inLeapYear should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, "iso8601");
+instance.inLeapYear;
+
+Object.defineProperty(Temporal.Calendar.prototype, "inLeapYear", inLeapYearOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/custom.js
new file mode 100644
index 0000000000..25daeb1c2b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/custom.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-get-temporal.plaindatetime.prototype.inleapyear
+description: Custom calendar tests for inLeapYear().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ inLeapYear(...args) {
+ ++calls;
+ assert.compareArray(args, [pdt], "inLeapYear arguments");
+ return true;
+ }
+}
+
+const calendar = new CustomCalendar();
+const pdt = new Temporal.PlainDateTime(1830, 8, 25, 20, 0, 0, 0, 0, 0, calendar);
+const result = pdt.inLeapYear;
+assert.sameValue(result, true, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/prop-desc.js
new file mode 100644
index 0000000000..30bdbbeaed
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/prop-desc.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-get-temporal.plaindatetime.prototype.inleapyear
+description: The "inLeapYear" property of Temporal.PlainDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "inLeapYear");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/validate-calendar-value.js
new file mode 100644
index 0000000000..bbcde3216a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/validate-calendar-value.js
@@ -0,0 +1,56 @@
+// |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-get-temporal.plaindatetime.prototype.inleapyear
+description: Validate result returned from calendar inLeapYear() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [null, TypeError],
+ [0, TypeError],
+ [-0, TypeError],
+ [42, TypeError],
+ [7.1, TypeError],
+ [NaN, TypeError],
+ [Infinity, TypeError],
+ [-Infinity, TypeError],
+ ["", TypeError],
+ ["a string", TypeError],
+ ["0", TypeError],
+ [Symbol("foo"), TypeError],
+ [0n, TypeError],
+ [42n, TypeError],
+ [{}, TypeError],
+ [{valueOf() { return false; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ inLeapYear() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainDateTime(1981, 12, 15, 14, 15, 45, 987, 654, 321, calendar);
+ assert.throws(error, () => instance.inLeapYear, `${typeof result} ${String(result)} not converted to boolean`);
+});
+
+const preservedResults = [
+ true,
+ false,
+];
+
+preservedResults.forEach(result => {
+ const calendar = new class extends Temporal.Calendar {
+ inLeapYear() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainDateTime(1981, 12, 15, 14, 15, 45, 987, 654, 321, calendar);
+ assert.sameValue(instance.inLeapYear, result, `${typeof result} ${String(result)} preserved`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/microsecond/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/microsecond/branding.js
new file mode 100644
index 0000000000..ae0dfd4306
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/microsecond/branding.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-get-temporal.plaindatetime.prototype.microsecond
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const microsecond = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "microsecond").get;
+
+assert.sameValue(typeof microsecond, "function");
+
+assert.throws(TypeError, () => microsecond.call(undefined), "undefined");
+assert.throws(TypeError, () => microsecond.call(null), "null");
+assert.throws(TypeError, () => microsecond.call(true), "true");
+assert.throws(TypeError, () => microsecond.call(""), "empty string");
+assert.throws(TypeError, () => microsecond.call(Symbol()), "symbol");
+assert.throws(TypeError, () => microsecond.call(1), "1");
+assert.throws(TypeError, () => microsecond.call({}), "plain object");
+assert.throws(TypeError, () => microsecond.call(Temporal.PlainDateTime), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => microsecond.call(Temporal.PlainDateTime.prototype), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/microsecond/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/microsecond/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/microsecond/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/microsecond/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/microsecond/prop-desc.js
new file mode 100644
index 0000000000..f14748090e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/microsecond/prop-desc.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-get-temporal.plaindatetime.prototype.microsecond
+description: The "microsecond" property of Temporal.PlainDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "microsecond");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/microsecond/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/microsecond/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/microsecond/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/millisecond/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/millisecond/branding.js
new file mode 100644
index 0000000000..806197f618
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/millisecond/branding.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-get-temporal.plaindatetime.prototype.millisecond
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const millisecond = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "millisecond").get;
+
+assert.sameValue(typeof millisecond, "function");
+
+assert.throws(TypeError, () => millisecond.call(undefined), "undefined");
+assert.throws(TypeError, () => millisecond.call(null), "null");
+assert.throws(TypeError, () => millisecond.call(true), "true");
+assert.throws(TypeError, () => millisecond.call(""), "empty string");
+assert.throws(TypeError, () => millisecond.call(Symbol()), "symbol");
+assert.throws(TypeError, () => millisecond.call(1), "1");
+assert.throws(TypeError, () => millisecond.call({}), "plain object");
+assert.throws(TypeError, () => millisecond.call(Temporal.PlainDateTime), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => millisecond.call(Temporal.PlainDateTime.prototype), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/millisecond/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/millisecond/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/millisecond/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/millisecond/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/millisecond/prop-desc.js
new file mode 100644
index 0000000000..67667b89f2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/millisecond/prop-desc.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-get-temporal.plaindatetime.prototype.millisecond
+description: The "millisecond" property of Temporal.PlainDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "millisecond");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/millisecond/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/millisecond/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/millisecond/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/minute/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/minute/branding.js
new file mode 100644
index 0000000000..b8c7df0cbc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/minute/branding.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-get-temporal.plaindatetime.prototype.minute
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const minute = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "minute").get;
+
+assert.sameValue(typeof minute, "function");
+
+assert.throws(TypeError, () => minute.call(undefined), "undefined");
+assert.throws(TypeError, () => minute.call(null), "null");
+assert.throws(TypeError, () => minute.call(true), "true");
+assert.throws(TypeError, () => minute.call(""), "empty string");
+assert.throws(TypeError, () => minute.call(Symbol()), "symbol");
+assert.throws(TypeError, () => minute.call(1), "1");
+assert.throws(TypeError, () => minute.call({}), "plain object");
+assert.throws(TypeError, () => minute.call(Temporal.PlainDateTime), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => minute.call(Temporal.PlainDateTime.prototype), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/minute/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/minute/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/minute/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/minute/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/minute/prop-desc.js
new file mode 100644
index 0000000000..b8294ac693
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/minute/prop-desc.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-get-temporal.plaindatetime.prototype.minute
+description: The "minute" property of Temporal.PlainDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "minute");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/minute/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/minute/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/minute/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/month/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/month/branding.js
new file mode 100644
index 0000000000..0b3cfafaac
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/month/branding.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-get-temporal.plaindatetime.prototype.month
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const month = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "month").get;
+
+assert.sameValue(typeof month, "function");
+
+assert.throws(TypeError, () => month.call(undefined), "undefined");
+assert.throws(TypeError, () => month.call(null), "null");
+assert.throws(TypeError, () => month.call(true), "true");
+assert.throws(TypeError, () => month.call(""), "empty string");
+assert.throws(TypeError, () => month.call(Symbol()), "symbol");
+assert.throws(TypeError, () => month.call(1), "1");
+assert.throws(TypeError, () => month.call({}), "plain object");
+assert.throws(TypeError, () => month.call(Temporal.PlainDateTime), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => month.call(Temporal.PlainDateTime.prototype), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/month/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/month/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/month/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/month/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/month/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..297016272f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/month/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plaindatetime.prototype.month
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const monthOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "month");
+Object.defineProperty(Temporal.Calendar.prototype, "month", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("month should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, "iso8601");
+instance.month;
+
+Object.defineProperty(Temporal.Calendar.prototype, "month", monthOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/month/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/month/custom.js
new file mode 100644
index 0000000000..2bc1a17f12
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/month/custom.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-get-temporal.plaindatetime.prototype.month
+description: Custom calendar tests for month().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ month(...args) {
+ ++calls;
+ assert.compareArray(args, [pdt], "month arguments");
+ return 7;
+ }
+}
+
+const calendar = new CustomCalendar();
+const pdt = new Temporal.PlainDateTime(1830, 8, 25, 20, 0, 0, 0, 0, 0, calendar);
+const result = pdt.month;
+assert.sameValue(result, 7, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/month/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/month/prop-desc.js
new file mode 100644
index 0000000000..513395b28a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/month/prop-desc.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-get-temporal.plaindatetime.prototype.month
+description: The "month" property of Temporal.PlainDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "month");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/month/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/month/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/month/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/month/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/month/validate-calendar-value.js
new file mode 100644
index 0000000000..d53f50541d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/month/validate-calendar-value.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-get-temporal.plaindatetime.prototype.month
+description: Validate result returned from calendar month() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [null, TypeError],
+ [false, TypeError],
+ [Infinity, RangeError],
+ [-Infinity, RangeError],
+ [NaN, RangeError],
+ [-7, RangeError],
+ [-0.1, RangeError],
+ ["string", TypeError],
+ [Symbol("foo"), TypeError],
+ [7n, TypeError],
+ [{}, TypeError],
+ [true, TypeError],
+ [7.1, RangeError],
+ ["7", TypeError],
+ ["7.5", TypeError],
+ [{valueOf() { return 7; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ month() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainDateTime(1981, 12, 15, 14, 15, 45, 987, 654, 321, calendar);
+ assert.throws(error, () => instance.month, `${typeof result} ${String(result)} not converted to positive integer`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthCode/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthCode/branding.js
new file mode 100644
index 0000000000..a327685c07
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthCode/branding.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-get-temporal.plaindatetime.prototype.monthcode
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const monthCode = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "monthCode").get;
+
+assert.sameValue(typeof monthCode, "function");
+
+assert.throws(TypeError, () => monthCode.call(undefined), "undefined");
+assert.throws(TypeError, () => monthCode.call(null), "null");
+assert.throws(TypeError, () => monthCode.call(true), "true");
+assert.throws(TypeError, () => monthCode.call(""), "empty string");
+assert.throws(TypeError, () => monthCode.call(Symbol()), "symbol");
+assert.throws(TypeError, () => monthCode.call(1), "1");
+assert.throws(TypeError, () => monthCode.call({}), "plain object");
+assert.throws(TypeError, () => monthCode.call(Temporal.PlainDateTime), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => monthCode.call(Temporal.PlainDateTime.prototype), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthCode/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthCode/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthCode/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthCode/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthCode/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..eec179a36a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthCode/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plaindatetime.prototype.monthcode
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const monthCodeOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "monthCode");
+Object.defineProperty(Temporal.Calendar.prototype, "monthCode", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("monthCode should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, "iso8601");
+instance.monthCode;
+
+Object.defineProperty(Temporal.Calendar.prototype, "monthCode", monthCodeOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthCode/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthCode/custom.js
new file mode 100644
index 0000000000..27ebb31985
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthCode/custom.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-get-temporal.plaindatetime.prototype.monthcode
+description: Custom calendar tests for monthCode().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ monthCode(...args) {
+ ++calls;
+ assert.compareArray(args, [pdt], "monthCode arguments");
+ return "M01";
+ }
+}
+
+const calendar = new CustomCalendar();
+const pdt = new Temporal.PlainDateTime(1830, 8, 25, 20, 0, 0, 0, 0, 0, calendar);
+const result = pdt.monthCode;
+assert.sameValue(result, "M01", "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthCode/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthCode/prop-desc.js
new file mode 100644
index 0000000000..0f8fb871ce
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthCode/prop-desc.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-get-temporal.plaindatetime.prototype.monthcode
+description: The "monthCode" property of Temporal.PlainDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "monthCode");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthCode/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthCode/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthCode/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthCode/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthCode/validate-calendar-value.js
new file mode 100644
index 0000000000..1f17391289
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthCode/validate-calendar-value.js
@@ -0,0 +1,31 @@
+// |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-get-temporal.plaindatetime.prototype.monthcode
+description: Validate result returned from calendar monthCode() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [Symbol("foo"), TypeError],
+ [null, TypeError],
+ [true, TypeError],
+ [false, TypeError],
+ [7.1, TypeError],
+ [{toString() { return "M01"; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ monthCode() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainDateTime(1981, 12, 15, 14, 15, 45, 987, 654, 321, calendar);
+ assert.throws(error, () => instance.monthCode, `${typeof result} ${String(result)} not converted to string`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/basic.js
new file mode 100644
index 0000000000..a1e7d322ca
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/basic.js
@@ -0,0 +1,15 @@
+// |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-get-temporal.plaindatetime.prototype.monthsinyear
+description: Checking months in year for a "normal" case (non-undefined, non-boundary case, etc.)
+features: [Temporal]
+---*/
+
+const calendar = Temporal.Calendar.from("iso8601");
+const datetime = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789, calendar);
+assert.sameValue(datetime.monthsInYear, 12, "check months in year information");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/branding.js
new file mode 100644
index 0000000000..24380ac91f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/branding.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-get-temporal.plaindatetime.prototype.monthsinyear
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const monthsInYear = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "monthsInYear").get;
+
+assert.sameValue(typeof monthsInYear, "function");
+
+assert.throws(TypeError, () => monthsInYear.call(undefined), "undefined");
+assert.throws(TypeError, () => monthsInYear.call(null), "null");
+assert.throws(TypeError, () => monthsInYear.call(true), "true");
+assert.throws(TypeError, () => monthsInYear.call(""), "empty string");
+assert.throws(TypeError, () => monthsInYear.call(Symbol()), "symbol");
+assert.throws(TypeError, () => monthsInYear.call(1), "1");
+assert.throws(TypeError, () => monthsInYear.call({}), "plain object");
+assert.throws(TypeError, () => monthsInYear.call(Temporal.PlainDateTime), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => monthsInYear.call(Temporal.PlainDateTime.prototype), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..b9a114f28c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plaindatetime.prototype.monthsinyear
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const monthsInYearOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "monthsInYear");
+Object.defineProperty(Temporal.Calendar.prototype, "monthsInYear", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("monthsInYear should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, "iso8601");
+instance.monthsInYear;
+
+Object.defineProperty(Temporal.Calendar.prototype, "monthsInYear", monthsInYearOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/custom.js
new file mode 100644
index 0000000000..c0af9d53c8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/custom.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-get-temporal.plaindatetime.prototype.monthsinyear
+description: Custom calendar tests for monthsInYear().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ monthsInYear(...args) {
+ ++calls;
+ assert.compareArray(args, [pdt], "monthsInYear arguments");
+ return 7;
+ }
+}
+
+const calendar = new CustomCalendar();
+const pdt = new Temporal.PlainDateTime(1830, 8, 25, 20, 0, 0, 0, 0, 0, calendar);
+const result = pdt.monthsInYear;
+assert.sameValue(result, 7, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/prop-desc.js
new file mode 100644
index 0000000000..17d83e9ddd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/prop-desc.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-get-temporal.plaindatetime.prototype.monthsinyear
+description: The "monthsInYear" property of Temporal.PlainDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "monthsInYear");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/validate-calendar-value.js
new file mode 100644
index 0000000000..0bdce32ca5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/validate-calendar-value.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-get-temporal.plaindatetime.prototype.monthsinyear
+description: Validate result returned from calendar monthsInYear() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [null, TypeError],
+ [false, TypeError],
+ [Infinity, RangeError],
+ [-Infinity, RangeError],
+ [NaN, RangeError],
+ [-7, RangeError],
+ [-0.1, RangeError],
+ ["string", TypeError],
+ [Symbol("foo"), TypeError],
+ [7n, TypeError],
+ [{}, TypeError],
+ [true, TypeError],
+ [7.1, RangeError],
+ ["7", TypeError],
+ ["7.5", TypeError],
+ [{valueOf() { return 7; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ monthsInYear() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainDateTime(1981, 12, 15, 14, 15, 45, 987, 654, 321, calendar);
+ assert.throws(error, () => instance.monthsInYear, `${typeof result} ${String(result)} not converted to positive integer`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/nanosecond/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/nanosecond/branding.js
new file mode 100644
index 0000000000..45a90d8547
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/nanosecond/branding.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-get-temporal.plaindatetime.prototype.nanosecond
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const nanosecond = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "nanosecond").get;
+
+assert.sameValue(typeof nanosecond, "function");
+
+assert.throws(TypeError, () => nanosecond.call(undefined), "undefined");
+assert.throws(TypeError, () => nanosecond.call(null), "null");
+assert.throws(TypeError, () => nanosecond.call(true), "true");
+assert.throws(TypeError, () => nanosecond.call(""), "empty string");
+assert.throws(TypeError, () => nanosecond.call(Symbol()), "symbol");
+assert.throws(TypeError, () => nanosecond.call(1), "1");
+assert.throws(TypeError, () => nanosecond.call({}), "plain object");
+assert.throws(TypeError, () => nanosecond.call(Temporal.PlainDateTime), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => nanosecond.call(Temporal.PlainDateTime.prototype), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/nanosecond/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/nanosecond/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/nanosecond/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/nanosecond/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/nanosecond/prop-desc.js
new file mode 100644
index 0000000000..89b1843e50
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/nanosecond/prop-desc.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-get-temporal.plaindatetime.prototype.nanosecond
+description: The "nanosecond" property of Temporal.PlainDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "nanosecond");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/nanosecond/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/nanosecond/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/nanosecond/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/prop-desc.js
new file mode 100644
index 0000000000..c95a815a3c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/prop-desc.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-plaindatetime-prototype
+description: The "prototype" property of Temporal.PlainDateTime
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(typeof Temporal.PlainDateTime.prototype, "object");
+assert.notSameValue(Temporal.PlainDateTime.prototype, null);
+
+verifyProperty(Temporal.PlainDateTime, "prototype", {
+ writable: false,
+ enumerable: false,
+ configurable: false,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/balance.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/balance.js
new file mode 100644
index 0000000000..550f5ccd88
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/balance.js
@@ -0,0 +1,22 @@
+// |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.plaindatetime.prototype.round
+description: Rounding balances to the next smallest unit
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const dt = new Temporal.PlainDateTime(1976, 11, 18, 23, 59, 59, 999, 999, 999);
+
+["day", "hour", "minute", "second", "millisecond", "microsecond"].forEach((smallestUnit) => {
+ TemporalHelpers.assertPlainDateTime(
+ dt.round({ smallestUnit }),
+ 1976, 11, "M11", 19, 0, 0, 0, 0, 0, 0,
+ `balances to next ${smallestUnit}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/branding.js
new file mode 100644
index 0000000000..e8f620dbc2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/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.plaindatetime.prototype.round
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const round = Temporal.PlainDateTime.prototype.round;
+
+assert.sameValue(typeof round, "function");
+
+const args = ['hour'];
+
+assert.throws(TypeError, () => round.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => round.apply(null, args), "null");
+assert.throws(TypeError, () => round.apply(true, args), "true");
+assert.throws(TypeError, () => round.apply("", args), "empty string");
+assert.throws(TypeError, () => round.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => round.apply(1, args), "1");
+assert.throws(TypeError, () => round.apply({}, args), "plain object");
+assert.throws(TypeError, () => round.apply(Temporal.PlainDateTime, args), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => round.apply(Temporal.PlainDateTime.prototype, args), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/builtin.js
new file mode 100644
index 0000000000..f906a20fa6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/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.plaindatetime.prototype.round
+description: >
+ Tests that Temporal.PlainDateTime.prototype.round
+ 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.PlainDateTime.prototype.round),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.prototype.round),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.prototype.round),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.prototype.round.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/length.js
new file mode 100644
index 0000000000..2f32b93ea5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/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.plaindatetime.prototype.round
+description: Temporal.PlainDateTime.prototype.round.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.PlainDateTime.prototype.round, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/limits.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/limits.js
new file mode 100644
index 0000000000..7a12038973
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/limits.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.plaindatetime.prototype.round
+description: Checking limits of representable PlainDateTime
+features: [Temporal]
+---*/
+
+const min = new Temporal.PlainDateTime(-271821, 4, 19, 0, 0, 0, 0, 0, 1);
+const max = new Temporal.PlainDateTime(275760, 9, 13, 23, 59, 59, 999, 999, 999);
+
+["day", "hour", "minute", "second", "millisecond", "microsecond"].forEach((smallestUnit) => {
+ assert.throws(
+ RangeError,
+ () => min.round({ smallestUnit, roundingMode: "floor" }),
+ `rounding beyond limit (unit = ${smallestUnit}, rounding mode = floor)`
+ );
+ assert.throws(
+ RangeError,
+ () => max.round({ smallestUnit, roundingMode: "ceil" }),
+ `rounding beyond limit (unit = ${smallestUnit}, rounding mode = ceil)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/name.js
new file mode 100644
index 0000000000..bd1fc4cb17
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/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.plaindatetime.prototype.round
+description: Temporal.PlainDateTime.prototype.round.name is "round".
+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.PlainDateTime.prototype.round, "name", {
+ value: "round",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/not-a-constructor.js
new file mode 100644
index 0000000000..664de7a906
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/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.plaindatetime.prototype.round
+description: >
+ Temporal.PlainDateTime.prototype.round 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.PlainDateTime.prototype.round();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.prototype.round), false,
+ "isConstructor(Temporal.PlainDateTime.prototype.round)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/options-wrong-type.js
new file mode 100644
index 0000000000..b30b8481a8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/options-wrong-type.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.plaindatetime.prototype.round
+description: TypeError thrown when options argument is missing or a non-string primitive
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const badOptions = [
+ undefined,
+ null,
+ true,
+ Symbol(),
+ 1,
+ 2n,
+];
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2);
+assert.throws(TypeError, () => instance.round(), "TypeError on missing options argument");
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.round(value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/prop-desc.js
new file mode 100644
index 0000000000..f4dee3b7c9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/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.plaindatetime.prototype.round
+description: The "round" property of Temporal.PlainDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.prototype.round,
+ "function",
+ "`typeof PlainDateTime.prototype.round` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime.prototype, "round", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/rounding-direction.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/rounding-direction.js
new file mode 100644
index 0000000000..2193d200b2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/rounding-direction.js
@@ -0,0 +1,34 @@
+// |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.plaindatetime.prototype.round
+description: Rounding down is towards the Big Bang, not the epoch or 1 BCE
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(-99, 12, 15, 12, 0, 0, 500);
+TemporalHelpers.assertPlainDateTime(
+ instance.round({ smallestUnit: "second", roundingMode: "floor" }),
+ -99, 12, "M12", 15, 12, 0, 0, 0, 0, 0,
+ "Rounding down is towards the Big Bang, not the epoch or 1 BCE (roundingMode floor)"
+);
+TemporalHelpers.assertPlainDateTime(
+ instance.round({ smallestUnit: "second", roundingMode: "trunc" }),
+ -99, 12, "M12", 15, 12, 0, 0, 0, 0, 0,
+ "Rounding down is towards the Big Bang, not the epoch or 1 BCE (roundingMode trunc)"
+);
+TemporalHelpers.assertPlainDateTime(
+ instance.round({ smallestUnit: "second", roundingMode: "ceil" }),
+ -99, 12, "M12", 15, 12, 0, 1, 0, 0, 0,
+ "Rounding up is away from the Big Bang, not the epoch or 1 BCE (roundingMode ceil)"
+);
+TemporalHelpers.assertPlainDateTime(
+ instance.round({ smallestUnit: "second", roundingMode: "halfExpand" }),
+ -99, 12, "M12", 15, 12, 0, 1, 0, 0, 0,
+ "Rounding up is away from the Big Bang, not the epoch or 1 BCE (roundingMode halfExpand)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-divides.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-divides.js
new file mode 100644
index 0000000000..51bdf65d7f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-divides.js
@@ -0,0 +1,56 @@
+// |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.plaindatetime.prototype.round
+description: Rounding increment should properly divide the relevant time unit
+features: [Temporal]
+---*/
+
+const dt = new Temporal.PlainDateTime(1976, 11, 18, 14, 23, 30, 123, 456, 789);
+
+[1, 2, 3, 4, 6, 8, 12].forEach((roundingIncrement) => {
+ assert.sameValue(
+ dt.round({ smallestUnit: "hour", roundingIncrement }) instanceof Temporal.PlainDateTime,
+ true,
+ `valid hour increments divide into 24 (rounding increment = ${roundingIncrement})`);
+});
+
+["minute", "second"].forEach((smallestUnit) => {
+ [1, 2, 3, 4, 5, 6, 10, 12, 15, 20, 30].forEach((roundingIncrement) => {
+ assert.sameValue(
+ dt.round({ smallestUnit, roundingIncrement }) instanceof Temporal.PlainDateTime,
+ true,
+ `valid ${smallestUnit} increments divide into 60 (rounding increment = ${roundingIncrement})`
+ );
+ });
+});
+
+["millisecond", "microsecond", "nanosecond"].forEach((smallestUnit) => {
+ [1, 2, 4, 5, 8, 10, 20, 25, 40, 50, 100, 125, 200, 250, 500].forEach((roundingIncrement) => {
+ assert.sameValue(
+ dt.round({ smallestUnit, roundingIncrement }) instanceof Temporal.PlainDateTime,
+ true,
+ `valid ${smallestUnit} increments divide into 1000 (rounding increment = ${roundingIncrement})`);
+ });
+});
+
+const nextIncrements = {
+ "hour": 24,
+ "minute": 60,
+ "second": 60,
+ "millisecond": 1000,
+ "microsecond": 1000,
+ "nanosecond": 1000
+};
+
+Object.entries(nextIncrements).forEach(([unit, next]) => {
+ assert.throws(
+ RangeError,
+ () => dt.round({ smallestUnit: unit, roundingIncrement: next }),
+ `throws on increments that are equal to the next highest (unit = ${unit}, increment = ${next})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-does-not-divide.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-does-not-divide.js
new file mode 100644
index 0000000000..df4a40dfe6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-does-not-divide.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.plaindatetime.prototype.round
+description: Throw exception if the rounding unit does not properly divide the relevant time unit
+features: [Temporal]
+---*/
+
+const dt = new Temporal.PlainDateTime(1976, 11, 18, 14, 23, 30, 123, 456, 789);
+const units = ["day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"];
+units.forEach((unit) => {
+ assert.throws(
+ RangeError,
+ () => dt.round({ smallestUnit: unit, roundingIncrement: 29 }),
+ `throws on increments that do not divide evenly into the next highest (unit = ${unit})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-nan.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-nan.js
new file mode 100644
index 0000000000..c87e6f24c3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-nan.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.plaindatetime.prototype.round
+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-totemporaldatetimeroundingincrement step 5:
+ 5. Return ? ToTemporalRoundingIncrement(_normalizedOptions_, _maximum_, *false*).
+ sec-temporal.plaindatetime.prototype.round step 8:
+ 8. Let _roundingIncrement_ be ? ToTemporalDateTimeRoundingIncrement(_options_, _smallestUnit_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+assert.throws(RangeError, () => datetime.round({ smallestUnit: 'second', roundingIncrement: NaN }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-non-integer.js
new file mode 100644
index 0000000000..646f4e2143
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-non-integer.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.plaindatetime.prototype.round
+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 datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 5);
+const result = datetime.round({ smallestUnit: "nanosecond", roundingIncrement: 2.5, roundingMode: "expand" });
+TemporalHelpers.assertPlainDateTime(result, 2000, 5, "M05", 2, 12, 34, 56, 0, 0, 6, "roundingIncrement 2.5 truncates to 2");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-one-day.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-one-day.js
new file mode 100644
index 0000000000..7d48e74944
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-one-day.js
@@ -0,0 +1,20 @@
+// |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.plaindatetime.prototype.round
+description: One day is a valid rounding increment
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const dt = new Temporal.PlainDateTime(1976, 11, 18, 14, 23, 30, 123, 456, 789);
+
+TemporalHelpers.assertPlainDateTime(
+ dt.round({ smallestUnit: "day", roundingIncrement: 1 }),
+ 1976, 11, "M11", 19, 0, 0, 0, 0, 0, 0,
+ "1 day is a valid increment"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-out-of-range.js
new file mode 100644
index 0000000000..4c3be90a69
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-out-of-range.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.plaindatetime.prototype.round
+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 datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 5);
+assert.throws(RangeError, () => datetime.round({ smallestUnit: "nanoseconds", roundingIncrement: -Infinity }));
+assert.throws(RangeError, () => datetime.round({ smallestUnit: "nanoseconds", roundingIncrement: -1 }));
+assert.throws(RangeError, () => datetime.round({ smallestUnit: "nanoseconds", roundingIncrement: 0 }));
+assert.throws(RangeError, () => datetime.round({ smallestUnit: "nanoseconds", roundingIncrement: 0.9 }));
+assert.throws(RangeError, () => datetime.round({ smallestUnit: "nanoseconds", roundingIncrement: 1e9 + 1 }));
+assert.throws(RangeError, () => datetime.round({ smallestUnit: "nanoseconds", roundingIncrement: Infinity }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-undefined.js
new file mode 100644
index 0000000000..d5bbcce128
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-undefined.js
@@ -0,0 +1,29 @@
+// |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.plaindatetime.prototype.round
+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-totemporaldatetimeroundingincrement step 5:
+ 5. Return ? ToTemporalRoundingIncrement(_normalizedOptions_, _maximum_, *false*).
+ sec-temporal.plaindatetime.prototype.round step 8:
+ 8. Let _roundingIncrement_ be ? ToTemporalDateTimeRoundingIncrement(_options_, _smallestUnit_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+const explicit = datetime.round({ smallestUnit: 'second', roundingIncrement: undefined });
+TemporalHelpers.assertPlainDateTime(explicit, 2000, 5, "M05", 2, 12, 34, 57, 0, 0, 0, "default roundingIncrement is 1");
+
+const implicit = datetime.round({ smallestUnit: 'second' });
+TemporalHelpers.assertPlainDateTime(implicit, 2000, 5, "M05", 2, 12, 34, 57, 0, 0, 0, "default roundingIncrement is 1");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-wrong-type.js
new file mode 100644
index 0000000000..05c5d22697
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-wrong-type.js
@@ -0,0 +1,29 @@
+// |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.round
+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-totemporaldatetimeroundingincrement step 5:
+ 5. Return ? ToTemporalRoundingIncrement(_normalizedOptions_, _maximum_, *false*).
+ sec-temporal.plaindatetime.prototype.round step 8:
+ 8. Let _roundingIncrement_ be ? ToTemporalDateTimeRoundingIncrement(_options_, _smallestUnit_).
+includes: [temporalHelpers.js, compareArray.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+TemporalHelpers.checkRoundingIncrementOptionWrongType(
+ (roundingIncrement) => datetime.round({ smallestUnit: 'second', roundingIncrement }),
+ (result, descr) => TemporalHelpers.assertPlainDateTime(result, 2000, 5, "M05", 2, 12, 34, 57, 0, 0, 0, descr),
+ (result, descr) => TemporalHelpers.assertPlainDateTime(result, 2000, 5, "M05", 2, 12, 34, 56, 0, 0, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-basic.js
new file mode 100644
index 0000000000..085b8bac3b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-basic.js
@@ -0,0 +1,50 @@
+// |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.plaindatetime.prototype.round
+description: Basic checks for rounding mode
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const dt = new Temporal.PlainDateTime(1976, 11, 18, 14, 23, 30, 123, 456, 789);
+
+TemporalHelpers.assertPlainDateTime(
+ dt.round({ smallestUnit: "hour", roundingIncrement: 4 }),
+ 1976, 11, "M11", 18, 16, 0, 0, 0, 0, 0,
+ "rounds to an increment of hours"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ dt.round({ smallestUnit: "minute", roundingIncrement: 15 }),
+ 1976, 11, "M11", 18, 14, 30, 0, 0, 0, 0,
+ "rounds to an increment of minutes"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ dt.round({ smallestUnit: "second", roundingIncrement: 30 }),
+ 1976, 11, "M11", 18, 14, 23, 30, 0, 0, 0,
+ "rounds to an increment of seconds"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ dt.round({ smallestUnit: "millisecond", roundingIncrement: 10 }),
+ 1976, 11, "M11", 18, 14, 23, 30, 120, 0, 0,
+ "rounds to an increment of milliseconds"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ dt.round({ smallestUnit: "microsecond", roundingIncrement: 10 }),
+ 1976, 11, "M11", 18, 14, 23, 30, 123, 460, 0,
+ "rounds to an increment of microseconds"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ dt.round({ smallestUnit: "nanosecond", roundingIncrement: 10 }),
+ 1976, 11, "M11", 18, 14, 23, 30, 123, 456, 790,
+ "rounds to an increment of nanoseconds"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-ceil.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-ceil.js
new file mode 100644
index 0000000000..6363a0b9f0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-ceil.js
@@ -0,0 +1,35 @@
+// |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.plaindatetime.prototype.round
+description: Tests calculations with roundingMode "ceil".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 14, 23, 30, 123, 987, 500);
+
+const expected = [
+ ["day", [1976, 11, 'M11', 19]],
+ ["hour", [1976, 11, 'M11', 18, 15]],
+ ["minute", [1976, 11, 'M11', 18, 14, 24]],
+ ["second", [1976, 11, 'M11', 18, 14, 23, 31]],
+ ["millisecond", [1976, 11, 'M11', 18, 14, 23, 30, 124]],
+ ["microsecond", [1976, 11, 'M11', 18, 14, 23, 30, 123, 988]],
+ ["nanosecond", [1976, 11, 'M11', 18, 14, 23, 30, 123, 987, 500]],
+];
+
+const roundingMode = "ceil";
+
+expected.forEach(([smallestUnit, expected]) => {
+ const [y, mon, mc, d, h = 0, min = 0, s = 0, ms = 0, µs = 0, ns = 0] = expected;
+ TemporalHelpers.assertPlainDateTime(
+ instance.round({ smallestUnit, roundingMode }),
+ y, mon, mc, d, h, min, s, ms, µs, ns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-expand.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-expand.js
new file mode 100644
index 0000000000..00b8ab4742
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-expand.js
@@ -0,0 +1,35 @@
+// |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.plaindatetime.prototype.round
+description: Tests calculations with roundingMode "expand".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 14, 23, 30, 123, 987, 500);
+
+const expected = [
+ ["day", [1976, 11, 'M11', 19]],
+ ["hour", [1976, 11, 'M11', 18, 15]],
+ ["minute", [1976, 11, 'M11', 18, 14, 24]],
+ ["second", [1976, 11, 'M11', 18, 14, 23, 31]],
+ ["millisecond", [1976, 11, 'M11', 18, 14, 23, 30, 124]],
+ ["microsecond", [1976, 11, 'M11', 18, 14, 23, 30, 123, 988]],
+ ["nanosecond", [1976, 11, 'M11', 18, 14, 23, 30, 123, 987, 500]],
+];
+
+const roundingMode = "expand";
+
+expected.forEach(([smallestUnit, expected]) => {
+ const [y, mon, mc, d, h = 0, min = 0, s = 0, ms = 0, µs = 0, ns = 0] = expected;
+ TemporalHelpers.assertPlainDateTime(
+ instance.round({ smallestUnit, roundingMode }),
+ y, mon, mc, d, h, min, s, ms, µs, ns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-floor.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-floor.js
new file mode 100644
index 0000000000..1a70fe0c2c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-floor.js
@@ -0,0 +1,35 @@
+// |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.plaindatetime.prototype.round
+description: Tests calculations with roundingMode "floor".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 14, 23, 30, 123, 987, 500);
+
+const expected = [
+ ["day", [1976, 11, 'M11', 18]],
+ ["hour", [1976, 11, 'M11', 18, 14]],
+ ["minute", [1976, 11, 'M11', 18, 14, 23]],
+ ["second", [1976, 11, 'M11', 18, 14, 23, 30]],
+ ["millisecond", [1976, 11, 'M11', 18, 14, 23, 30, 123]],
+ ["microsecond", [1976, 11, 'M11', 18, 14, 23, 30, 123, 987]],
+ ["nanosecond", [1976, 11, 'M11', 18, 14, 23, 30, 123, 987, 500]],
+];
+
+const roundingMode = "floor";
+
+expected.forEach(([smallestUnit, expected]) => {
+ const [y, mon, mc, d, h = 0, min = 0, s = 0, ms = 0, µs = 0, ns = 0] = expected;
+ TemporalHelpers.assertPlainDateTime(
+ instance.round({ smallestUnit, roundingMode }),
+ y, mon, mc, d, h, min, s, ms, µs, ns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-halfCeil.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-halfCeil.js
new file mode 100644
index 0000000000..57a84b3cd6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-halfCeil.js
@@ -0,0 +1,35 @@
+// |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.plaindatetime.prototype.round
+description: Tests calculations with roundingMode "halfCeil".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 14, 23, 30, 123, 987, 500);
+
+const expected = [
+ ["day", [1976, 11, 'M11', 19]],
+ ["hour", [1976, 11, 'M11', 18, 14]],
+ ["minute", [1976, 11, 'M11', 18, 14, 24]],
+ ["second", [1976, 11, 'M11', 18, 14, 23, 30]],
+ ["millisecond", [1976, 11, 'M11', 18, 14, 23, 30, 124]],
+ ["microsecond", [1976, 11, 'M11', 18, 14, 23, 30, 123, 988]],
+ ["nanosecond", [1976, 11, 'M11', 18, 14, 23, 30, 123, 987, 500]],
+];
+
+const roundingMode = "halfCeil";
+
+expected.forEach(([smallestUnit, expected]) => {
+ const [y, mon, mc, d, h = 0, min = 0, s = 0, ms = 0, µs = 0, ns = 0] = expected;
+ TemporalHelpers.assertPlainDateTime(
+ instance.round({ smallestUnit, roundingMode }),
+ y, mon, mc, d, h, min, s, ms, µs, ns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-halfEven.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-halfEven.js
new file mode 100644
index 0000000000..b65c21d152
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-halfEven.js
@@ -0,0 +1,35 @@
+// |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.plaindatetime.prototype.round
+description: Tests calculations with roundingMode "halfEven".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 14, 23, 30, 123, 987, 500);
+
+const expected = [
+ ["day", [1976, 11, 'M11', 19]],
+ ["hour", [1976, 11, 'M11', 18, 14]],
+ ["minute", [1976, 11, 'M11', 18, 14, 24]],
+ ["second", [1976, 11, 'M11', 18, 14, 23, 30]],
+ ["millisecond", [1976, 11, 'M11', 18, 14, 23, 30, 124]],
+ ["microsecond", [1976, 11, 'M11', 18, 14, 23, 30, 123, 988]],
+ ["nanosecond", [1976, 11, 'M11', 18, 14, 23, 30, 123, 987, 500]],
+];
+
+const roundingMode = "halfEven";
+
+expected.forEach(([smallestUnit, expected]) => {
+ const [y, mon, mc, d, h = 0, min = 0, s = 0, ms = 0, µs = 0, ns = 0] = expected;
+ TemporalHelpers.assertPlainDateTime(
+ instance.round({ smallestUnit, roundingMode }),
+ y, mon, mc, d, h, min, s, ms, µs, ns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-halfExpand.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-halfExpand.js
new file mode 100644
index 0000000000..8e5c31cc24
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-halfExpand.js
@@ -0,0 +1,35 @@
+// |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.plaindatetime.prototype.round
+description: Tests calculations with roundingMode "halfExpand".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 14, 23, 30, 123, 987, 500);
+
+const expected = [
+ ["day", [1976, 11, 'M11', 19]],
+ ["hour", [1976, 11, 'M11', 18, 14]],
+ ["minute", [1976, 11, 'M11', 18, 14, 24]],
+ ["second", [1976, 11, 'M11', 18, 14, 23, 30]],
+ ["millisecond", [1976, 11, 'M11', 18, 14, 23, 30, 124]],
+ ["microsecond", [1976, 11, 'M11', 18, 14, 23, 30, 123, 988]],
+ ["nanosecond", [1976, 11, 'M11', 18, 14, 23, 30, 123, 987, 500]],
+];
+
+const roundingMode = "halfExpand";
+
+expected.forEach(([smallestUnit, expected]) => {
+ const [y, mon, mc, d, h = 0, min = 0, s = 0, ms = 0, µs = 0, ns = 0] = expected;
+ TemporalHelpers.assertPlainDateTime(
+ instance.round({ smallestUnit, roundingMode }),
+ y, mon, mc, d, h, min, s, ms, µs, ns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-halfFloor.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-halfFloor.js
new file mode 100644
index 0000000000..9eb15ee2a3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-halfFloor.js
@@ -0,0 +1,35 @@
+// |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.plaindatetime.prototype.round
+description: Tests calculations with roundingMode "halfFloor".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 14, 23, 30, 123, 987, 500);
+
+const expected = [
+ ["day", [1976, 11, 'M11', 19]],
+ ["hour", [1976, 11, 'M11', 18, 14]],
+ ["minute", [1976, 11, 'M11', 18, 14, 24]],
+ ["second", [1976, 11, 'M11', 18, 14, 23, 30]],
+ ["millisecond", [1976, 11, 'M11', 18, 14, 23, 30, 124]],
+ ["microsecond", [1976, 11, 'M11', 18, 14, 23, 30, 123, 987]],
+ ["nanosecond", [1976, 11, 'M11', 18, 14, 23, 30, 123, 987, 500]],
+];
+
+const roundingMode = "halfFloor";
+
+expected.forEach(([smallestUnit, expected]) => {
+ const [y, mon, mc, d, h = 0, min = 0, s = 0, ms = 0, µs = 0, ns = 0] = expected;
+ TemporalHelpers.assertPlainDateTime(
+ instance.round({ smallestUnit, roundingMode }),
+ y, mon, mc, d, h, min, s, ms, µs, ns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-halfTrunc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-halfTrunc.js
new file mode 100644
index 0000000000..7a6534d351
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-halfTrunc.js
@@ -0,0 +1,35 @@
+// |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.plaindatetime.prototype.round
+description: Tests calculations with roundingMode "halfTrunc".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 14, 23, 30, 123, 987, 500);
+
+const expected = [
+ ["day", [1976, 11, 'M11', 19]],
+ ["hour", [1976, 11, 'M11', 18, 14]],
+ ["minute", [1976, 11, 'M11', 18, 14, 24]],
+ ["second", [1976, 11, 'M11', 18, 14, 23, 30]],
+ ["millisecond", [1976, 11, 'M11', 18, 14, 23, 30, 124]],
+ ["microsecond", [1976, 11, 'M11', 18, 14, 23, 30, 123, 987]],
+ ["nanosecond", [1976, 11, 'M11', 18, 14, 23, 30, 123, 987, 500]],
+];
+
+const roundingMode = "halfTrunc";
+
+expected.forEach(([smallestUnit, expected]) => {
+ const [y, mon, mc, d, h = 0, min = 0, s = 0, ms = 0, µs = 0, ns = 0] = expected;
+ TemporalHelpers.assertPlainDateTime(
+ instance.round({ smallestUnit, roundingMode }),
+ y, mon, mc, d, h, min, s, ms, µs, ns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-halfexpand-is-default.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-halfexpand-is-default.js
new file mode 100644
index 0000000000..5a66171d04
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-halfexpand-is-default.js
@@ -0,0 +1,34 @@
+// |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.plaindatetime.prototype.round
+description: Half-expand is the default rounding mode
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const dt = new Temporal.PlainDateTime(1976, 11, 18, 14, 23, 30, 123, 456, 789);
+
+const units = {
+ "day": [1976, 11, "M11", 19, 0, 0, 0, 0, 0, 0],
+ "hour": [1976, 11, "M11", 18, 14, 0, 0, 0, 0, 0],
+ "minute": [1976, 11, "M11", 18, 14, 24, 0, 0, 0, 0],
+ "second": [1976, 11, "M11", 18, 14, 23, 30, 0, 0, 0],
+ "millisecond": [1976, 11, "M11", 18, 14, 23, 30, 123, 0, 0],
+ "microsecond": [1976, 11, "M11", 18, 14, 23, 30, 123, 457, 0],
+ "nanosecond": [1976, 11, "M11", 18, 14, 23, 30, 123, 456, 789]
+};
+
+const expected = [1976, 11, "M11", 18, 0, 0, 0, 0, 0, 0];
+
+Object.entries(units).forEach(([unit, expected]) => {
+ TemporalHelpers.assertPlainDateTime(
+ dt.round({ smallestUnit: unit }),
+ ...expected,
+ `halfExpand is the default (smallest unit = ${unit}, rounding mode absent)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-invalid-string.js
new file mode 100644
index 0000000000..8bb99a0d7e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-invalid-string.js
@@ -0,0 +1,16 @@
+// |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.plaindatetime.prototype.round
+description: RangeError thrown when roundingMode option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 123, 987, 500);
+for (const roundingMode of ["other string", "cile", "CEIL", "ce\u0131l", "auto", "halfexpand", "floor\0"]) {
+ assert.throws(RangeError, () => datetime.round({ smallestUnit: "microsecond", roundingMode }));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-trunc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-trunc.js
new file mode 100644
index 0000000000..51634108db
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-trunc.js
@@ -0,0 +1,35 @@
+// |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.plaindatetime.prototype.round
+description: Tests calculations with roundingMode "trunc".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 14, 23, 30, 123, 987, 500);
+
+const expected = [
+ ["day", [1976, 11, 'M11', 18]],
+ ["hour", [1976, 11, 'M11', 18, 14]],
+ ["minute", [1976, 11, 'M11', 18, 14, 23]],
+ ["second", [1976, 11, 'M11', 18, 14, 23, 30]],
+ ["millisecond", [1976, 11, 'M11', 18, 14, 23, 30, 123]],
+ ["microsecond", [1976, 11, 'M11', 18, 14, 23, 30, 123, 987]],
+ ["nanosecond", [1976, 11, 'M11', 18, 14, 23, 30, 123, 987, 500]],
+];
+
+const roundingMode = "trunc";
+
+expected.forEach(([smallestUnit, expected]) => {
+ const [y, mon, mc, d, h = 0, min = 0, s = 0, ms = 0, µs = 0, ns = 0] = expected;
+ TemporalHelpers.assertPlainDateTime(
+ instance.round({ smallestUnit, roundingMode }),
+ y, mon, mc, d, h, min, s, ms, µs, ns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-undefined.js
new file mode 100644
index 0000000000..852164e980
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-undefined.js
@@ -0,0 +1,29 @@
+// |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.plaindatetime.prototype.round
+description: Fallback value for roundingMode option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 123, 987, 500);
+
+const explicit1 = datetime.round({ smallestUnit: "microsecond", roundingMode: undefined });
+TemporalHelpers.assertPlainDateTime(explicit1, 2000, 5, "M05", 2, 12, 34, 56, 123, 988, 0, "default roundingMode is halfExpand");
+const implicit1 = datetime.round({ smallestUnit: "microsecond" });
+TemporalHelpers.assertPlainDateTime(implicit1, 2000, 5, "M05", 2, 12, 34, 56, 123, 988, 0, "default roundingMode is halfExpand");
+
+const explicit2 = datetime.round({ smallestUnit: "millisecond", roundingMode: undefined });
+TemporalHelpers.assertPlainDateTime(explicit2, 2000, 5, "M05", 2, 12, 34, 56, 124, 0, 0, "default roundingMode is halfExpand");
+const implicit2 = datetime.round({ smallestUnit: "millisecond" });
+TemporalHelpers.assertPlainDateTime(implicit2, 2000, 5, "M05", 2, 12, 34, 56, 124, 0, 0, "default roundingMode is halfExpand");
+
+const explicit3 = datetime.round({ smallestUnit: "second", roundingMode: undefined });
+TemporalHelpers.assertPlainDateTime(explicit3, 2000, 5, "M05", 2, 12, 34, 56, 0, 0, 0, "default roundingMode is halfExpand");
+const implicit3 = datetime.round({ smallestUnit: "second" });
+TemporalHelpers.assertPlainDateTime(implicit3, 2000, 5, "M05", 2, 12, 34, 56, 0, 0, 0, "default roundingMode is halfExpand");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-wrong-type.js
new file mode 100644
index 0000000000..58da6f97c2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-wrong-type.js
@@ -0,0 +1,18 @@
+// |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.plaindatetime.prototype.round
+description: Type conversions for roundingMode option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 123, 987, 500);
+TemporalHelpers.checkStringOptionWrongType("roundingMode", "halfExpand",
+ (roundingMode) => datetime.round({ smallestUnit: "microsecond", roundingMode }),
+ (result, descr) => TemporalHelpers.assertPlainDateTime(result, 2000, 5, "M05", 2, 12, 34, 56, 123, 988, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundto-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundto-invalid-string.js
new file mode 100644
index 0000000000..e466c5c37a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/roundto-invalid-string.js
@@ -0,0 +1,36 @@
+// |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.plaindatetime.prototype.round
+description: RangeError thrown when smallestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 123, 987, 500);
+const badValues = [
+ "era",
+ "eraYear",
+ "year",
+ "month",
+ "week",
+ "millisecond\0",
+ "mill\u0131second",
+ "SECOND",
+ "eras",
+ "eraYears",
+ "years",
+ "months",
+ "weeks",
+ "milliseconds\0",
+ "mill\u0131seconds",
+ "SECONDS",
+ "other string",
+];
+for (const smallestUnit of badValues) {
+ assert.throws(RangeError, () => datetime.round(smallestUnit),
+ `"${smallestUnit}" is not a valid value for smallest unit`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/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/PlainDateTime/prototype/round/smallestunit-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/smallestunit-invalid-string.js
new file mode 100644
index 0000000000..fe08dcf32f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/smallestunit-invalid-string.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.plaindatetime.prototype.round
+description: RangeError thrown when smallestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 123, 987, 500);
+const badValues = [
+ "era",
+ "eraYear",
+ "year",
+ "month",
+ "week",
+ "millisecond\0",
+ "mill\u0131second",
+ "SECOND",
+ "eras",
+ "eraYears",
+ "years",
+ "months",
+ "weeks",
+ "milliseconds\0",
+ "mill\u0131seconds",
+ "SECONDS",
+ "other string",
+];
+for (const smallestUnit of badValues) {
+ assert.throws(RangeError, () => datetime.round({ smallestUnit }),
+ `"${smallestUnit}" is not a valid value for smallest unit`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/smallestunit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/smallestunit-plurals-accepted.js
new file mode 100644
index 0000000000..5883d38140
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/smallestunit-plurals-accepted.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.plaindatetime.prototype.round
+description: Plural units are accepted as well for the smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal, arrow-function]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 789, 999, 999);
+const validUnits = [
+ "day",
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+];
+TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => datetime.round({ smallestUnit }), validUnits);
+TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => datetime.round(smallestUnit), validUnits);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/smallestunit-string-shorthand.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/smallestunit-string-shorthand.js
new file mode 100644
index 0000000000..98e903dc48
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/smallestunit-string-shorthand.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.plaindatetime.prototype.round
+description: String as first argument is equivalent to options bag with smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal, arrow-function]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 789, 999, 999);
+const validUnits = [
+ "day",
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+];
+validUnits.forEach((smallestUnit) => {
+ const full = instance.round({ smallestUnit });
+ const shorthand = instance.round(smallestUnit);
+ TemporalHelpers.assertPlainDateTimesEqual(shorthand, full, `"${smallestUnit}" as first argument to round is equivalent to options bag`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/smallestunit-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/smallestunit-wrong-type.js
new file mode 100644
index 0000000000..58438c8163
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/smallestunit-wrong-type.js
@@ -0,0 +1,18 @@
+// |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.plaindatetime.prototype.round
+description: Type conversions for smallestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 123, 987, 500);
+TemporalHelpers.checkStringOptionWrongType("smallestUnit", "microsecond",
+ (smallestUnit) => datetime.round({ smallestUnit }),
+ (result, descr) => TemporalHelpers.assertPlainDateTime(result, 2000, 5, "M05", 2, 12, 34, 56, 123, 988, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/subclassing-ignored.js
new file mode 100644
index 0000000000..1a4fad9078
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/subclassing-ignored.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.plaindatetime.prototype.round
+description: Objects of a subclass are never created as return values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.PlainDateTime,
+ [2000, 5, 2, 12, 34, 56, 987, 654, 321],
+ "round",
+ [{ smallestUnit: 'second' }],
+ (result) => TemporalHelpers.assertPlainDateTime(result, 2000, 5, "M05", 2, 12, 34, 57, 0, 0, 0),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/throws-argument-object-insufficient-data.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/throws-argument-object-insufficient-data.js
new file mode 100644
index 0000000000..5a78bfc8e6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/throws-argument-object-insufficient-data.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.plaindatetime.prototype.round
+description: Throw if smallest unit is missing from argument
+features: [Temporal]
+---*/
+
+const dt = new Temporal.PlainDateTime(1976, 11, 18, 14, 23, 30, 123, 456, 789);
+
+assert.throws(
+ RangeError,
+ () => dt.round({ roundingIncrement: 1, roundingMode: "ceil" }),
+ "throws without required smallestUnit parameter"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/throws-argument-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/throws-argument-object.js
new file mode 100644
index 0000000000..3087ab282a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/throws-argument-object.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.plaindatetime.prototype.round
+description: Throw if argument is an empty plain object
+features: [Temporal]
+---*/
+
+const dt = new Temporal.PlainDateTime(1976, 11, 18, 14, 23, 30, 123, 456, 789);
+
+assert.throws(
+ RangeError,
+ () => dt.round({}),
+ "throws on empty object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/throws-no-argument.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/throws-no-argument.js
new file mode 100644
index 0000000000..7d3b5cc82b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/throws-no-argument.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.plaindatetime.prototype.round
+description: Throw if no arguments at all are given
+features: [Temporal]
+---*/
+
+const dt = new Temporal.PlainDateTime(1976, 11, 18, 14, 23, 30, 123, 456, 789);
+
+assert.throws(
+ TypeError,
+ () => dt.round(),
+ "throws without any parameters"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/throws-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/throws-undefined.js
new file mode 100644
index 0000000000..3e1e32fc93
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/round/throws-undefined.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.plaindatetime.prototype.round
+description: Throw if sole argument is undefined
+features: [Temporal]
+---*/
+
+const dt = new Temporal.PlainDateTime(1976, 11, 18, 14, 23, 30, 123, 456, 789);
+
+assert.throws(
+ TypeError,
+ () => dt.round(undefined),
+ "throws without undefined as sole parameter"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/second/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/second/branding.js
new file mode 100644
index 0000000000..8b168faf11
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/second/branding.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-get-temporal.plaindatetime.prototype.second
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const second = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "second").get;
+
+assert.sameValue(typeof second, "function");
+
+assert.throws(TypeError, () => second.call(undefined), "undefined");
+assert.throws(TypeError, () => second.call(null), "null");
+assert.throws(TypeError, () => second.call(true), "true");
+assert.throws(TypeError, () => second.call(""), "empty string");
+assert.throws(TypeError, () => second.call(Symbol()), "symbol");
+assert.throws(TypeError, () => second.call(1), "1");
+assert.throws(TypeError, () => second.call({}), "plain object");
+assert.throws(TypeError, () => second.call(Temporal.PlainDateTime), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => second.call(Temporal.PlainDateTime.prototype), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/second/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/second/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/second/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/second/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/second/prop-desc.js
new file mode 100644
index 0000000000..ebed5d7c63
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/second/prop-desc.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-get-temporal.plaindatetime.prototype.second
+description: The "second" property of Temporal.PlainDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "second");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/second/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/second/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/second/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..5ef369ced3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-builtin-calendar-no-array-iteration.js
@@ -0,0 +1,24 @@
+// |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.plaindatetime.prototype.since
+description: >
+ Calling the method with a property bag argument with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const arg = { year: 2000, month: 5, day: 2, hour: 21, minute: 43, second: 5, calendar: "iso8601" };
+instance.since(arg);
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-number.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-number.js
new file mode 100644
index 0000000000..ba9ca1e11a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/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.plaindatetime.prototype.since
+description: A number cannot be used in place of a Temporal.PlainDateTime
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18);
+
+const numbers = [
+ 1,
+ 19761118,
+ -19761118,
+ 1234567890,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => instance.since(arg),
+ `A number (${arg}) is not a valid ISO string for PlainDateTime`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-object.js
new file mode 100644
index 0000000000..57b3654f02
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-object.js
@@ -0,0 +1,20 @@
+// |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.plaindatetime.prototype.since
+description: Plain objects are accepted as an argument
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dt = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789);
+
+TemporalHelpers.assertDuration(
+ dt.since({ year: 2019, month: 10, day: 29, hour: 10 }),
+ 0, 0, 0, -15684, -18, -36, -29, -876, -543, -211,
+ "casts argument (plain object)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-plaindate.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-plaindate.js
new file mode 100644
index 0000000000..07fc4c0423
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-plaindate.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.plaindatetime.prototype.since
+description: Fast path for converting Temporal.PlainDate to Temporal.PlainDateTime by reading internal slots
+info: |
+ sec-temporal.plaindatetime.prototype.since step 3:
+ 3. Set _other_ to ? ToTemporalDateTime(_other_).
+ sec-temporal-totemporaldatetime step 2.b:
+ b. If _item_ has an [[InitializedTemporalDate]] internal slot, then
+ i. Return ? CreateTemporalDateTime(_item_.[[ISOYear]], _item_.[[ISOMonth]], _item_.[[ISODay]], 0, 0, 0, 0, 0, 0, _item_.[[Calendar]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalPlainDateTimeFastPath((date, calendar) => {
+ const datetime = new Temporal.PlainDateTime(2000, 5, 2, 0, 0, 0, 987, 654, 321, calendar);
+ const result = datetime.since(date);
+ assert.sameValue(result.total({ unit: "nanoseconds" }), 987654321, "PlainDate is converted to midnight");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-propertybag-calendar-case-insensitive.js
new file mode 100644
index 0000000000..d35b163b19
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-propertybag-calendar-case-insensitive.js
@@ -0,0 +1,20 @@
+// |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.plaindatetime.prototype.since
+description: The calendar name is case-insensitive
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18);
+
+const calendar = "IsO8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.since(arg);
+TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-propertybag-calendar-leap-second.js
new file mode 100644
index 0000000000..1a344939c7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-propertybag-calendar-leap-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.plaindatetime.prototype.since
+description: Leap second is a valid ISO string for a calendar in a property bag
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18);
+
+const calendar = "2016-12-31T23:59:60";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.since(arg);
+TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "leap second is a valid ISO string for calendar"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-propertybag-calendar-number.js
new file mode 100644
index 0000000000..9c7f65ec05
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-propertybag-calendar-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.plaindatetime.prototype.since
+description: A number as calendar in a property bag is not accepted
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(1976, 11, 18);
+
+const numbers = [
+ 1,
+ 19970327,
+ -19970327,
+ 1234567890,
+];
+for (const calendar of numbers) {
+ const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+ assert.throws(
+ TypeError,
+ () => instance.since(arg),
+ "Numbers cannot be used as a calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-propertybag-calendar-string.js
new file mode 100644
index 0000000000..a4a4e10cd5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-propertybag-calendar-string.js
@@ -0,0 +1,20 @@
+// |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.plaindatetime.prototype.since
+description: A calendar ID is valid input for Calendar
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18);
+
+const calendar = "iso8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.since(arg);
+TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, `Calendar created from string "${calendar}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-propertybag-calendar-wrong-type.js
new file mode 100644
index 0000000000..d3a8e5aba7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-propertybag-calendar-wrong-type.js
@@ -0,0 +1,46 @@
+// |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.plaindatetime.prototype.since
+description: >
+ Appropriate error thrown when a calendar property from a property bag cannot
+ be converted to a calendar object or string
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [calendar, description] of primitiveTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ typeof calendar === 'string' ? RangeError : TypeError,
+ () => instance.since(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+ [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields()
+];
+
+for (const [calendar, description] of typeErrorTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ 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/PlainDateTime/prototype/since/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-propertybag-calendar-year-zero.js
new file mode 100644
index 0000000000..6cd66acaf2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-propertybag-calendar-year-zero.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.plaindatetime.prototype.since
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T17:45",
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+01:00",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.since(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..2fa40a1aee
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-calendar-annotation.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.plaindatetime.prototype.since
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["1976-11-18T15:23[u-ca=iso8601]", "without time zone"],
+ ["1976-11-18T15:23[UTC][u-ca=iso8601]", "with time zone"],
+ ["1976-11-18T15:23[!u-ca=iso8601]", "with ! and no time zone"],
+ ["1976-11-18T15:23[UTC][!u-ca=iso8601]", "with ! and time zone"],
+ ["1976-11-18T15:23[u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 15, 23);
+
+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/PlainDateTime/prototype/since/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..b0b311f210
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-critical-unknown-annotation.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.plaindatetime.prototype.since
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "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.PlainDateTime(2000, 5, 2, 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/PlainDateTime/prototype/since/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..017213c52c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-date-with-utc-offset.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.plaindatetime.prototype.since
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 15, 23);
+
+const validStrings = [
+ "1976-11-18T15:23+00:00",
+ "1976-11-18T15:23+00:00[UTC]",
+ "1976-11-18T15:23+00:00[!UTC]",
+ "1976-11-18T15:23-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 PlainDateTime`
+ );
+}
+
+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 PlainDateTime`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..6f358f3325
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/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.plaindatetime.prototype.since
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[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.PlainDateTime(2000, 5, 2, 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/PlainDateTime/prototype/since/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..c99a4af9c7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-multiple-time-zone.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.plaindatetime.prototype.since
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "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.PlainDateTime(2000, 5, 2, 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/PlainDateTime/prototype/since/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-time-separators.js
new file mode 100644
index 0000000000..0f15f572a4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-time-separators.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.plaindatetime.prototype.since
+description: Time separator in string argument can vary
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["1976-11-18T15:23", "uppercase T"],
+ ["1976-11-18t15:23", "lowercase T"],
+ ["1976-11-18 15:23", "space between date and time"],
+];
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 15, 23);
+
+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/PlainDateTime/prototype/since/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..81a90f173c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-time-zone-annotation.js
@@ -0,0 +1,35 @@
+// |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.plaindatetime.prototype.since
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["1976-11-18T15:23[Asia/Kolkata]", "named, with no offset"],
+ ["1976-11-18T15:23[!Europe/Vienna]", "named, with ! and no offset"],
+ ["1976-11-18T15:23[+00:00]", "numeric, with no offset"],
+ ["1976-11-18T15:23[!-02:30]", "numeric, with ! and no offset"],
+ ["1976-11-18T15:23+00:00[UTC]", "named, with offset"],
+ ["1976-11-18T15:23+00:00[!Africa/Abidjan]", "named, with offset and !"],
+ ["1976-11-18T15:23+00:00[+01:00]", "numeric, with offset"],
+ ["1976-11-18T15:23+00:00[!-08:00]", "numeric, with offset and !"],
+];
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 15, 23);
+
+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/PlainDateTime/prototype/since/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..3960259916
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-unknown-annotation.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.plaindatetime.prototype.since
+description: Various forms of unknown annotation
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["1976-11-18T15:23[foo=bar]", "alone"],
+ ["1976-11-18T15:23[UTC][foo=bar]", "with time zone"],
+ ["1976-11-18T15:23[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["1976-11-18T15:23[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["1976-11-18T15:23[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 15, 23);
+
+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/PlainDateTime/prototype/since/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..cc0b2f2449
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-with-utc-designator.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.plaindatetime.prototype.since
+description: RangeError thrown if a string with UTC designator is used as a PlainDateTime
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "2019-10-01T09:00:00Z",
+ "2019-10-01T09:00:00Z[UTC]",
+];
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 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 PlainDateTime"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string.js
new file mode 100644
index 0000000000..f1d3237a1e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string.js
@@ -0,0 +1,20 @@
+// |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.plaindatetime.prototype.since
+description: Date-like string arguments are acceptable
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dt = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789);
+
+TemporalHelpers.assertDuration(
+ dt.since("2019-10-29T10:46:38.271986102"),
+ 0, 0, 0, -15684, -19, -23, -8, -148, -529, -313,
+ "casts argument (string)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-wrong-type.js
new file mode 100644
index 0000000000..fb55c45f60
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/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.plaindatetime.prototype.since
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainDateTime
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 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.PlainDateTime, "Temporal.PlainDateTime, object"],
+ [Temporal.PlainDateTime.prototype, "Temporal.PlainDateTime.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/PlainDateTime/prototype/since/argument-zoneddatetime-balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-zoneddatetime-balance-negative-time-units.js
new file mode 100644
index 0000000000..803747b39b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/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.plaindatetime.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-totemporaldatetime step 3.b:
+ b. If _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ ...
+ ii. 1. Return ? BuiltinTimeZoneGetPlainDateTimeFor(_item_.[[TimeZone]], _instant_, _item_.[[Calendar]]).
+ sec-temporal.plaindatetime.prototype.since step 3:
+ 3. Set _other_ ? ToTemporalDateTime(_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.PlainDateTime(1970, 1, 1).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/PlainDateTime/prototype/since/argument-zoneddatetime-negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-zoneddatetime-negative-epochnanoseconds.js
new file mode 100644
index 0000000000..a128e5820b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/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.plaindatetime.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.PlainDateTime(2000, 5, 2, 15, 30, 45, 987, 654, 321);
+const result = instance.since(datetime);
+TemporalHelpers.assertDuration(result, 0, 0, 0, 11239, 22, 40, 10, 987, 654, 320);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..76738f0272
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/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.plaindatetime.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 plain = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ const zoned = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => plain.since(zoned));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..1fd51aa6b3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/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.plaindatetime.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 plain = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ const zoned = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => plain.since(zoned),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..0838e28752
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/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.plaindatetime.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 plain = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ const zoned = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => plain.since(zoned));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..c9fcd949d7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/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.plaindatetime.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 plain = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ const zoned = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => plain.since(zoned));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/balance-negative-duration.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/balance-negative-duration.js
new file mode 100644
index 0000000000..901ede1927
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/balance-negative-duration.js
@@ -0,0 +1,37 @@
+// |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.plaindatetime.prototype.since
+description: Negative durations are balanced correctly by the modulo operation in NanosecondsToDays
+info: |
+ sec-temporal-nanosecondstodays step 6:
+ 6. If Type(_relativeTo_) is not Object or _relativeTo_ does not have an [[InitializedTemporalZonedDateTime]] internal slot, then
+ a. Return the new Record { ..., [[Nanoseconds]]: abs(_nanoseconds_) modulo _dayLengthNs_ × _sign_, ... }.
+ sec-temporal-balanceduration step 4:
+ 4. If _largestUnit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*, then
+ a. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _relativeTo_).
+ sec-temporal-differenceisodatetime steps 7 and 13:
+ 7. If _timeSign_ is -_dateSign_, then
+ ...
+ b. Set _timeDifference_ to ? BalanceDuration(-_timeSign_, _timeDifference_.[[Hours]], _timeDifference_.[[Minutes]], _timeDifference_.[[Seconds]], _timeDifference_.[[Milliseconds]], _timeDifference_.[[Microseconds]], _timeDifference_.[[Nanoseconds]], _largestUnit_).
+ ...
+ 16. Return ? BalanceDuration(_dateDifference_.[[Years]], _dateDifference_.[[Months]], _dateDifference_.[[Weeks]], _dateDifference_.[[Days]], _timeDifference_.[[Hours]], _timeDifference_.[[Minutes]], _timeDifference_.[[Seconds]], _timeDifference_.[[Milliseconds]], _timeDifference_.[[Microseconds]], _timeDifference_.[[Nanoseconds]], _largestUnit_).
+ sec-temporal.plaindatetime.prototype.since step 14:
+ 14. Let _diff_ be ? DifferenceISODateTime(_other_.[[ISOYear]], _other_.[[ISOMonth]], _other_.[[ISODay]], _other_.[[ISOHour]], _other_.[[ISOMinute]], _other_.[[ISOSecond]], _other_.[[ISOMillisecond]], _other_.[[ISOMicrosecond]], _other_.[[ISONanosecond]], _dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], _dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], _dateTime_.[[Calendar]], _largestUnit_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier1 = new Temporal.PlainDateTime(2000, 5, 2, 9);
+const later1 = new Temporal.PlainDateTime(2000, 5, 5, 10);
+const result1 = later1.since(earlier1, { largestUnit: 'day' });
+TemporalHelpers.assertDuration(result1, 0, 0, 0, 3, 1, 0, 0, 0, 0, 0, "date sign == time sign");
+
+const earlier2 = new Temporal.PlainDateTime(2000, 5, 2, 10);
+const later2 = new Temporal.PlainDateTime(2000, 5, 5, 9);
+const result2 = later2.since(earlier2, { largestUnit: 'day' });
+TemporalHelpers.assertDuration(result2, 0, 0, 0, 2, 23, 0, 0, 0, 0, 0, "date sign != time sign");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/balance-negative-time-units.js
new file mode 100644
index 0000000000..d4f1df6ea2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/balance-negative-time-units.js
@@ -0,0 +1,53 @@
+// |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.plaindatetime.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-differenceisodatetime step 2:
+ 2. Let _timeDifference_ be ? DifferenceTime(_h1_, _min1_, _s1_, _ms1_, _mus1_, _ns1_, _h2_, _min2_, _s2_, _ms2_, _mus2_, _ns2_).
+ sec-temporal.plaindatetime.prototype.since step 14:
+ 14. Let _diff_ be ? DifferenceISODateTime(_other_.[[ISOYear]], _other_.[[ISOMonth]], _other_.[[ISODay]], _other_.[[ISOHour]], _other_.[[ISOMinute]], _other_.[[ISOSecond]], _other_.[[ISOMillisecond]], _other_.[[ISOMicrosecond]], _other_.[[ISONanosecond]], _dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], _dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], _dateTime_.[[Calendar]], _largestUnit_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(1996, 5, 2, 1, 1, 1, 1, 1, 1);
+
+const result1 = datetime.since(new Temporal.PlainDateTime(1996, 5, 2, 0, 0, 0, 0, 0, 2));
+TemporalHelpers.assertDuration(result1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 999, "nanoseconds balance");
+
+const result2 = datetime.since(new Temporal.PlainDateTime(1996, 5, 2, 0, 0, 0, 0, 2));
+TemporalHelpers.assertDuration(result2, 0, 0, 0, 0, 1, 1, 1, 0, 999, 1, "microseconds balance");
+
+const result3 = datetime.since(new Temporal.PlainDateTime(1996, 5, 2, 0, 0, 0, 2));
+TemporalHelpers.assertDuration(result3, 0, 0, 0, 0, 1, 1, 0, 999, 1, 1, "milliseconds balance");
+
+const result4 = datetime.since(new Temporal.PlainDateTime(1996, 5, 2, 0, 0, 2));
+TemporalHelpers.assertDuration(result4, 0, 0, 0, 0, 1, 0, 59, 1, 1, 1, "seconds balance");
+
+const result5 = datetime.since(new Temporal.PlainDateTime(1996, 5, 2, 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 = datetime.since(new Temporal.PlainDateTime(1996, 5, 2, 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/PlainDateTime/prototype/since/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/branding.js
new file mode 100644
index 0000000000..e5d37d2d5b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/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.plaindatetime.prototype.since
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const since = Temporal.PlainDateTime.prototype.since;
+
+assert.sameValue(typeof since, "function");
+
+const args = [new Temporal.PlainDateTime(2022, 6, 22)];
+
+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.PlainDateTime, args), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => since.apply(Temporal.PlainDateTime.prototype, args), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..8645aec4f9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plaindatetime.prototype.since
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dateUntilOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dateUntil");
+Object.defineProperty(Temporal.Calendar.prototype, "dateUntil", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("dateUntil should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, "iso8601");
+instance.since(new Temporal.PlainDateTime(1999, 4, 1));
+
+Object.defineProperty(Temporal.Calendar.prototype, "dateUntil", dateUntilOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/builtin.js
new file mode 100644
index 0000000000..b559811c5a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/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.plaindatetime.prototype.since
+description: >
+ Tests that Temporal.PlainDateTime.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.PlainDateTime.prototype.since),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.prototype.since),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.prototype.since),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.prototype.since.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/calendar-dateadd-called-with-plaindate-instance.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/calendar-dateadd-called-with-plaindate-instance.js
new file mode 100644
index 0000000000..e50a5121fa
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/calendar-dateadd-called-with-plaindate-instance.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.plaindatetime.prototype.since
+description: >
+ relativeTo parameters that are not ZonedDateTime or undefined, are always
+ converted to PlainDate for observable calendar calls
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarDateAddPlainDateInstance();
+const instance = new Temporal.PlainDateTime(1970, 1, 1, 0, 0, 0, 0, 0, 0, calendar);
+instance.since(new Temporal.PlainDateTime(2000, 5, 2, 0, 0, 0, 0, 0, 0, calendar), { smallestUnit: "month" });
+assert(calendar.dateAddCallCount > 0, "assertions in calendar.dateAdd() should have been tested");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/calendar-datefromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..c7e9451467
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/calendar-datefromfields-called-with-null-prototype-fields.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.plaindatetime.prototype.since
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const arg = { year: 2000, month: 5, day: 2, calendar };
+instance.since(arg);
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/calendar-dateuntil-called-with-copy-of-options.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/calendar-dateuntil-called-with-copy-of-options.js
new file mode 100644
index 0000000000..25a93951fb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/calendar-dateuntil-called-with-copy-of-options.js
@@ -0,0 +1,35 @@
+// |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.plaindatetime.prototype.since
+description: The dateUntil() method on the calendar is called with a copy of the options bag
+features: [Temporal]
+---*/
+
+const originalOptions = {
+ largestUnit: "year",
+ shouldBeCopied: {},
+};
+let called = false;
+
+class Calendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+
+ dateUntil(d1, d2, options) {
+ called = true;
+ assert.notSameValue(options, originalOptions, "options bag should be a copy");
+ assert.sameValue(options.shouldBeCopied, originalOptions.shouldBeCopied, "options bag should be a shallow copy");
+ return new Temporal.Duration();
+ }
+}
+const calendar = new Calendar();
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+const later = new Temporal.PlainDateTime(2001, 6, 3, 13, 35, 57, 988, 655, 322, calendar);
+earlier.since(later, originalOptions);
+assert(called, "calendar.dateUntil must be called");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/calendar-dateuntil-called-with-null-prototype-options.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/calendar-dateuntil-called-with-null-prototype-options.js
new file mode 100644
index 0000000000..58d5037862
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/calendar-dateuntil-called-with-null-prototype-options.js
@@ -0,0 +1,20 @@
+// |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.plaindatetime.prototype.since
+description: >
+ Calendar.dateUntil method is called with a null-prototype object as the
+ options value when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckOptionsPrototypePollution();
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+const argument = new Temporal.PlainDateTime(2022, 6, 14, 18, 21, 36, 660, 690, 387, calendar);
+instance.since(argument, { largestUnit: "months" });
+assert.sameValue(calendar.dateUntilCallCount, 1, "dateUntil should have been called on the calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/calendar-dateuntil-called-with-plaindate-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/calendar-dateuntil-called-with-plaindate-calendar.js
new file mode 100644
index 0000000000..cdb2988218
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/calendar-dateuntil-called-with-plaindate-calendar.js
@@ -0,0 +1,34 @@
+// |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.plaindatetime.prototype.since
+description: calendar.dateUntil() is passed PlainDate objects with the receiver's calendar
+info: |
+ DifferenceISODateTime ( y1, mon1, d1, h1, min1, s1, ms1, mus1, ns1, y2, mon2, d2, h2, min2, s2, ms2, mus2, ns2, calendar, largestUnit [ , options ] )
+
+ 8. Let _date1_ be ? CreateTemporalDate(_balanceResult_.[[Year]], _balanceResult_.[[Month]], _balanceResult_.[[Day]], _calendar_).
+ 9. Let _date2_ be ? CreateTemporalDate(_y2_, _mon2_, _d2_, _calendar_).
+ 12. Let _dateDifference_ be ? CalendarDateUntil(_calendar_, _date1_, _date2_, _untilOptions_).
+features: [Temporal]
+---*/
+
+class Calendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+
+ dateUntil(d1, d2) {
+ assert.sameValue(d1.getCalendar(), this, "d1.calendar");
+ assert.sameValue(d2.getCalendar(), this, "d2.calendar");
+ return new Temporal.Duration();
+ }
+}
+const calendar = new Calendar();
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+const later = new Temporal.PlainDateTime(2001, 6, 3, 13, 35, 57, 988, 655, 322, calendar);
+const result = earlier.since(later);
+assert(result instanceof Temporal.Duration, "result");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/calendar-dateuntil-called-with-singular-largestunit.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/calendar-dateuntil-called-with-singular-largestunit.js
new file mode 100644
index 0000000000..fbcd184fc1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/calendar-dateuntil-called-with-singular-largestunit.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.plaindatetime.prototype.since
+description: The options object passed to calendar.dateUntil has a largestUnit property with its value in the singular form
+info: |
+ sec-temporal.plaindatetime.prototype.since step 14:
+ 14. Let _diff_ be ? DifferenceISODateTime(_other_.[[ISOYear]], _other_.[[ISOMonth]], _other_.[[ISODay]], _other_.[[ISOHour]], _other_.[[ISOMinute]], _other_.[[ISOSecond]], _other_.[[ISOMillisecond]], _other_.[[ISOMicrosecond]], _other_.[[ISONanosecond]], _dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], _dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], _dateTime_.[[Calendar]], _largestUnit_, _options_).
+ sec-temporal-differenceisodatetime steps 9–11:
+ 9. Let _dateLargestUnit_ be ! LargerOfTwoTemporalUnits(*"day"*, _largestUnit_).
+ 10. Let _untilOptions_ be ? MergeLargestUnitOption(_options_, _dateLargestUnit_).
+ 11. Let _dateDifference_ be ? CalendarDateUntil(_calendar_, _date1_, _date2_, _untilOptions_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkCalendarDateUntilLargestUnitSingular(
+ (calendar, largestUnit) => {
+ const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+ const later = new Temporal.PlainDateTime(2001, 6, 3, 13, 35, 57, 988, 655, 322, calendar);
+ later.since(earlier, { largestUnit });
+ },
+ {
+ years: ["year"],
+ months: ["month"],
+ weeks: ["week"],
+ days: [],
+ hours: [],
+ minutes: [],
+ seconds: [],
+ milliseconds: [],
+ microseconds: [],
+ nanoseconds: []
+ }
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/calendar-fields-iterable.js
new file mode 100644
index 0000000000..36e4e4ee78
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/calendar-fields-iterable.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.plaindatetime.prototype.since
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plaindatetime.prototype.since step 3:
+ 3. Set _other_ to ? ToTemporalDateTime(_other_).
+ sec-temporal-totemporaldatetime step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"hour"*, *"microsecond"*, *"millisecond"*, *"minute"*, *"month"*, *"monthCode"*, *"nanosecond"*, *"second"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar1);
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+datetime.since({ year: 2005, month: 6, day: 2, calendar: calendar2 });
+
+assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/calendar-temporal-object.js
new file mode 100644
index 0000000000..6a264c5b07
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/calendar-temporal-object.js
@@ -0,0 +1,29 @@
+// |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.plaindatetime.prototype.since
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plaindatetime.prototype.since step 3:
+ 3. Set _other_ to ? ToTemporalDateTime(_other_).
+ sec-temporal-totemporaldatetime step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, temporalObject);
+ datetime.since({ year: 2005, month: 6, day: 2, calendar: temporalObject });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..051d7f7fce
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/constructor-in-calendar-fields.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.plaindatetime.prototype.since
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+assert.throws(RangeError, () => instance.since(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/different-calendars-throws.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/different-calendars-throws.js
new file mode 100644
index 0000000000..3ea7e14b34
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/different-calendars-throws.js
@@ -0,0 +1,42 @@
+// |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.plaindatetime.prototype.since
+description: Fail if the argument is a PlainDateTime with a different calendar
+features: [Temporal]
+---*/
+
+const dt1 = new Temporal.PlainDateTime(2000, 1, 1, 0, 0, 0, 0, 0, 0);
+const dt2 = new Temporal.PlainDateTime(2000, 1, 1, 0, 0, 0, 0, 0, 0, {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "custom",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+});
+
+assert.throws(
+ RangeError,
+ () => dt1.since(dt2),
+ "different calendars not allowed"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/duplicate-calendar-fields.js
new file mode 100644
index 0000000000..5a714170e6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/duplicate-calendar-fields.js
@@ -0,0 +1,19 @@
+// |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.plaindatetime.prototype.since
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['day'], ['hour'], ['microsecond'], ['millisecond'], ['minute'], ['month'], ['monthCode'], ['nanosecond'], ['second'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar };
+ const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+ assert.throws(RangeError, () => instance.since(arg));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..1845f4ccc2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/infinity-throws-rangeerror.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.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plaindatetime.prototype.since
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 15);
+const base = { year: 2000, month: 5, day: 2, hour: 15, minute: 30, second: 45, millisecond: 987, microsecond: 654, nanosecond: 321 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.since({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.since({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/largestunit-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/largestunit-invalid-string.js
new file mode 100644
index 0000000000..e67796f959
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/largestunit-invalid-string.js
@@ -0,0 +1,31 @@
+// |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.plaindatetime.prototype.since
+description: RangeError thrown when largestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2001, 6, 3, 13, 35, 57, 987, 654, 321);
+const badValues = [
+ "era",
+ "eraYear",
+ "millisecond\0",
+ "mill\u0131second",
+ "SECOND",
+ "eras",
+ "eraYears",
+ "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/PlainDateTime/prototype/since/largestunit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/largestunit-plurals-accepted.js
new file mode 100644
index 0000000000..df1ef728cc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/largestunit-plurals-accepted.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.plaindatetime.prototype.since
+description: Plural units are accepted as well for the largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const later = new Temporal.PlainDateTime(2001, 6, 12, 13, 35, 57, 988, 655, 322);
+const validUnits = [
+ "year",
+ "month",
+ "week",
+ "day",
+ "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/PlainDateTime/prototype/since/largestunit-smallestunit-mismatch.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/largestunit-smallestunit-mismatch.js
new file mode 100644
index 0000000000..4b902d0f69
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/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.plaindatetime.prototype.since
+description: RangeError thrown when smallestUnit is larger than largestUnit
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2001, 6, 3, 13, 35, 57, 987, 654, 321);
+const units = ["years", "months", "weeks", "days", "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/PlainDateTime/prototype/since/largestunit-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/largestunit-undefined.js
new file mode 100644
index 0000000000..780b25118f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/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.plaindatetime.prototype.since
+description: Fallback value for largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2001, 6, 3, 13, 35, 57, 987, 654, 321);
+
+const explicit = later.since(earlier, { largestUnit: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 397, 1, 1, 1, 987, 654, 321, "default largestUnit is day");
+const implicit = later.since(earlier, {});
+TemporalHelpers.assertDuration(implicit, 0, 0, 0, 397, 1, 1, 1, 987, 654, 321, "default largestUnit is day");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/largestunit-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/largestunit-wrong-type.js
new file mode 100644
index 0000000000..12f194cd64
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/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.plaindatetime.prototype.since
+description: Type conversions for largestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2001, 6, 3, 13, 35, 57, 987, 654, 321);
+TemporalHelpers.checkStringOptionWrongType("largestUnit", "year",
+ (largestUnit) => later.since(earlier, { largestUnit }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 1, 1, 0, 1, 1, 1, 1, 987, 654, 321, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/largestunit.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/largestunit.js
new file mode 100644
index 0000000000..852a7b8068
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/largestunit.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.plaindatetime.prototype.since
+description: Specify behavior of PlainDateTime.since when largest specified unit is years or months.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+const lastFeb20 = new Temporal.PlainDateTime(2020, 2, 20, 5, 45, 20);
+const lastFeb21 = new Temporal.PlainDateTime(2021, 2, 21, 17, 18, 57);
+TemporalHelpers.assertDuration(lastFeb21.since(lastFeb20), 0, 0, 0, 367, 11, 33, 37, 0, 0, 0, 'does not include higher units than necessary (largest unit unspecified)');
+TemporalHelpers.assertDuration(lastFeb21.since(lastFeb20, { largestUnit: 'months' }), 0, 12, 0, 1, 11, 33, 37, 0, 0, 0, 'does not include higher units than necessary (largest unit is months)');
+TemporalHelpers.assertDuration(lastFeb21.since(lastFeb20, { largestUnit: 'years' }), 1, 0, 0, 1, 11, 33, 37, 0, 0, 0, 'does not include higher units than necessary (largest unit is years)');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/leap-second.js
new file mode 100644
index 0000000000..62e2963d22
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/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.plaindatetime.prototype.since
+description: Leap second is a valid ISO string for PlainDateTime
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2016, 12, 31, 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 PlainDateTime"
+);
+
+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 PlainDateTime"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/length.js
new file mode 100644
index 0000000000..944b5458a9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/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.plaindatetime.prototype.since
+description: Temporal.PlainDateTime.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.PlainDateTime.prototype.since, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/name.js
new file mode 100644
index 0000000000..eb45ee1e00
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/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.plaindatetime.prototype.since
+description: Temporal.PlainDateTime.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.PlainDateTime.prototype.since, "name", {
+ value: "since",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/no-unnecessary-units.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/no-unnecessary-units.js
new file mode 100644
index 0000000000..62ec2d003e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/no-unnecessary-units.js
@@ -0,0 +1,33 @@
+// |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.plaindatetime.prototype.since
+description: Do not return Durations with unnecessary units
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const feb2 = new Temporal.PlainDateTime(2020, 2, 2, 0, 0);
+const feb28 = new Temporal.PlainDateTime(2021, 2, 28, 0, 0);
+
+TemporalHelpers.assertDuration(
+ feb28.since(feb2),
+ 0, 0, 0, 392, 0, 0, 0, 0, 0, 0,
+ "does not include higher units than necessary (no largest unit)"
+);
+
+TemporalHelpers.assertDuration(
+ feb28.since(feb2, { largestUnit: "months" }),
+ 0, 12, 0, 26, 0, 0, 0, 0, 0, 0,
+ "does not include higher units than necessary (largest unit = months)"
+);
+
+TemporalHelpers.assertDuration(
+ feb28.since(feb2, { largestUnit: "years" }),
+ 1, 0, 0, 26, 0, 0, 0, 0, 0, 0,
+ "does not include higher units than necessary (largest unit = years)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/not-a-constructor.js
new file mode 100644
index 0000000000..e1288d502d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/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.plaindatetime.prototype.since
+description: >
+ Temporal.PlainDateTime.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.PlainDateTime.prototype.since();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.prototype.since), false,
+ "isConstructor(Temporal.PlainDateTime.prototype.since)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/options-empty.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/options-empty.js
new file mode 100644
index 0000000000..8044a80ed1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/options-empty.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.plaindatetime.prototype.since
+description: Empty objects are acceptable
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const feb20 = new Temporal.PlainDateTime(2020, 2, 1, 0, 0);
+const feb21 = new Temporal.PlainDateTime(2021, 2, 1, 0, 0);
+
+TemporalHelpers.assertDuration(
+ feb21.since(feb20, {}),
+ 0, 0, 0, 366, 0, 0, 0, 0, 0, 0,
+ "empty plain object options"
+);
+
+TemporalHelpers.assertDuration(
+ feb21.since(feb20, () => {}),
+ 0, 0, 0, 366, 0, 0, 0, 0, 0, 0,
+ "empty function object options"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/options-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/options-invalid.js
new file mode 100644
index 0000000000..8e410662d2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/options-invalid.js
@@ -0,0 +1,23 @@
+// |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.plaindatetime.prototype.since
+description: A variety of bad options (type error thrown)
+features: [Temporal, Symbol]
+---*/
+
+const feb20 = new Temporal.PlainDateTime(2020, 2, 1, 0, 0);
+const feb21 = new Temporal.PlainDateTime(2021, 2, 1, 0, 0);
+
+const badOptions = [null, 1, 'hello', true, Symbol('foo'), 1n];
+badOptions.forEach((bad) => {
+ assert.throws(
+ TypeError,
+ () => feb21.since(feb20, bad),
+ `bad options (${typeof bad})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/options-undefined.js
new file mode 100644
index 0000000000..a5334b31d6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/options-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.plaindatetime.prototype.since
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const later = new Temporal.PlainDateTime(2000, 6, 12, 12, 34, 56, 987, 654, 322);
+
+const explicit = later.since(earlier, undefined);
+assert.sameValue(explicit.years, 0, "default largest unit is days");
+assert.sameValue(explicit.months, 0, "default largest unit is days");
+assert.sameValue(explicit.weeks, 0, "default largest unit is days");
+assert.sameValue(explicit.days, 41, "default largest unit is days");
+assert.sameValue(explicit.nanoseconds, 1, "default smallest unit is nanoseconds and no rounding");
+
+const implicit = later.since(earlier);
+assert.sameValue(implicit.years, 0, "default largest unit is days");
+assert.sameValue(implicit.months, 0, "default largest unit is days");
+assert.sameValue(implicit.weeks, 0, "default largest unit is days");
+assert.sameValue(implicit.days, 41, "default largest unit is days");
+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/PlainDateTime/prototype/since/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/options-wrong-type.js
new file mode 100644
index 0000000000..4a19f0ef5f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/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.plaindatetime.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.PlainDateTime(2000, 5, 2);
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.since(new Temporal.PlainDateTime(1976, 11, 18), value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/order-of-operations.js
new file mode 100644
index 0000000000..aaeca20a9b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/order-of-operations.js
@@ -0,0 +1,250 @@
+// |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.plaindatetime.prototype.since
+description: Properties on objects passed to since() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ // ToTemporalDateTime
+ "get other.calendar",
+ "has other.calendar.dateAdd",
+ "has other.calendar.dateFromFields",
+ "has other.calendar.dateUntil",
+ "has other.calendar.day",
+ "has other.calendar.dayOfWeek",
+ "has other.calendar.dayOfYear",
+ "has other.calendar.daysInMonth",
+ "has other.calendar.daysInWeek",
+ "has other.calendar.daysInYear",
+ "has other.calendar.fields",
+ "has other.calendar.id",
+ "has other.calendar.inLeapYear",
+ "has other.calendar.mergeFields",
+ "has other.calendar.month",
+ "has other.calendar.monthCode",
+ "has other.calendar.monthDayFromFields",
+ "has other.calendar.monthsInYear",
+ "has other.calendar.weekOfYear",
+ "has other.calendar.year",
+ "has other.calendar.yearMonthFromFields",
+ "has other.calendar.yearOfWeek",
+ "get other.calendar.dateFromFields",
+ "get other.calendar.fields",
+ "call other.calendar.fields",
+ "get other.day",
+ "get other.day.valueOf",
+ "call other.day.valueOf",
+ "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.month",
+ "get other.month.valueOf",
+ "call other.month.valueOf",
+ "get other.monthCode",
+ "get other.monthCode.toString",
+ "call other.monthCode.toString",
+ "get other.nanosecond",
+ "get other.nanosecond.valueOf",
+ "call other.nanosecond.valueOf",
+ "get other.second",
+ "get other.second.valueOf",
+ "call other.second.valueOf",
+ "get other.year",
+ "get other.year.valueOf",
+ "call other.year.valueOf",
+ "call other.calendar.dateFromFields",
+ // CalendarEquals
+ "get this.calendar.id",
+ "get other.calendar.id",
+ // 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 ownCalendar = TemporalHelpers.calendarObserver(actual, "this.calendar");
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, ownCalendar);
+
+const otherDateTimePropertyBag = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2001,
+ month: 6,
+ monthCode: "M06",
+ day: 2,
+ hour: 1,
+ minute: 46,
+ second: 40,
+ millisecond: 250,
+ microsecond: 500,
+ nanosecond: 750,
+ calendar: TemporalHelpers.calendarObserver(actual, "other.calendar"),
+}, "other");
+
+function createOptionsObserver({ smallestUnit = "nanoseconds", largestUnit = "auto", roundingMode = "halfExpand", roundingIncrement = 1 } = {}) {
+ return TemporalHelpers.propertyBagObserver(actual, {
+ // order is significant, due to iterating through properties in order to
+ // copy them to an internal null-prototype object:
+ roundingIncrement,
+ roundingMode,
+ largestUnit,
+ smallestUnit,
+ additional: "property",
+ }, "options");
+}
+
+// clear any observable things that happened while constructing the objects
+actual.splice(0);
+
+// basic order of observable operations with calendar call, without rounding:
+instance.since(otherDateTimePropertyBag, createOptionsObserver({ largestUnit: "years" }));
+assert.compareArray(actual, expected.concat([
+ // lookup
+ "get this.calendar.dateAdd",
+ "get this.calendar.dateUntil",
+ // CalendarDateUntil
+ "call this.calendar.dateUntil",
+]), "order of operations");
+actual.splice(0); // clear
+
+// short-circuit for identical objects:
+const identicalPropertyBag = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2000,
+ month: 5,
+ monthCode: "M05",
+ day: 2,
+ hour: 12,
+ minute: 34,
+ second: 56,
+ millisecond: 987,
+ microsecond: 654,
+ nanosecond: 321,
+ calendar: TemporalHelpers.calendarObserver(actual, "other.calendar"),
+}, "other");
+
+instance.since(identicalPropertyBag, createOptionsObserver());
+assert.compareArray(actual, expected, "order of operations with identical datetimes");
+actual.splice(0); // clear
+
+// code path through RoundDuration that rounds to the nearest year:
+const expectedOpsForYearRounding = expected.concat([
+ // lookup
+ "get this.calendar.dateAdd",
+ "get this.calendar.dateUntil",
+ // CalendarDateUntil
+ "call this.calendar.dateUntil",
+ // RoundDuration
+ "call this.calendar.dateAdd", // 12.d
+ "call this.calendar.dateAdd", // 12.f
+ "call this.calendar.dateUntil", // 12.n
+ "call this.calendar.dateAdd", // 12.x MoveRelativeDate
+ // (12.r not called because other units can't add up to >1 year at this point)
+ // BalanceDateDurationRelative
+ "call this.calendar.dateAdd", // 9.c
+ "call this.calendar.dateUntil" // 9.d
+]);
+instance.since(otherDateTimePropertyBag, createOptionsObserver({ smallestUnit: "years" }));
+assert.compareArray(actual, expectedOpsForYearRounding, "order of operations with smallestUnit = years");
+actual.splice(0); // clear
+
+// code path through RoundDuration that rounds to the nearest year and skips a DateUntil call:
+const otherDatePropertyBagSameMonth = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2001,
+ month: 5,
+ monthCode: "M05",
+ day: 2,
+ hour: 12,
+ minute: 34,
+ second: 56,
+ millisecond: 987,
+ microsecond: 654,
+ nanosecond: 321,
+ calendar: TemporalHelpers.calendarObserver(actual, "other.calendar"),
+}, "other");
+const expectedOpsForYearRoundingSameMonth = expected.concat([
+ // lookup
+ "get this.calendar.dateAdd",
+ "get this.calendar.dateUntil",
+ // CalendarDateUntil
+ "call this.calendar.dateUntil",
+ // RoundDuration
+ "call this.calendar.dateAdd", // 12.d
+ "call this.calendar.dateAdd", // 12.f
+ "call this.calendar.dateAdd", // 12.x MoveRelativeDate
+ // (12.n not called because months and weeks == 0)
+ // BalanceDateDurationRelative
+ "call this.calendar.dateAdd", // 9.c
+ "call this.calendar.dateUntil" // 9.d
+]);
+instance.until(otherDatePropertyBagSameMonth, createOptionsObserver({ smallestUnit: "years" }));
+assert.compareArray(actual, expectedOpsForYearRoundingSameMonth, "order of operations with smallestUnit = years and no excess months/weeks");
+actual.splice(0); // clear
+
+// code path through RoundDuration that rounds to the nearest month:
+const expectedOpsForMonthRounding = expected.concat([
+ // lookup
+ "get this.calendar.dateAdd",
+ "get this.calendar.dateUntil",
+ // CalendarDateUntil
+ "call this.calendar.dateUntil",
+ // RoundDuration
+ "call this.calendar.dateAdd", // 13.c
+ "call this.calendar.dateAdd", // 13.e
+ "call this.calendar.dateUntil", // 13.m
+ "call this.calendar.dateAdd", // 13.w MoveRelativeDate
+ // BalanceDateDurationRelative
+ "call this.calendar.dateAdd", // 10.d
+ "call this.calendar.dateUntil", // 10.e
+]);
+instance.since(otherDateTimePropertyBag, createOptionsObserver({ smallestUnit: "months" }));
+assert.compareArray(actual, expectedOpsForMonthRounding, "order of operations with smallestUnit = months");
+actual.splice(0); // clear
+
+// code path through RoundDuration that rounds to the nearest week:
+const expectedOpsForWeekRounding = expected.concat([
+ // lookup
+ "get this.calendar.dateAdd",
+ "get this.calendar.dateUntil",
+ // CalendarDateUntil
+ "call this.calendar.dateUntil",
+ // RoundDuration
+ "call this.calendar.dateUntil", // 14.f
+ "call this.calendar.dateAdd", // 14.p MoveRelativeDate
+ // BalanceDateDurationRelative
+ "call this.calendar.dateAdd", // 16
+ "call this.calendar.dateUntil", // 17
+]);
+instance.since(otherDateTimePropertyBag, createOptionsObserver({ smallestUnit: "weeks" }));
+assert.compareArray(actual, expectedOpsForWeekRounding, "order of operations with smallestUnit = weeks");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/prop-desc.js
new file mode 100644
index 0000000000..a6cdb6bb4b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/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.plaindatetime.prototype.since
+description: The "since" property of Temporal.PlainDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.prototype.since,
+ "function",
+ "`typeof PlainDateTime.prototype.since` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime.prototype, "since", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/proto-in-calendar-fields.js
new file mode 100644
index 0000000000..4c82a08a7a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/proto-in-calendar-fields.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.plaindatetime.prototype.since
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+assert.throws(RangeError, () => instance.since(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/read-time-fields-before-datefromfields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/read-time-fields-before-datefromfields.js
new file mode 100644
index 0000000000..5aa93cd273
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/read-time-fields-before-datefromfields.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.plaindatetime.prototype.since
+description: The time fields are read from the object before being passed to dateFromFields().
+info: |
+ sec-temporal.plaindatetime.prototype.since step 3:
+ 3. Set _other_ to ? ToTemporalDateTime(_other_).
+ sec-temporal-totemporaldatetime step 2.e:
+ e. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+ sec-temporal-interprettemporaldatetimefields steps 1–2:
+ 1. Let _timeResult_ be ? ToTemporalTimeRecord(_fields_).
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarMakeInfinityTime();
+const datetime = new Temporal.PlainDateTime(2021, 3, 31, 12, 34, 56, 987, 654, 321);
+const duration = datetime.since({ year: 2021, month: 3, day: 31, calendar });
+
+TemporalHelpers.assertDuration(duration, 0, 0, 0, 0, 12, 34, 56, 987, 654, 321);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/returns-days.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/returns-days.js
new file mode 100644
index 0000000000..ce6ac16027
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/returns-days.js
@@ -0,0 +1,47 @@
+// |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.plaindatetime.prototype.since
+description: Days are the default level of specificity
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const feb_1_2020 = new Temporal.PlainDateTime(2020, 2, 1, 0, 0);
+const feb_1_2021 = new Temporal.PlainDateTime(2021, 2, 1, 0, 0);
+
+TemporalHelpers.assertDuration(
+ feb_1_2021.since(feb_1_2020),
+ 0, 0, 0, 366, 0, 0, 0, 0, 0, 0,
+ "defaults to returning days (no options)"
+);
+
+TemporalHelpers.assertDuration(
+ feb_1_2021.since(feb_1_2020, { largestUnit: "auto" }),
+ 0, 0, 0, 366, 0, 0, 0, 0, 0, 0,
+ "defaults to returning days (largest unit = auto)"
+);
+
+TemporalHelpers.assertDuration(
+ feb_1_2021.since(feb_1_2020, { largestUnit: "days" }),
+ 0, 0, 0, 366, 0, 0, 0, 0, 0, 0,
+ "defaults to returning days (largest unit = days)"
+);
+
+const dt = new Temporal.PlainDateTime(2020, 2, 1, 0, 0, 0, 0, 0, 1);
+
+TemporalHelpers.assertDuration(
+ dt.since(feb_1_2020),
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
+ "defaults to returning days (nanosecond)"
+);
+
+TemporalHelpers.assertDuration(
+ feb_1_2021.since(dt),
+ 0, 0, 0, 365, 23, 59, 59, 999, 999, 999,
+ "defaults to returning days (nanosecond)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/round-cross-unit-boundary.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/round-cross-unit-boundary.js
new file mode 100644
index 0000000000..5ce0254b04
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/round-cross-unit-boundary.js
@@ -0,0 +1,36 @@
+// |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.plaindatetime.prototype.since
+description: Rounding can cross unit boundaries up to largestUnit
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+// Date units
+{
+ const earlier = new Temporal.PlainDateTime(2022, 1, 1);
+ const later = new Temporal.PlainDateTime(2023, 12, 25);
+ const duration = earlier.since(later, { largestUnit: "years", smallestUnit: "months", roundingMode: "expand" });
+ TemporalHelpers.assertDuration(duration, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, "-1 year -11 months balances to -2 years");
+}
+
+// Time units
+{
+ const earlier = new Temporal.PlainDateTime(2000, 5, 2);
+ const later = new Temporal.PlainDateTime(2000, 5, 2, 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:59 balances to -2 hours");
+}
+
+// Both
+{
+ const earlier = new Temporal.PlainDateTime(1970, 1, 1);
+ const later = new Temporal.PlainDateTime(1971, 12, 31, 23, 59, 59, 999, 999, 999);
+ const duration = earlier.since(later, { largestUnit: "years", smallestUnit: "microseconds", roundingMode: "expand" });
+ TemporalHelpers.assertDuration(duration, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, "rounding down 1 ns balances to -2 years");
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/round-negative-duration.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/round-negative-duration.js
new file mode 100644
index 0000000000..33ded673ff
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/round-negative-duration.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.plaindatetime.prototype.since
+description: Negative durations are rounded correctly by the modulo operation in NanosecondsToDays
+info: |
+ sec-temporal-nanosecondstodays step 6:
+ 6. If Type(_relativeTo_) is not Object or _relativeTo_ does not have an [[InitializedTemporalZonedDateTime]] internal slot, then
+ a. Return the new Record { ..., [[Nanoseconds]]: abs(_nanoseconds_) modulo _dayLengthNs_ × _sign_, ... }.
+ sec-temporal-roundduration step 6:
+ 6. If _unit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*, then
+ ...
+ d. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _intermediate_).
+ sec-temporal.plaindatetime.prototype.since step 15:
+ 15. Let _roundResult_ be ? RoundDuration(−_diff_.[[Years]], −_diff_.[[Months]], −_diff_.[[Weeks]], −_diff_.[[Days]], −_diff_.[[Hours]], −_diff_.[[Minutes]], −_diff_.[[Seconds]], −_diff_.[[Milliseconds]], −_diff_.[[Microseconds]], −_diff_.[[Nanoseconds]], _roundingIncrement_, _smallestUnit_, _roundingMode_, _dateTime_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12);
+const later = new Temporal.PlainDateTime(2000, 5, 5);
+const result = later.since(earlier, { smallestUnit: "day", roundingIncrement: 2 });
+TemporalHelpers.assertDuration(result, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/round-relative-to-receiver.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/round-relative-to-receiver.js
new file mode 100644
index 0000000000..af8a646eb0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/round-relative-to-receiver.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.plaindatetime.prototype.since
+description: Values are rounded relative to the receiver
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dt1 = new Temporal.PlainDateTime(2019, 1, 1);
+const dt2 = new Temporal.PlainDateTime(2020, 7, 2);
+
+TemporalHelpers.assertDuration(
+ dt2.since(dt1, { smallestUnit: "years", roundingMode: "halfExpand" }),
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "rounds relative to the receiver (positive case)"
+);
+
+TemporalHelpers.assertDuration(
+ dt1.since(dt2, { smallestUnit: "years", roundingMode: "halfExpand" }),
+ -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "rounds relative to the receiver (negative case)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/rounding-zero-year-month-week-length.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/rounding-zero-year-month-week-length.js
new file mode 100644
index 0000000000..48be068e79
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/rounding-zero-year-month-week-length.js
@@ -0,0 +1,34 @@
+// |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.plaindatetime.prototype.since
+description: >
+ A malicious calendar resulting in a year, month, or week length of zero is
+ handled correctly
+info: |
+ RoundDuration
+ 10.z. If _oneYearDays_ = 0, throw a *RangeError* exception.
+ ...
+ 11.z. If _oneMonthDays_ = 0, throw a *RangeError* exception.
+ ...
+ 12.s. If _oneWeekDays_ = 0, throw a *RangeError* exception.
+features: [Temporal]
+---*/
+
+const cal = new class extends Temporal.Calendar {
+ dateAdd(date, duration, options) {
+ // Called several times, last call sets oneYear/Month/WeekDays to 0
+ return new Temporal.PlainDate(1970, 1, 1);
+ }
+}("iso8601");
+
+const dt1 = new Temporal.PlainDateTime(1970, 1, 1, 0, 0, 0, 0, 0, 0, cal);
+const dt2 = new Temporal.PlainDateTime(1971, 1, 1, 0, 0, 0, 0, 0, 1, cal);
+
+assert.throws(RangeError, () => dt1.since(dt2, { smallestUnit: "years" }), "zero year length handled correctly");
+assert.throws(RangeError, () => dt1.since(dt2, { smallestUnit: "months" }), "zero month length handled correctly");
+assert.throws(RangeError, () => dt1.since(dt2, { smallestUnit: "weeks" }), "zero week length handled correctly");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-basic.js
new file mode 100644
index 0000000000..823ee96147
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-basic.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.plaindatetime.prototype.since
+description: Round to different smallest increments
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2019, 1, 8, 8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainDateTime(2021, 9, 7, 12, 39, 40, 987, 654, 321);
+
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "hours", roundingIncrement: 3, roundingMode: "halfExpand" }),
+ 0, 0, 0, 973, 3, 0, 0, 0, 0, 0,
+ "rounds to an increment of hours"
+);
+
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "minutes", roundingIncrement: 30, roundingMode: "halfExpand" }),
+ 0, 0, 0, 973, 4, 30, 0, 0, 0,0,
+ "rounds to an increment of minutes"
+);
+
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "seconds", roundingIncrement: 15, roundingMode: "halfExpand" }),
+ 0, 0, 0, 973, 4, 17, 0, 0, 0, 0,
+ "rounds to an increment of seconds"
+);
+
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "milliseconds", roundingIncrement: 10, roundingMode: "halfExpand" }),
+ 0, 0, 0, 973, 4, 17, 4, 860, 0, 0,
+ "rounds to an increment of milliseconds"
+);
+
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "microseconds", roundingIncrement: 10, roundingMode: "halfExpand" }),
+ 0, 0, 0, 973, 4, 17, 4, 864, 200, 0,
+ "rounds to an increment of microseconds"
+);
+
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "nanoseconds", roundingIncrement: 10, roundingMode: "halfExpand" }),
+ 0, 0, 0, 973, 4, 17, 4, 864, 197, 530,
+ "rounds to an increment of nanoseconds"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-cleanly-divides.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-cleanly-divides.js
new file mode 100644
index 0000000000..9268e99cd5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-cleanly-divides.js
@@ -0,0 +1,42 @@
+// |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.plaindatetime.prototype.since
+description: Rounding argument cleanly divides the relevant smallest unit
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2019, 1, 8, 8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainDateTime(2021, 9, 7, 12, 39, 40, 987, 654, 321);
+
+[1, 2, 3, 4, 6, 8, 12].forEach((roundingIncrement) => {
+ const options = { smallestUnit: "hours", roundingIncrement };
+ assert(
+ later.since(earlier, options) instanceof Temporal.Duration,
+ `valid hour increments divide into 24 (rounding increment = ${roundingIncrement}, smallest unit = hours)`
+ );
+});
+
+["minutes", "seconds"].forEach((smallestUnit) => {
+ [1, 2, 3, 4, 5, 6, 10, 12, 15, 20, 30].forEach((roundingIncrement) => {
+ const options = { smallestUnit, roundingIncrement };
+ assert(
+ later.since(earlier, options) instanceof Temporal.Duration,
+ `valid ${smallestUnit} increments divide into 60 (rounding increment = ${roundingIncrement})`
+ );
+ });
+});
+
+["milliseconds", "microseconds", "nanoseconds"].forEach((smallestUnit) => {
+ [1, 2, 4, 5, 8, 10, 20, 25, 40, 50, 100, 125, 200, 250, 500].forEach((roundingIncrement) => {
+ const options = { smallestUnit, roundingIncrement };
+ assert(
+ later.since(earlier, options) instanceof Temporal.Duration,
+ `valid ${smallestUnit} increments divide into 1000 (rounding increment = ${roundingIncrement})`
+ );
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-does-not-divide.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-does-not-divide.js
new file mode 100644
index 0000000000..7bae2ca7cc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-does-not-divide.js
@@ -0,0 +1,48 @@
+// |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.plaindatetime.prototype.since
+description: Throw if rounding increment does not cleanly divide the relevant unit
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2019, 1, 8, 8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainDateTime(2021, 9, 7, 12, 39, 40, 987, 654, 321);
+
+const badIncrements = {
+ "hours": 11,
+ "minutes": 29,
+ "seconds": 29,
+ "milliseconds": 29,
+ "microseconds": 29,
+ "nanoseconds": 29
+};
+
+Object.entries(badIncrements).forEach(([unit, bad]) => {
+ assert.throws(
+ RangeError,
+ () => later.since(earlier, { smallestUnit: unit, roundingIncrement: bad }),
+ `throws on increments that do not divide evenly into the next highest (unit = ${unit}, increment = ${bad})`
+ );
+});
+
+const fullIncrements = {
+ "hours": 24,
+ "minutes": 60,
+ "seconds": 60,
+ "milliseconds": 1000,
+ "microseconds": 1000,
+ "nanoseconds": 1000
+};
+
+Object.entries(fullIncrements).forEach(([unit, bad]) => {
+ assert.throws(
+ RangeError,
+ () => later.since(earlier, { smallestUnit: unit, roundingIncrement: bad }),
+ `throws on increments that are equal to the next highest (unit = ${unit}, rounding increment = ${bad}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-nan.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-nan.js
new file mode 100644
index 0000000000..79691a7652
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/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.plaindatetime.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.plaindatetime.prototype.since step 13:
+ 12. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*).
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const later = new Temporal.PlainDateTime(2001, 6, 3, 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/PlainDateTime/prototype/since/roundingincrement-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-non-integer.js
new file mode 100644
index 0000000000..d2005912fd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-non-integer.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.plaindatetime.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.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2000, 5, 2, 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");
+const result2 = later.since(earlier, { smallestUnit: "days", roundingIncrement: 1e9 + 0.5, roundingMode: "expand" });
+TemporalHelpers.assertDuration(result2, 0, 0, 0, 1e9, 0, 0, 0, 0, 0, 0, "roundingIncrement 1e9 + 0.5 truncates to 1e9");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-out-of-range.js
new file mode 100644
index 0000000000..0e9d196c8f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/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.plaindatetime.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.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2000, 5, 2, 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/PlainDateTime/prototype/since/roundingincrement-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-undefined.js
new file mode 100644
index 0000000000..1e480dd764
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/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.plaindatetime.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.plaindatetime.prototype.since step 13:
+ 13. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const later = new Temporal.PlainDateTime(2001, 6, 3, 13, 35, 57, 988, 655, 322);
+
+const explicit = later.since(earlier, { roundingIncrement: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 397, 1, 1, 1, 1, 1, 1, "default roundingIncrement is 1");
+
+const implicit = later.since(earlier, {});
+TemporalHelpers.assertDuration(implicit, 0, 0, 0, 397, 1, 1, 1, 1, 1, 1, "default roundingIncrement is 1");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-wrong-type.js
new file mode 100644
index 0000000000..2602578de8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/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.plaindatetime.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.plaindatetime.prototype.since step 13:
+ 13. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*).
+includes: [temporalHelpers.js, compareArray.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const later = new Temporal.PlainDateTime(2001, 6, 3, 13, 35, 57, 988, 655, 322);
+
+TemporalHelpers.checkRoundingIncrementOptionWrongType(
+ (roundingIncrement) => later.since(earlier, { roundingIncrement }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 397, 1, 1, 1, 1, 1, 1, descr),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 397, 1, 1, 1, 1, 1, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-ceil.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-ceil.js
new file mode 100644
index 0000000000..34a5fc36cb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-ceil.js
@@ -0,0 +1,45 @@
+// |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.plaindatetime.prototype.since
+description: Tests calculations with roundingMode "ceil".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2019, 1, 8, 8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainDateTime(2021, 9, 7, 12, 39, 40, 987, 654, 289);
+
+const expected = [
+ ["years", [3], [-2]],
+ ["months", [0, 32], [0, -31]],
+ ["weeks", [0, 0, 140], [0, 0, -139]],
+ ["days", [0, 0, 0, 974], [0, 0, 0, -973]],
+ ["hours", [0, 0, 0, 973, 5], [0, 0, 0, -973, -4]],
+ ["minutes", [0, 0, 0, 973, 4, 18], [0, 0, 0, -973, -4, -17]],
+ ["seconds", [0, 0, 0, 973, 4, 17, 5], [0, 0, 0, -973, -4, -17, -4]],
+ ["milliseconds", [0, 0, 0, 973, 4, 17, 4, 865], [0, 0, 0, -973, -4, -17, -4, -864]],
+ ["microseconds", [0, 0, 0, 973, 4, 17, 4, 864, 198], [0, 0, 0, -973, -4, -17, -4, -864, -197]],
+ ["nanoseconds", [0, 0, 0, 973, 4, 17, 4, 864, 197, 500], [0, 0, 0, -973, -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/PlainDateTime/prototype/since/roundingmode-expand.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-expand.js
new file mode 100644
index 0000000000..91f7fc35b1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-expand.js
@@ -0,0 +1,45 @@
+// |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.plaindatetime.prototype.since
+description: Tests calculations with roundingMode "expand".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2019, 1, 8, 8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainDateTime(2021, 9, 7, 12, 39, 40, 987, 654, 289);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [0, 32], [0, -32]],
+ ["weeks", [0, 0, 140], [0, 0, -140]],
+ ["days", [0, 0, 0, 974], [0, 0, 0, -974]],
+ ["hours", [0, 0, 0, 973, 5], [0, 0, 0, -973, -5]],
+ ["minutes", [0, 0, 0, 973, 4, 18], [0, 0, 0, -973, -4, -18]],
+ ["seconds", [0, 0, 0, 973, 4, 17, 5], [0, 0, 0, -973, -4, -17, -5]],
+ ["milliseconds", [0, 0, 0, 973, 4, 17, 4, 865], [0, 0, 0, -973, -4, -17, -4, -865]],
+ ["microseconds", [0, 0, 0, 973, 4, 17, 4, 864, 198], [0, 0, 0, -973, -4, -17, -4, -864, -198]],
+ ["nanoseconds", [0, 0, 0, 973, 4, 17, 4, 864, 197, 500], [0, 0, 0, -973, -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/PlainDateTime/prototype/since/roundingmode-floor.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-floor.js
new file mode 100644
index 0000000000..df8eb42a2d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-floor.js
@@ -0,0 +1,45 @@
+// |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.plaindatetime.prototype.since
+description: Tests calculations with roundingMode "floor".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2019, 1, 8, 8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainDateTime(2021, 9, 7, 12, 39, 40, 987, 654, 289);
+
+const expected = [
+ ["years", [2], [-3]],
+ ["months", [0, 31], [0, -32]],
+ ["weeks", [0, 0, 139], [0, 0, -140]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -974]],
+ ["hours", [0, 0, 0, 973, 4], [0, 0, 0, -973, -5]],
+ ["minutes", [0, 0, 0, 973, 4, 17], [0, 0, 0, -973, -4, -18]],
+ ["seconds", [0, 0, 0, 973, 4, 17, 4], [0, 0, 0, -973, -4, -17, -5]],
+ ["milliseconds", [0, 0, 0, 973, 4, 17, 4, 864], [0, 0, 0, -973, -4, -17, -4, -865]],
+ ["microseconds", [0, 0, 0, 973, 4, 17, 4, 864, 197], [0, 0, 0, -973, -4, -17, -4, -864, -198]],
+ ["nanoseconds", [0, 0, 0, 973, 4, 17, 4, 864, 197, 500], [0, 0, 0, -973, -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/PlainDateTime/prototype/since/roundingmode-halfCeil.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-halfCeil.js
new file mode 100644
index 0000000000..c6f21e1cd4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-halfCeil.js
@@ -0,0 +1,45 @@
+// |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.plaindatetime.prototype.since
+description: Tests calculations with roundingMode "halfCeil".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2019, 1, 8, 8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainDateTime(2021, 9, 7, 12, 39, 40, 987, 654, 289);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [0, 32], [0, -32]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+ ["hours", [0, 0, 0, 973, 4], [0, 0, 0, -973, -4]],
+ ["minutes", [0, 0, 0, 973, 4, 17], [0, 0, 0, -973, -4, -17]],
+ ["seconds", [0, 0, 0, 973, 4, 17, 5], [0, 0, 0, -973, -4, -17, -5]],
+ ["milliseconds", [0, 0, 0, 973, 4, 17, 4, 864], [0, 0, 0, -973, -4, -17, -4, -864]],
+ ["microseconds", [0, 0, 0, 973, 4, 17, 4, 864, 198], [0, 0, 0, -973, -4, -17, -4, -864, -197]],
+ ["nanoseconds", [0, 0, 0, 973, 4, 17, 4, 864, 197, 500], [0, 0, 0, -973, -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/PlainDateTime/prototype/since/roundingmode-halfEven.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-halfEven.js
new file mode 100644
index 0000000000..16af003b07
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-halfEven.js
@@ -0,0 +1,45 @@
+// |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.plaindatetime.prototype.since
+description: Tests calculations with roundingMode "halfEven".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2019, 1, 8, 8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainDateTime(2021, 9, 7, 12, 39, 40, 987, 654, 289);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [0, 32], [0, -32]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+ ["hours", [0, 0, 0, 973, 4], [0, 0, 0, -973, -4]],
+ ["minutes", [0, 0, 0, 973, 4, 17], [0, 0, 0, -973, -4, -17]],
+ ["seconds", [0, 0, 0, 973, 4, 17, 5], [0, 0, 0, -973, -4, -17, -5]],
+ ["milliseconds", [0, 0, 0, 973, 4, 17, 4, 864], [0, 0, 0, -973, -4, -17, -4, -864]],
+ ["microseconds", [0, 0, 0, 973, 4, 17, 4, 864, 198], [0, 0, 0, -973, -4, -17, -4, -864, -198]],
+ ["nanoseconds", [0, 0, 0, 973, 4, 17, 4, 864, 197, 500], [0, 0, 0, -973, -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/PlainDateTime/prototype/since/roundingmode-halfExpand.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-halfExpand.js
new file mode 100644
index 0000000000..d9626be1ba
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-halfExpand.js
@@ -0,0 +1,45 @@
+// |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.plaindatetime.prototype.since
+description: Tests calculations with roundingMode "halfExpand".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2019, 1, 8, 8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainDateTime(2021, 9, 7, 12, 39, 40, 987, 654, 289);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [0, 32], [0, -32]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+ ["hours", [0, 0, 0, 973, 4], [0, 0, 0, -973, -4]],
+ ["minutes", [0, 0, 0, 973, 4, 17], [0, 0, 0, -973, -4, -17]],
+ ["seconds", [0, 0, 0, 973, 4, 17, 5], [0, 0, 0, -973, -4, -17, -5]],
+ ["milliseconds", [0, 0, 0, 973, 4, 17, 4, 864], [0, 0, 0, -973, -4, -17, -4, -864]],
+ ["microseconds", [0, 0, 0, 973, 4, 17, 4, 864, 198], [0, 0, 0, -973, -4, -17, -4, -864, -198]],
+ ["nanoseconds", [0, 0, 0, 973, 4, 17, 4, 864, 197, 500], [0, 0, 0, -973, -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/PlainDateTime/prototype/since/roundingmode-halfFloor.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-halfFloor.js
new file mode 100644
index 0000000000..401b03bf04
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-halfFloor.js
@@ -0,0 +1,45 @@
+// |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.plaindatetime.prototype.since
+description: Tests calculations with roundingMode "halfFloor".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2019, 1, 8, 8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainDateTime(2021, 9, 7, 12, 39, 40, 987, 654, 289);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [0, 32], [0, -32]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+ ["hours", [0, 0, 0, 973, 4], [0, 0, 0, -973, -4]],
+ ["minutes", [0, 0, 0, 973, 4, 17], [0, 0, 0, -973, -4, -17]],
+ ["seconds", [0, 0, 0, 973, 4, 17, 5], [0, 0, 0, -973, -4, -17, -5]],
+ ["milliseconds", [0, 0, 0, 973, 4, 17, 4, 864], [0, 0, 0, -973, -4, -17, -4, -864]],
+ ["microseconds", [0, 0, 0, 973, 4, 17, 4, 864, 197], [0, 0, 0, -973, -4, -17, -4, -864, -198]],
+ ["nanoseconds", [0, 0, 0, 973, 4, 17, 4, 864, 197, 500], [0, 0, 0, -973, -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/PlainDateTime/prototype/since/roundingmode-halfTrunc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-halfTrunc.js
new file mode 100644
index 0000000000..cdcb279d87
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-halfTrunc.js
@@ -0,0 +1,45 @@
+// |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.plaindatetime.prototype.since
+description: Tests calculations with roundingMode "halfTrunc".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2019, 1, 8, 8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainDateTime(2021, 9, 7, 12, 39, 40, 987, 654, 289);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [0, 32], [0, -32]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+ ["hours", [0, 0, 0, 973, 4], [0, 0, 0, -973, -4]],
+ ["minutes", [0, 0, 0, 973, 4, 17], [0, 0, 0, -973, -4, -17]],
+ ["seconds", [0, 0, 0, 973, 4, 17, 5], [0, 0, 0, -973, -4, -17, -5]],
+ ["milliseconds", [0, 0, 0, 973, 4, 17, 4, 864], [0, 0, 0, -973, -4, -17, -4, -864]],
+ ["microseconds", [0, 0, 0, 973, 4, 17, 4, 864, 197], [0, 0, 0, -973, -4, -17, -4, -864, -197]],
+ ["nanoseconds", [0, 0, 0, 973, 4, 17, 4, 864, 197, 500], [0, 0, 0, -973, -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/PlainDateTime/prototype/since/roundingmode-halfexpand-default-changes.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-halfexpand-default-changes.js
new file mode 100644
index 0000000000..4a0e3b2bf2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-halfexpand-default-changes.js
@@ -0,0 +1,33 @@
+// |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.plaindatetime.prototype.since
+description: A different default for largest unit will be used if smallest unit is larger than "days"
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2019, 1, 8, 8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainDateTime(2021, 9, 7, 12, 39, 40, 987, 654, 321);
+
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "years", roundingMode: "halfExpand" }),
+ 3, 0, 0, 0, 0, 0, 0, 0, 0,0,
+ "assumes a different default for largestUnit if smallestUnit is larger than days (smallest unit = years)"
+);
+
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "months", roundingMode: "halfExpand" }),
+ 0, 32, 0, 0, 0, 0, 0, 0, 0, 0,
+ "assumes a different default for largestUnit if smallestUnit is larger than days (smallest unit = months)"
+);
+
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "weeks", roundingMode: "halfExpand" }),
+ 0, 0, 139, 0, 0, 0, 0, 0, 0, 0,
+ "assumes a different default for largestUnit if smallestUnit is larger than days (smallest unit = weeks)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-invalid-string.js
new file mode 100644
index 0000000000..c146e82462
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/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.plaindatetime.prototype.since
+description: RangeError thrown when roundingMode option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2000, 5, 3, 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/PlainDateTime/prototype/since/roundingmode-trunc-is-default.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-trunc-is-default.js
new file mode 100644
index 0000000000..5e6bf589be
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-trunc-is-default.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.plaindatetime.prototype.since
+description: Truncation (trunc) is the default rounding mode
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2019, 1, 8, 8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainDateTime(2021, 9, 7, 12, 39, 40, 987, 654, 321);
+
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "minutes" }),
+ 0, 0, 0, 973, 4, 17, 0, 0, 0, 0,
+ "trunc is the default (round up)"
+);
+
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "seconds" }),
+ 0, 0, 0, 973, 4, 17, 4, 0, 0, 0,
+ "trunc is the default (round down)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-trunc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-trunc.js
new file mode 100644
index 0000000000..d9c74ae636
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-trunc.js
@@ -0,0 +1,45 @@
+// |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.plaindatetime.prototype.since
+description: Tests calculations with roundingMode "trunc".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2019, 1, 8, 8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainDateTime(2021, 9, 7, 12, 39, 40, 987, 654, 289);
+
+const expected = [
+ ["years", [2], [-2]],
+ ["months", [0, 31], [0, -31]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+ ["hours", [0, 0, 0, 973, 4], [0, 0, 0, -973, -4]],
+ ["minutes", [0, 0, 0, 973, 4, 17], [0, 0, 0, -973, -4, -17]],
+ ["seconds", [0, 0, 0, 973, 4, 17, 4], [0, 0, 0, -973, -4, -17, -4]],
+ ["milliseconds", [0, 0, 0, 973, 4, 17, 4, 864], [0, 0, 0, -973, -4, -17, -4, -864]],
+ ["microseconds", [0, 0, 0, 973, 4, 17, 4, 864, 197], [0, 0, 0, -973, -4, -17, -4, -864, -197]],
+ ["nanoseconds", [0, 0, 0, 973, 4, 17, 4, 864, 197, 500], [0, 0, 0, -973, -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/PlainDateTime/prototype/since/roundingmode-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-undefined.js
new file mode 100644
index 0000000000..3e12c14e9c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-undefined.js
@@ -0,0 +1,30 @@
+// |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.plaindatetime.prototype.since
+description: Fallback value for roundingMode option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2000, 5, 3, 13, 35, 57, 123, 987, 500);
+
+const explicit1 = later.since(earlier, { smallestUnit: "microsecond", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit1, 0, 0, 0, 1, 1, 1, 1, 123, 987, 0, "default roundingMode is trunc");
+const implicit1 = later.since(earlier, { smallestUnit: "microsecond" });
+TemporalHelpers.assertDuration(implicit1, 0, 0, 0, 1, 1, 1, 1, 123, 987, 0, "default roundingMode is trunc");
+
+const explicit2 = later.since(earlier, { smallestUnit: "millisecond", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit2, 0, 0, 0, 1, 1, 1, 1, 123, 0, 0, "default roundingMode is trunc");
+const implicit2 = later.since(earlier, { smallestUnit: "millisecond" });
+TemporalHelpers.assertDuration(implicit2, 0, 0, 0, 1, 1, 1, 1, 123, 0, 0, "default roundingMode is trunc");
+
+const explicit3 = later.since(earlier, { smallestUnit: "second", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit3, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, "default roundingMode is trunc");
+const implicit3 = later.since(earlier, { smallestUnit: "second" });
+TemporalHelpers.assertDuration(implicit3, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, "default roundingMode is trunc");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-wrong-type.js
new file mode 100644
index 0000000000..ecae2f1b35
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/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.plaindatetime.prototype.since
+description: Type conversions for roundingMode option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2000, 5, 3, 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, 1, 1, 1, 1, 123, 987, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/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/PlainDateTime/prototype/since/smallestunit-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/smallestunit-invalid-string.js
new file mode 100644
index 0000000000..36f1d33e50
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/smallestunit-invalid-string.js
@@ -0,0 +1,31 @@
+// |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.plaindatetime.prototype.since
+description: RangeError thrown when smallestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2000, 5, 3, 13, 35, 57, 987, 654, 321);
+const badValues = [
+ "era",
+ "eraYear",
+ "millisecond\0",
+ "mill\u0131second",
+ "SECOND",
+ "eras",
+ "eraYears",
+ "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/PlainDateTime/prototype/since/smallestunit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/smallestunit-plurals-accepted.js
new file mode 100644
index 0000000000..5a8b6cda7b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/smallestunit-plurals-accepted.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.plaindatetime.prototype.since
+description: Plural units are accepted as well for the smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const later = new Temporal.PlainDateTime(2001, 6, 12, 13, 35, 57, 988, 655, 322);
+const validUnits = [
+ "year",
+ "month",
+ "week",
+ "day",
+ "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/PlainDateTime/prototype/since/smallestunit-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/smallestunit-undefined.js
new file mode 100644
index 0000000000..7f2571caab
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/smallestunit-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.plaindatetime.prototype.since
+description: Fallback value for smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2000, 5, 3, 13, 35, 57, 987, 654, 321);
+
+const explicit = later.since(earlier, { smallestUnit: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 1, 1, 1, 1, 987, 654, 321, "default smallestUnit is nanosecond");
+const implicit = later.since(earlier, {});
+TemporalHelpers.assertDuration(implicit, 0, 0, 0, 1, 1, 1, 1, 987, 654, 321, "default smallestUnit is nanosecond");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/smallestunit-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/smallestunit-wrong-type.js
new file mode 100644
index 0000000000..17d4abb27c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/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.plaindatetime.prototype.since
+description: Type conversions for smallestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2000, 5, 3, 13, 35, 57, 987, 654, 321);
+TemporalHelpers.checkStringOptionWrongType("smallestUnit", "microsecond",
+ (smallestUnit) => later.since(earlier, { smallestUnit }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 1, 1, 1, 1, 987, 654, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/subseconds.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/subseconds.js
new file mode 100644
index 0000000000..cfd38c4dfd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/subseconds.js
@@ -0,0 +1,33 @@
+// |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.plaindatetime.prototype.since
+description: Returned granularity may be finer than seconds
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const feb20 = new Temporal.PlainDateTime(2020, 2, 1, 0, 0);
+const feb21 = new Temporal.PlainDateTime(2020, 2, 2, 0, 0, 0, 250, 250, 250);
+
+TemporalHelpers.assertDuration(
+ feb21.since(feb20, { largestUnit: "milliseconds" }),
+ 0, 0, 0, 0, 0, 0, 0, 86400250, 250, 250,
+ "can return subseconds (milliseconds)"
+);
+
+TemporalHelpers.assertDuration(
+ feb21.since(feb20, { largestUnit: "microseconds" }),
+ 0, 0, 0, 0, 0, 0, 0, 0, 86400250250, 250,
+ "can return subseconds (microseconds)"
+);
+
+TemporalHelpers.assertDuration(
+ feb21.since(feb20, { largestUnit: "nanoseconds" }),
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 86400250250250,
+ "can return subseconds (nanoseconds)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/weeks-months-mutually-exclusive.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/weeks-months-mutually-exclusive.js
new file mode 100644
index 0000000000..6919df2c1c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/weeks-months-mutually-exclusive.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.plaindatetime.prototype.since
+description: Weeks and months are mutually exclusive
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dt = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789);
+const laterDateTime = dt.add({ days: 42, hours: 3 });
+
+TemporalHelpers.assertDuration(
+ laterDateTime.since(dt, { largestUnit: "weeks" }),
+ 0, 0, 6, 0, 3, 0, 0, 0, 0, 0,
+ "weeks and months are mutually exclusive (prefer weeks)"
+);
+
+TemporalHelpers.assertDuration(
+ laterDateTime.since(dt, { largestUnit: "months" }),
+ 0, 1, 0, 12, 3, 0, 0, 0,0, 0,
+ "weeks and months are mutually exclusive (prefer months)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/year-zero.js
new file mode 100644
index 0000000000..84d2cfd46d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/year-zero.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.plaindatetime.prototype.since
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-12-07",
+ "-000000-12-07T03:24:30",
+ "-000000-12-07T03:24:30+01:00",
+ "-000000-12-07T03:24:30+00:00[UTC]",
+];
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.since(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/ambiguous-date.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/ambiguous-date.js
new file mode 100644
index 0000000000..f23e3f376c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/ambiguous-date.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.plaindatetime.prototype.subtract
+description: Ambiguous subtraction is handled according to the overflow option
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const mar31 = new Temporal.PlainDateTime(2020, 3, 31, 15, 0);
+
+TemporalHelpers.assertPlainDateTime(
+ mar31.subtract({ months: 1 }),
+ 2020, 2, "M02", 29, 15, 0, 0, 0, 0, 0,
+ "constrain when ambiguous result (overflow options not supplied)"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ mar31.subtract({ months: 1 }, { overflow: "constrain" }),
+ 2020, 2, "M02", 29, 15, 0, 0, 0, 0, 0,
+ "constrain when ambiguous result (overflow options supplied)"
+);
+
+assert.throws(
+ RangeError,
+ () => mar31.subtract({ months: 1 }, { overflow: "reject" }),
+ "throw when ambiguous result with reject"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-duration-max.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-duration-max.js
new file mode 100644
index 0000000000..7b43031195
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-duration-max.js
@@ -0,0 +1,58 @@
+// |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.plaindatetime.prototype.subtract
+description: Maximum allowed duration
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(1970, 1, 1);
+
+const maxCases = [
+ ["P273790Y8M11DT23H59M59.999999999S", "string with max years"],
+ [{ years: 273790, months: 8, days: 11, nanoseconds: 86399999999999 }, "property bag with max years"],
+ ["P3285488M11DT23H59M59.999999999S", "string with max months"],
+ [{ months: 3285488, days: 11, nanoseconds: 86399999999999 }, "property bag with max months"],
+ ["P14285714W2DT23H59M59.999999999S", "string with max weeks"],
+ [{ weeks: 14285714, days: 2, nanoseconds: 86399999999999 }, "property bag with max weeks"],
+ ["P100000000DT23H59M59.999999999S", "string with max days"],
+ [{ days: 100000000, nanoseconds: 86399999999999 }, "property bag with max days"],
+ ["PT2400000023H59M59.999999999S", "string with max hours"],
+ [{ hours: 2400000023, nanoseconds: 3599999999999 }, "property bag with max hours"],
+ ["PT144000001439M59.999999999S", "string with max minutes"],
+ [{ minutes: 144000001439, nanoseconds: 59999999999 }, "property bag with max minutes"],
+ ["PT8640000086399.999999999S", "string with max seconds"],
+ [{ seconds: 8640000086399, nanoseconds: 999999999 }, "property bag with max seconds"],
+];
+
+for (const [arg, descr] of maxCases) {
+ const result = instance.subtract(arg);
+ TemporalHelpers.assertPlainDateTime(result, -271821, 4, "M04", 19, 0, 0, 0, 0, 0, 1, `operation succeeds with ${descr}`);
+}
+
+const minCases = [
+ ["-P273790Y8M12DT23H59M59.999999999S", "string with min years"],
+ [{ years: -273790, months: -8, days: -12, nanoseconds: -86399999999999 }, "property bag with min years"],
+ ["-P3285488M12DT23H59M59.999999999S", "string with min months"],
+ [{ months: -3285488, days: -12, nanoseconds: -86399999999999 }, "property bag with min months"],
+ ["-P14285714W2DT23H59M59.999999999S", "string with min weeks"],
+ [{ weeks: -14285714, days: -2, nanoseconds: -86399999999999 }, "property bag with min weeks"],
+ ["-P100000000DT23H59M59.999999999S", "string with min days"],
+ [{ days: -100000000, nanoseconds: -86399999999999 }, "property bag with min days"],
+ ["-PT2400000023H59M59.999999999S", "string with min hours"],
+ [{ hours: -2400000023, nanoseconds: -3599999999999 }, "property bag with min hours"],
+ ["-PT144000001439M59.999999999S", "string with min minutes"],
+ [{ minutes: -144000001439, nanoseconds: -59999999999 }, "property bag with min minutes"],
+ ["-PT8640000086399.999999999S", "string with min seconds"],
+ [{ seconds: -8640000086399, nanoseconds: -999999999 }, "property bag with min seconds"],
+];
+
+for (const [arg, descr] of minCases) {
+ const result = instance.subtract(arg);
+ TemporalHelpers.assertPlainDateTime(result, 275760, 9, "M09", 13, 23, 59, 59, 999, 999, 999, `operation succeeds with ${descr}`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-duration-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-duration-out-of-range.js
new file mode 100644
index 0000000000..d5067d41f8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-duration-out-of-range.js
@@ -0,0 +1,75 @@
+// |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.plaindatetime.prototype.subtract
+description: Duration-like argument that is out of range
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(1970, 1, 1);
+
+const cases = [
+ // 2^32 = 4294967296
+ ["P4294967296Y", "string with years > max"],
+ [{ years: 4294967296 }, "property bag with years > max"],
+ ["-P4294967296Y", "string with years < min"],
+ [{ years: -4294967296 }, "property bag with years < min"],
+ ["P4294967296M", "string with months > max"],
+ [{ months: 4294967296 }, "property bag with months > max"],
+ ["-P4294967296M", "string with months < min"],
+ [{ months: -4294967296 }, "property bag with months < min"],
+ ["P4294967296W", "string with weeks > max"],
+ [{ weeks: 4294967296 }, "property bag with weeks > max"],
+ ["-P4294967296W", "string with weeks < min"],
+ [{ weeks: -4294967296 }, "property bag with weeks < min"],
+
+ // ceil(max safe integer / 86400) = 104249991375
+ ["P104249991375D", "string with days > max"],
+ [{ days: 104249991375 }, "property bag with days > max"],
+ ["P104249991374DT24H", "string where hours balance into days > max"],
+ [{ days: 104249991374, hours: 24 }, "property bag where hours balance into days > max"],
+ ["-P104249991375D", "string with days < min"],
+ [{ days: -104249991375 }, "property bag with days < min"],
+ ["-P104249991374DT24H", "string where hours balance into days < min"],
+ [{ days: -104249991374, hours: -24 }, "property bag where hours balance into days < min"],
+
+ // ceil(max safe integer / 3600) = 2501999792984
+ ["PT2501999792984H", "string with hours > max"],
+ [{ hours: 2501999792984 }, "property bag with hours > max"],
+ ["PT2501999792983H60M", "string where minutes balance into hours > max"],
+ [{ hours: 2501999792983, minutes: 60 }, "property bag where minutes balance into hours > max"],
+ ["-PT2501999792984H", "string with hours < min"],
+ [{ hours: -2501999792984 }, "property bag with hours < min"],
+ ["-PT2501999792983H60M", "string where minutes balance into hours < min"],
+ [{ hours: -2501999792983, minutes: -60 }, "property bag where minutes balance into hours < min"],
+
+ // ceil(max safe integer / 60) = 150119987579017
+ ["PT150119987579017M", "string with minutes > max"],
+ [{ minutes: 150119987579017 }, "property bag with minutes > max"],
+ ["PT150119987579016M60S", "string where seconds balance into minutes > max"],
+ [{ minutes: 150119987579016, seconds: 60 }, "property bag where seconds balance into minutes > max"],
+ ["-PT150119987579017M", "string with minutes < min"],
+ [{ minutes: -150119987579017 }, "property bag with minutes < min"],
+ ["-PT150119987579016M60S", "string where seconds balance into minutes < min"],
+ [{ minutes: -150119987579016, seconds: -60 }, "property bag where seconds balance into minutes < min"],
+
+ // 2^53 = 9007199254740992
+ ["PT9007199254740992S", "string with seconds > max"],
+ [{ seconds: 9007199254740992 }, "property bag with seconds > max"],
+ [{ seconds: 9007199254740991, milliseconds: 1000 }, "property bag where milliseconds balance into seconds > max"],
+ [{ seconds: 9007199254740991, microseconds: 1000000 }, "property bag where microseconds balance into seconds > max"],
+ [{ seconds: 9007199254740991, nanoseconds: 1000000000 }, "property bag where nanoseconds balance into seconds > max"],
+ ["-PT9007199254740992S", "string with seconds < min"],
+ [{ seconds: -9007199254740992 }, "property bag with seconds < min"],
+ [{ seconds: -9007199254740991, milliseconds: -1000 }, "property bag where milliseconds balance into seconds < min"],
+ [{ seconds: -9007199254740991, microseconds: -1000000 }, "property bag where microseconds balance into seconds < min"],
+ [{ seconds: -9007199254740991, nanoseconds: -1000000000 }, "property bag where nanoseconds balance into seconds < min"],
+];
+
+for (const [arg, descr] of cases) {
+ assert.throws(RangeError, () => instance.subtract(arg), `${descr} is out of range`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-duration.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-duration.js
new file mode 100644
index 0000000000..bc8df78a45
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-duration.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.plaindatetime.prototype.subtract
+description: Duration object arguments are handled
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const jan31 = new Temporal.PlainDateTime(2020, 1, 31, 15, 0);
+
+const subtractWithDuration = jan31.subtract(new Temporal.Duration(0, 1, 0, 0, 0, 1));
+TemporalHelpers.assertPlainDateTime(
+ subtractWithDuration,
+ 2019, 12, "M12", 31, 14, 59, 0, 0, 0, 0,
+ "Duration argument"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-invalid-property.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-invalid-property.js
new file mode 100644
index 0000000000..139dfa66ea
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-invalid-property.js
@@ -0,0 +1,31 @@
+// |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.plaindatetime.prototype.subtract
+description: temporalDurationLike object must contain at least one correctly spelled property
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 15, 30, 45, 987, 654, 321);
+
+assert.throws(
+ TypeError,
+ () => instance.subtract({}),
+ "Throws TypeError if no property is present"
+);
+
+assert.throws(
+ TypeError,
+ () => instance.subtract({ nonsense: true }),
+ "Throws TypeError if no recognized property is present"
+);
+
+assert.throws(
+ TypeError,
+ () => instance.subtract({ sign: 1 }),
+ "Sign property is not recognized"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-mixed-sign.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-mixed-sign.js
new file mode 100644
index 0000000000..82e4584910
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-mixed-sign.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.plaindatetime.prototype.subtract
+description: Positive and negative values in the temporalDurationLike argument are not acceptable
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 15, 30, 45, 987, 654, 321);
+
+["constrain", "reject"].forEach((overflow) => {
+ assert.throws(
+ RangeError,
+ () => instance.subtract({ hours: 1, minutes: -30 }, { overflow }),
+ `mixed positive and negative values always throw (overflow = "${overflow}")`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-not-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-not-object.js
new file mode 100644
index 0000000000..62a65a3720
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-not-object.js
@@ -0,0 +1,22 @@
+// |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.plaindatetime.prototype.subtract
+description: Passing a primitive other than string to subtract() throws
+features: [Symbol, Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 15, 30, 45, 987, 654, 321);
+assert.throws(TypeError, () => instance.subtract(undefined), "undefined");
+assert.throws(TypeError, () => instance.subtract(null), "null");
+assert.throws(TypeError, () => instance.subtract(true), "boolean");
+assert.throws(RangeError, () => instance.subtract(""), "empty string");
+assert.throws(TypeError, () => instance.subtract(Symbol()), "Symbol");
+assert.throws(TypeError, () => instance.subtract(7), "number");
+assert.throws(TypeError, () => instance.subtract(7n), "bigint");
+assert.throws(TypeError, () => instance.subtract([]), "array");
+assert.throws(TypeError, () => instance.subtract(() => {}), "function");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-singular-properties.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-singular-properties.js
new file mode 100644
index 0000000000..c033a487eb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-singular-properties.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.plaindatetime.prototype.subtract
+description: Singular properties in the property bag are always ignored
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 15, 30, 45, 987, 654, 321);
+
+[
+ { year: 1 },
+ { month: 2 },
+ { week: 3 },
+ { day: 4 },
+ { hour: 5 },
+ { minute: 6 },
+ { second: 7 },
+ { millisecond: 8 },
+ { microsecond: 9 },
+ { nanosecond: 10 },
+].forEach((badObject) => {
+ assert.throws(TypeError, () => instance.subtract(badObject),
+ "Throw TypeError if temporalDurationLike is not valid");
+});
+
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-string-fractional-units-rounding-mode.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-string-fractional-units-rounding-mode.js
new file mode 100644
index 0000000000..ff4eb22bd4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-string-fractional-units-rounding-mode.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.plaindatetime.prototype.subtract
+description: Strings with fractional duration units are rounded with the correct rounding mode
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2);
+
+TemporalHelpers.assertPlainDateTime(datetime.subtract("PT1.03125H"), 2000, 5, "M05", 1, 22, 58, 7, 500, 0, 0,
+ "positive fractional units rounded with correct rounding mode");
+TemporalHelpers.assertPlainDateTime(datetime.subtract("-PT1.03125H"), 2000, 5, "M05", 2, 1, 1, 52, 500, 0, 0,
+ "negative fractional units rounded with correct rounding mode");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-string-negative-fractional-units.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-string-negative-fractional-units.js
new file mode 100644
index 0000000000..d1ce63b571
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-string-negative-fractional-units.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.plaindatetime.prototype.subtract
+description: Strings with fractional duration units are treated with the correct sign
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2);
+
+const resultHours = instance.subtract("-PT24.567890123H");
+TemporalHelpers.assertPlainDateTime(resultHours, 2000, 5, "M05", 3, 0, 34, 4, 404, 442, 800, "negative fractional hours");
+
+const resultMinutes = instance.subtract("-PT1440.567890123M");
+TemporalHelpers.assertPlainDateTime(resultMinutes, 2000, 5, "M05", 3, 0, 0, 34, 73, 407, 380, "negative fractional minutes");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-string.js
new file mode 100644
index 0000000000..53f5e8101a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-string.js
@@ -0,0 +1,16 @@
+// |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.plaindatetime.prototype.subtract
+description: A string is parsed into the correct object when passed as the argument
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = Temporal.PlainDateTime.from({ year: 2000, month: 5, day: 2, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321 });
+const result = instance.subtract("P3D");
+TemporalHelpers.assertPlainDateTime(result, 2000, 4, "M04", 29, 0, 34, 56, 987, 654, 321);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/balance-negative-time-units.js
new file mode 100644
index 0000000000..fd8f37b576
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/balance-negative-time-units.js
@@ -0,0 +1,52 @@
+// |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.plaindatetime.prototype.subtract
+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-addtime step 8:
+ 8. Return ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_).
+ sec-temporal-adddatetime step 1:
+ 1. Let _timeResult_ be ? AddTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_, _hours_, _minutes_, _seconds_, _milliseconds_, _microseconds_, _nanoseconds_).
+ sec-temporal.plaindatetime.prototype.subtract step 5:
+ 5. Let _result_ be ? AddDateTime(_dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], _dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], _dateTime_.[[Calendar]], −_duration_.[[Years]], −_duration_.[[Months]], −_duration_.[[Weeks]], −_duration_.[[Days]], −_duration_.[[Hours]], −_duration_.[[Minutes]], −_duration_.[[Seconds]], −_duration_.[[Milliseconds]], −_duration_.[[Microseconds]], −_duration_.[[Nanoseconds]], _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(1996, 5, 2, 1, 1, 1, 1, 1, 1);
+
+const result1 = datetime.subtract(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, 2));
+TemporalHelpers.assertPlainDateTime(result1, 1996, 5, "M05", 2, 1, 1, 1, 1, 0, 999, "nanoseconds balance");
+
+const result2 = datetime.subtract(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 2));
+TemporalHelpers.assertPlainDateTime(result2, 1996, 5, "M05", 2, 1, 1, 1, 0, 999, 1, "microseconds balance");
+
+const result3 = datetime.subtract(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 2));
+TemporalHelpers.assertPlainDateTime(result3, 1996, 5, "M05", 2, 1, 1, 0, 999, 1, 1, "milliseconds balance");
+
+const result4 = datetime.subtract(new Temporal.Duration(0, 0, 0, 0, 0, 0, 2));
+TemporalHelpers.assertPlainDateTime(result4, 1996, 5, "M05", 2, 1, 0, 59, 1, 1, 1, "seconds balance");
+
+const result5 = datetime.subtract(new Temporal.Duration(0, 0, 0, 0, 0, 2));
+TemporalHelpers.assertPlainDateTime(result5, 1996, 5, "M05", 2, 0, 59, 1, 1, 1, 1, "minutes balance");
+
+const result6 = datetime.subtract(new Temporal.Duration(0, 0, 0, 0, 2));
+TemporalHelpers.assertPlainDateTime(result6, 1996, 5, "M05", 1, 23, 1, 1, 1, 1, 1, "hours balance");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/branding.js
new file mode 100644
index 0000000000..d2bd3b7bef
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/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.plaindatetime.prototype.subtract
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const subtract = Temporal.PlainDateTime.prototype.subtract;
+
+assert.sameValue(typeof subtract, "function");
+
+const args = [new Temporal.Duration(5)];
+
+assert.throws(TypeError, () => subtract.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => subtract.apply(null, args), "null");
+assert.throws(TypeError, () => subtract.apply(true, args), "true");
+assert.throws(TypeError, () => subtract.apply("", args), "empty string");
+assert.throws(TypeError, () => subtract.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => subtract.apply(1, args), "1");
+assert.throws(TypeError, () => subtract.apply({}, args), "plain object");
+assert.throws(TypeError, () => subtract.apply(Temporal.PlainDateTime, args), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => subtract.apply(Temporal.PlainDateTime.prototype, args), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..2e67ff726e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plaindatetime.prototype.subtract
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dateAddOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dateAdd");
+Object.defineProperty(Temporal.Calendar.prototype, "dateAdd", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("dateAdd should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, "iso8601");
+instance.subtract(new Temporal.Duration(1));
+
+Object.defineProperty(Temporal.Calendar.prototype, "dateAdd", dateAddOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/builtin.js
new file mode 100644
index 0000000000..2abbf5b33f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/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.plaindatetime.prototype.subtract
+description: >
+ Tests that Temporal.PlainDateTime.prototype.subtract
+ 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.PlainDateTime.prototype.subtract),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.prototype.subtract),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.prototype.subtract),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.prototype.subtract.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/calendar-dateadd.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/calendar-dateadd.js
new file mode 100644
index 0000000000..7b6ec5cff6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/calendar-dateadd.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.plaindatetime.prototype.subtract
+description: PlainDateTime.prototype.subtract should call dateAdd with the appropriate values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ dateAdd(plainDate, duration, options) {
+ ++calls;
+ TemporalHelpers.assertPlainDate(plainDate, 2020, 3, "M03", 14, "plainDate argument");
+ TemporalHelpers.assertDuration(duration, 0, -10, 0, -1, 0, 0, 0, 0, 0, 0, "duration argument");
+ assert.sameValue(typeof options, "object", "options argument: type");
+ assert.sameValue(Object.getPrototypeOf(options), null, "options argument: prototype");
+ return super.dateAdd(plainDate, duration, options);
+ }
+}
+
+const plainDateTime = new Temporal.PlainDateTime(2020, 3, 14, 12, 34, 56, 987, 654, 321, new CustomCalendar());
+const result = plainDateTime.subtract({ months: 10, hours: 14 });
+TemporalHelpers.assertPlainDateTime(result, 2019, 5, "M05", 13, 22, 34, 56, 987, 654, 321);
+assert.sameValue(calls, 1, "should have called dateAdd");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/hour-overflow.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/hour-overflow.js
new file mode 100644
index 0000000000..6da61f9205
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/hour-overflow.js
@@ -0,0 +1,33 @@
+// |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.plaindatetime.prototype.subtract
+description: Testing overflow hours (subtracting hours that push one to the next/previous day)
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const dt = new Temporal.PlainDateTime(2019, 10, 29, 10, 46, 38, 271, 986, 102);
+const later = new Temporal.PlainDateTime(2020, 5, 31, 23, 12, 38, 271, 986, 102);
+
+TemporalHelpers.assertPlainDateTime(
+ dt.subtract({ hours: 12 }),
+ 2019, 10, "M10", 28, 22, 46, 38, 271, 986, 102,
+ "subtract result"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ dt.add({ hours: -12 }),
+ 2019, 10, "M10", 28, 22, 46, 38, 271, 986, 102,
+ "hour overflow (pushes to previous day)"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ later.subtract({ hours: -2 }),
+ 2020, 6, "M06", 1, 1, 12, 38, 271, 986, 102,
+ "subtracting a negative amount of hours is equivalent to adding hours"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..b91a8d9857
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/infinity-throws-rangeerror.js
@@ -0,0 +1,38 @@
+// |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.
+
+/*---
+description: Temporal.PlainDateTime.prototype.subtract throws a RangeError if any value in a property bag is Infinity
+esid: sec-temporal.plaindatetime.prototype.subtract
+features: [Temporal]
+---*/
+
+const overflows = ["constrain", "reject"];
+const fields = ["years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"];
+
+const instance = Temporal.PlainDateTime.from({ year: 2000, month: 5, day: 2, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321 });
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.subtract({ [field]: Infinity }, { overflow }));
+ });
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return Infinity;
+ }
+};
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => instance.subtract({ [field]: obj }, { overflow }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/length.js
new file mode 100644
index 0000000000..ccb418cc58
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/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.plaindatetime.prototype.subtract
+description: Temporal.PlainDateTime.prototype.subtract.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.PlainDateTime.prototype.subtract, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/limits.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/limits.js
new file mode 100644
index 0000000000..a71c40fc40
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/limits.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.plaindatetime.prototype.subtract
+description: Checking limits of representable PlainDateTime
+features: [Temporal]
+---*/
+
+const min = new Temporal.PlainDateTime(-271821, 4, 19, 0, 0, 0, 0, 0, 1);
+const max = new Temporal.PlainDateTime(275760, 9, 13, 23, 59, 59, 999, 999, 999);
+
+["reject", "constrain"].forEach((overflow) => {
+ assert.throws(
+ RangeError,
+ () => min.subtract({ nanoseconds: 1 }, { overflow }),
+ `subtracting 1 nanosecond beyond minimum limit (overflow = ${overflow})`
+ );
+ assert.throws(
+ RangeError,
+ () => max.subtract({ nanoseconds: -1 }, { overflow }),
+ `subtracting -1 nanosecond beyond maximum limit (overflow = ${overflow})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/name.js
new file mode 100644
index 0000000000..beaac9ddae
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/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.plaindatetime.prototype.subtract
+description: Temporal.PlainDateTime.prototype.subtract.name is "subtract".
+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.PlainDateTime.prototype.subtract, "name", {
+ value: "subtract",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/negative-duration.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/negative-duration.js
new file mode 100644
index 0000000000..c4bd186e02
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/negative-duration.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.plaindatetime.prototype.subtract
+description: Negative durations can be supplied
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const jan31 = new Temporal.PlainDateTime(2020, 1, 31, 15, 0);
+
+TemporalHelpers.assertPlainDateTime(
+ jan31.subtract({ minutes: -30 }),
+ 2020, 1, "M01", 31, 15, 30, 0, 0, 0, 0,
+ "negative minutes"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ jan31.subtract({ seconds: -30 }),
+ 2020, 1, "M01", 31, 15, 0, 30, 0, 0, 0,
+ "negative seconds"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/negative-infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/negative-infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..e767d44cb7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/negative-infinity-throws-rangeerror.js
@@ -0,0 +1,38 @@
+// |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.
+
+/*---
+description: Temporal.PlainDateTime.prototype.subtract throws a RangeError if any value in a property bag is -Infinity
+esid: sec-temporal.plaindatetime.prototype.subtract
+features: [Temporal]
+---*/
+
+const overflows = ["constrain", "reject"];
+const fields = ["years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"];
+
+const instance = Temporal.PlainDateTime.from({ year: 2000, month: 5, day: 2, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321 });
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.subtract({ [field]: -Infinity }, { overflow }));
+ });
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return -Infinity;
+ }
+};
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => instance.subtract({ [field]: obj }, { overflow }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/non-integer-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/non-integer-throws-rangeerror.js
new file mode 100644
index 0000000000..d9e9765eb1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/non-integer-throws-rangeerror.js
@@ -0,0 +1,29 @@
+// |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.plaindatetime.prototype.subtract
+description: A non-integer value for any recognized property in the property bag, throws a RangeError
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 15, 30, 45, 987, 654, 321);
+const fields = [
+ "years",
+ "months",
+ "weeks",
+ "days",
+ "hours",
+ "minutes",
+ "seconds",
+ "milliseconds",
+ "microseconds",
+ "nanoseconds",
+];
+fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.subtract({ [field]: 1.5 }));
+ assert.throws(RangeError, () => instance.subtract({ [field]: -1.5 }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/not-a-constructor.js
new file mode 100644
index 0000000000..eef1ef6b6f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/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.plaindatetime.prototype.subtract
+description: >
+ Temporal.PlainDateTime.prototype.subtract 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.PlainDateTime.prototype.subtract();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.prototype.subtract), false,
+ "isConstructor(Temporal.PlainDateTime.prototype.subtract)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/options-empty.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/options-empty.js
new file mode 100644
index 0000000000..a26714caef
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/options-empty.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.plaindatetime.prototype.subtract
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const jan31 = new Temporal.PlainDateTime(2020, 1, 31, 15, 0);
+
+TemporalHelpers.assertPlainDateTime(
+ jan31.subtract({ months: 2 }, {}),
+ 2019, 11, "M11", 30, 15, 0, 0, 0, 0, 0,
+ "options may be empty object"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ jan31.subtract({ months: 2 }, () => {}),
+ 2019, 11, "M11", 30, 15, 0, 0, 0, 0, 0,
+ "options may be function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/options-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/options-invalid.js
new file mode 100644
index 0000000000..9fc78fd1aa
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/options-invalid.js
@@ -0,0 +1,23 @@
+// |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.plaindatetime.prototype.subtract
+description: Various invalid (wrong type) values for options argument
+features: [Temporal, Symbol]
+---*/
+
+const jan31 = new Temporal.PlainDateTime(2020, 1, 31, 15, 0);
+
+const badOptions = [null, 1, 'hello', true, Symbol('foo'), 1n];
+
+badOptions.forEach((bad) => {
+ assert.throws(
+ TypeError,
+ () => jan31.subtract({ years: 1 }, bad),
+ `invalid options (${typeof bad})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/options-undefined.js
new file mode 100644
index 0000000000..81f22694a9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/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.plaindatetime.prototype.subtract
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 3, 31, 12, 34, 56, 987, 654, 321);
+const duration = { months: 1 };
+
+const explicit = datetime.subtract(duration, undefined);
+assert.sameValue(explicit.month, 2, "default overflow is constrain");
+assert.sameValue(explicit.day, 29, "default overflow is constrain");
+
+const implicit = datetime.subtract(duration);
+assert.sameValue(implicit.month, 2, "default overflow is constrain");
+assert.sameValue(implicit.day, 29, "default overflow is constrain");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/options-wrong-type.js
new file mode 100644
index 0000000000..bea427754a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/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.plaindatetime.prototype.subtract
+description: TypeError thrown when options argument is a primitive
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const badOptions = [
+ null,
+ true,
+ "some string",
+ Symbol(),
+ 1,
+ 2n,
+];
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2);
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.subtract({ months: 1 }, value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/order-of-operations.js
new file mode 100644
index 0000000000..2dff5bdcf7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/order-of-operations.js
@@ -0,0 +1,127 @@
+// |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.plaindatetime.prototype.subtract
+description: Properties on an object passed to subtract() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ // ToTemporalDurationRecord
+ "get fields.days",
+ "get fields.days.valueOf",
+ "call fields.days.valueOf",
+ "get fields.hours",
+ "get fields.hours.valueOf",
+ "call fields.hours.valueOf",
+ "get fields.microseconds",
+ "get fields.microseconds.valueOf",
+ "call fields.microseconds.valueOf",
+ "get fields.milliseconds",
+ "get fields.milliseconds.valueOf",
+ "call fields.milliseconds.valueOf",
+ "get fields.minutes",
+ "get fields.minutes.valueOf",
+ "call fields.minutes.valueOf",
+ "get fields.months",
+ "get fields.months.valueOf",
+ "call fields.months.valueOf",
+ "get fields.nanoseconds",
+ "get fields.nanoseconds.valueOf",
+ "call fields.nanoseconds.valueOf",
+ "get fields.seconds",
+ "get fields.seconds.valueOf",
+ "call fields.seconds.valueOf",
+ "get fields.weeks",
+ "get fields.weeks.valueOf",
+ "call fields.weeks.valueOf",
+ "get fields.years",
+ "get fields.years.valueOf",
+ "call fields.years.valueOf",
+ // AddDateTime -> AddDate
+ "get this.calendar.dateAdd",
+ "call this.calendar.dateAdd",
+ // inside Calendar.p.dateAdd
+ "get options.overflow",
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+];
+const actual = [];
+
+const calendar = TemporalHelpers.calendarObserver(actual, "this.calendar");
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+// clear observable operations that occurred during the constructor call
+actual.splice(0);
+
+const fields = TemporalHelpers.propertyBagObserver(actual, {
+ years: 1,
+ months: 1,
+ weeks: 1,
+ days: 1,
+ hours: 1,
+ minutes: 1,
+ seconds: 1,
+ milliseconds: 1,
+ microseconds: 1,
+ nanoseconds: 1,
+}, "fields");
+
+const options = TemporalHelpers.propertyBagObserver(actual, { overflow: "constrain" }, "options");
+
+instance.subtract(fields, options);
+assert.compareArray(actual, expected, "order of operations");
+
+actual.splice(0); // clear
+
+const noCalendarExpected = [
+ // ToTemporalDurationRecord
+ "get fields.days",
+ "get fields.days.valueOf",
+ "call fields.days.valueOf",
+ "get fields.hours",
+ "get fields.hours.valueOf",
+ "call fields.hours.valueOf",
+ "get fields.microseconds",
+ "get fields.microseconds.valueOf",
+ "call fields.microseconds.valueOf",
+ "get fields.milliseconds",
+ "get fields.milliseconds.valueOf",
+ "call fields.milliseconds.valueOf",
+ "get fields.minutes",
+ "get fields.minutes.valueOf",
+ "call fields.minutes.valueOf",
+ "get fields.months",
+ "get fields.nanoseconds",
+ "get fields.nanoseconds.valueOf",
+ "call fields.nanoseconds.valueOf",
+ "get fields.seconds",
+ "get fields.seconds.valueOf",
+ "call fields.seconds.valueOf",
+ "get fields.weeks",
+ "get fields.years",
+ "get this.calendar.dateAdd",
+ // AddDateTime -> AddDate
+ "get options.overflow",
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+];
+
+const noCalendarFields = TemporalHelpers.propertyBagObserver(actual, {
+ days: 1,
+ hours: 1,
+ minutes: 1,
+ seconds: 1,
+ milliseconds: 1,
+ microseconds: 1,
+ nanoseconds: 1,
+}, "fields");
+
+instance.subtract(noCalendarFields, options);
+assert.compareArray(actual, noCalendarExpected, "order of operations with no calendar operation");
+
+actual.splice(0); // clear
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/overflow-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/overflow-invalid-string.js
new file mode 100644
index 0000000000..65a10f333f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/overflow-invalid-string.js
@@ -0,0 +1,33 @@
+// |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.plaindatetime.prototype.subtract
+description: RangeError thrown when overflow option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal.calendar.prototype.dateadd step 7:
+ 7. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal-adddatetime step 4:
+ 4. Let _addedDate_ be ? CalendarDateAdd(_calendar_, _datePart_, _dateDuration_, _options_).
+ sec-temporal.plaindatetime.prototype.subtract step 5:
+ 5. Let _result_ be ? AddDateTime(_dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], _dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], _dateTime_.[[Calendar]], −_duration_.[[Years]], −_duration_.[[Months]], −_duration_.[[Weeks]], −_duration_.[[Days]], −_duration_.[[Hours]], −_duration_.[[Minutes]], −_duration_.[[Seconds]], −_duration_.[[Milliseconds]], −_duration_.[[Microseconds]], −_duration_.[[Nanoseconds]], _options_).
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDateTime(2000, 5, 2, 12);
+const duration = new Temporal.Duration(3, 3, 0, 3, 3);
+const badOverflows = ["", "CONSTRAIN", "balance", "other string", "constra\u0131n", "reject\0"];
+for (const overflow of badOverflows) {
+ assert.throws(
+ RangeError,
+ () => date.subtract(duration, { overflow }),
+ `invalid overflow ("${overflow}")`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/overflow-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/overflow-undefined.js
new file mode 100644
index 0000000000..ddcfaf39fa
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/overflow-undefined.js
@@ -0,0 +1,31 @@
+// |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.plaindate.prototype.subtract
+description: Fallback value for overflow option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal.calendar.prototype.dateadd step 7:
+ 7. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal-adddatetime step 4:
+ 4. Let _addedDate_ be ? CalendarDateAdd(_calendar_, _datePart_, _dateDuration_, _options_).
+ sec-temporal.plaindatetime.prototype.subtract step 5:
+ 5. Let _result_ be ? AddDateTime(_dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], _dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], _dateTime_.[[Calendar]], −_duration_.[[Years]], −_duration_.[[Months]], −_duration_.[[Weeks]], −_duration_.[[Days]], −_duration_.[[Hours]], −_duration_.[[Minutes]], −_duration_.[[Seconds]], −_duration_.[[Milliseconds]], −_duration_.[[Microseconds]], −_duration_.[[Nanoseconds]], _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 31, 12);
+const duration = new Temporal.Duration(3, 1);
+
+const explicit = datetime.subtract(duration, { overflow: undefined });
+TemporalHelpers.assertPlainDateTime(explicit, 1997, 4, "M04", 30, 12, 0, 0, 0, 0, 0, "default overflow is constrain");
+const implicit = datetime.subtract(duration, {});
+TemporalHelpers.assertPlainDateTime(implicit, 1997, 4, "M04", 30, 12, 0, 0, 0, 0, 0, "default overflow is constrain");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/overflow-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/overflow-wrong-type.js
new file mode 100644
index 0000000000..debacd548a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/overflow-wrong-type.js
@@ -0,0 +1,30 @@
+// |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.plaindatetime.prototype.subtract
+description: Type conversions for overflow option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal.calendar.prototype.dateadd step 7:
+ 7. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal-adddatetime step 4:
+ 4. Let _addedDate_ be ? CalendarDateAdd(_calendar_, _datePart_, _dateDuration_, _options_).
+ sec-temporal.plaindatetime.prototype.subtract step 5:
+ 5. Let _result_ be ? AddDateTime(_dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], _dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], _dateTime_.[[Calendar]], −_duration_.[[Years]], −_duration_.[[Months]], −_duration_.[[Weeks]], −_duration_.[[Days]], −_duration_.[[Hours]], −_duration_.[[Minutes]], −_duration_.[[Seconds]], −_duration_.[[Milliseconds]], −_duration_.[[Microseconds]], −_duration_.[[Nanoseconds]], _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12);
+const duration = new Temporal.Duration(3, 3, 0, 3, 3);
+TemporalHelpers.checkStringOptionWrongType("overflow", "constrain",
+ (overflow) => datetime.subtract(duration, { overflow }),
+ (result, descr) => TemporalHelpers.assertPlainDateTime(result, 1997, 1, "M01", 30, 9, 0, 0, 0, 0, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/prop-desc.js
new file mode 100644
index 0000000000..19a42d65fa
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/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.plaindatetime.prototype.subtract
+description: The "subtract" property of Temporal.PlainDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.prototype.subtract,
+ "function",
+ "`typeof PlainDateTime.prototype.subtract` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime.prototype, "subtract", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/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/PlainDateTime/prototype/subtract/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/subclassing-ignored.js
new file mode 100644
index 0000000000..7bc53d362e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/subclassing-ignored.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.plaindatetime.prototype.subtract
+description: Objects of a subclass are never created as return values for subtract()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.PlainDateTime,
+ [2000, 5, 2, 12, 34, 56, 987, 654, 321],
+ "subtract",
+ [{ nanoseconds: 1 }],
+ (result) => TemporalHelpers.assertPlainDateTime(result, 2000, 5, "M05", 2, 12, 34, 56, 987, 654, 320),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/basic.js
new file mode 100644
index 0000000000..08c59f7a6f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/basic.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.plaindatetime.prototype.tojson
+description: Basic behavior for toJSON
+features: [Temporal]
+---*/
+
+const tests = [
+ [new Temporal.PlainDateTime(1976, 2, 4, 5, 3, 1), "1976-02-04T05:03:01"],
+ [new Temporal.PlainDateTime(1976, 11, 18, 15, 23), "1976-11-18T15:23:00"],
+ [new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30), "1976-11-18T15:23:30"],
+ [new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 400), "1976-11-18T15:23:30.1234"],
+];
+
+const options = new Proxy({}, {
+ get() { throw new Test262Error("should not get properties off argument") }
+});
+for (const [datetime, expected] of tests) {
+ assert.sameValue(datetime.toJSON(), expected, "toJSON without argument");
+ assert.sameValue(datetime.toJSON(options), expected, "toJSON with argument");
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/branding.js
new file mode 100644
index 0000000000..1463d2a4cf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/branding.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.plaindatetime.prototype.tojson
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toJSON = Temporal.PlainDateTime.prototype.toJSON;
+
+assert.sameValue(typeof toJSON, "function");
+
+assert.throws(TypeError, () => toJSON.call(undefined), "undefined");
+assert.throws(TypeError, () => toJSON.call(null), "null");
+assert.throws(TypeError, () => toJSON.call(true), "true");
+assert.throws(TypeError, () => toJSON.call(""), "empty string");
+assert.throws(TypeError, () => toJSON.call(Symbol()), "symbol");
+assert.throws(TypeError, () => toJSON.call(1), "1");
+assert.throws(TypeError, () => toJSON.call({}), "plain object");
+assert.throws(TypeError, () => toJSON.call(Temporal.PlainDateTime), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => toJSON.call(Temporal.PlainDateTime.prototype), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..d9ab62d1cc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plaindatetime.prototype.tojson
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const idOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "id");
+Object.defineProperty(Temporal.Calendar.prototype, "id", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("id should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, "iso8601");
+instance.toJSON();
+
+Object.defineProperty(Temporal.Calendar.prototype, "id", idOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/builtin.js
new file mode 100644
index 0000000000..d28100188d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/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.plaindatetime.prototype.tojson
+description: >
+ Tests that Temporal.PlainDateTime.prototype.toJSON
+ 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.PlainDateTime.prototype.toJSON),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.prototype.toJSON),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.prototype.toJSON),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.prototype.toJSON.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/length.js
new file mode 100644
index 0000000000..9920ebb9ca
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/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.plaindatetime.prototype.tojson
+description: Temporal.PlainDateTime.prototype.toJSON.length is 0
+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.PlainDateTime.prototype.toJSON, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/name.js
new file mode 100644
index 0000000000..6ff7b93cce
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/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.plaindatetime.prototype.tojson
+description: Temporal.PlainDateTime.prototype.toJSON.name is "toJSON".
+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.PlainDateTime.prototype.toJSON, "name", {
+ value: "toJSON",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/not-a-constructor.js
new file mode 100644
index 0000000000..ed5ee396d9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/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.plaindatetime.prototype.tojson
+description: >
+ Temporal.PlainDateTime.prototype.toJSON 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.PlainDateTime.prototype.toJSON();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.prototype.toJSON), false,
+ "isConstructor(Temporal.PlainDateTime.prototype.toJSON)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/prop-desc.js
new file mode 100644
index 0000000000..2683d24810
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/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.plaindatetime.prototype.tojson
+description: The "toJSON" property of Temporal.PlainDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.prototype.toJSON,
+ "function",
+ "`typeof PlainDateTime.prototype.toJSON` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime.prototype, "toJSON", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/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/PlainDateTime/prototype/toJSON/year-format.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/year-format.js
new file mode 100644
index 0000000000..5f8f2010e2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toJSON/year-format.js
@@ -0,0 +1,50 @@
+// |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.plaindatetime.prototype.tojson
+description: Verify that the year is appropriately formatted as 4 or 6 digits
+features: [Temporal]
+---*/
+
+let instance = new Temporal.PlainDateTime(-100000, 12, 3, 4, 56, 7, 890);
+assert.sameValue(instance.toJSON(), "-100000-12-03T04:56:07.89", "large negative year formatted as 6-digit");
+
+instance = new Temporal.PlainDateTime(-10000, 4, 5, 6, 7, 8, 910);
+assert.sameValue(instance.toJSON(), "-010000-04-05T06:07:08.91", "smallest 5-digit negative year formatted as 6-digit");
+
+instance = new Temporal.PlainDateTime(-9999, 6, 7, 8, 9, 10, 987);
+assert.sameValue(instance.toJSON(), "-009999-06-07T08:09:10.987", "largest 4-digit negative year formatted as 6-digit");
+
+instance = new Temporal.PlainDateTime(-1000, 8, 9, 10, 9, 8, 765);
+assert.sameValue(instance.toJSON(), "-001000-08-09T10:09:08.765", "smallest 4-digit negative year formatted as 6-digit");
+
+instance = new Temporal.PlainDateTime(-999, 10, 9, 8, 7, 6, 543);
+assert.sameValue(instance.toJSON(), "-000999-10-09T08:07:06.543", "largest 3-digit negative year formatted as 6-digit");
+
+instance = new Temporal.PlainDateTime(-1, 8, 7, 6, 54, 32, 100);
+assert.sameValue(instance.toJSON(), "-000001-08-07T06:54:32.1", "year -1 formatted as 6-digit");
+
+instance = new Temporal.PlainDateTime(0, 6, 5, 4, 32, 10, 123);
+assert.sameValue(instance.toJSON(), "0000-06-05T04:32:10.123", "year 0 formatted as 4-digit");
+
+instance = new Temporal.PlainDateTime(1, 4, 3, 21, 0, 12, 345);
+assert.sameValue(instance.toJSON(), "0001-04-03T21:00:12.345", "year 1 formatted as 4-digit");
+
+instance = new Temporal.PlainDateTime(999, 2, 10, 12, 34, 56, 789);
+assert.sameValue(instance.toJSON(), "0999-02-10T12:34:56.789", "largest 3-digit positive year formatted as 4-digit");
+
+instance = new Temporal.PlainDateTime(1000, 1, 23, 4, 56, 7, 890);
+assert.sameValue(instance.toJSON(), "1000-01-23T04:56:07.89", "smallest 4-digit positive year formatted as 4-digit");
+
+instance = new Temporal.PlainDateTime(9999, 4, 5, 6, 7, 8, 910);
+assert.sameValue(instance.toJSON(), "9999-04-05T06:07:08.91", "largest 4-digit positive year formatted as 4-digit");
+
+instance = new Temporal.PlainDateTime(10000, 6, 7, 8, 9, 10, 987);
+assert.sameValue(instance.toJSON(), "+010000-06-07T08:09:10.987", "smallest 5-digit positive year formatted as 6-digit");
+
+instance = new Temporal.PlainDateTime(100000, 8, 9, 10, 9, 8, 765);
+assert.sameValue(instance.toJSON(), "+100000-08-09T10:09:08.765", "large positive year formatted as 6-digit");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/branding.js
new file mode 100644
index 0000000000..bca2248179
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/branding.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.plaindatetime.prototype.tolocalestring
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toLocaleString = Temporal.PlainDateTime.prototype.toLocaleString;
+
+assert.sameValue(typeof toLocaleString, "function");
+
+assert.throws(TypeError, () => toLocaleString.call(undefined), "undefined");
+assert.throws(TypeError, () => toLocaleString.call(null), "null");
+assert.throws(TypeError, () => toLocaleString.call(true), "true");
+assert.throws(TypeError, () => toLocaleString.call(""), "empty string");
+assert.throws(TypeError, () => toLocaleString.call(Symbol()), "symbol");
+assert.throws(TypeError, () => toLocaleString.call(1), "1");
+assert.throws(TypeError, () => toLocaleString.call({}), "plain object");
+assert.throws(TypeError, () => toLocaleString.call(Temporal.PlainDateTime), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => toLocaleString.call(Temporal.PlainDateTime.prototype), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..66f7d218c3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plaindatetime.prototype.tolocalestring
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const idOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "id");
+Object.defineProperty(Temporal.Calendar.prototype, "id", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("id should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, "iso8601");
+instance.toLocaleString();
+
+Object.defineProperty(Temporal.Calendar.prototype, "id", idOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/builtin.js
new file mode 100644
index 0000000000..9f6fcf57ec
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/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.plaindatetime.prototype.tolocalestring
+description: >
+ Tests that Temporal.PlainDateTime.prototype.toLocaleString
+ 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.PlainDateTime.prototype.toLocaleString),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.prototype.toLocaleString),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.prototype.toLocaleString),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.prototype.toLocaleString.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/length.js
new file mode 100644
index 0000000000..25b9d265fb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/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.plaindatetime.prototype.tolocalestring
+description: Temporal.PlainDateTime.prototype.toLocaleString.length is 0
+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.PlainDateTime.prototype.toLocaleString, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/name.js
new file mode 100644
index 0000000000..51a6a67421
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/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.plaindatetime.prototype.tolocalestring
+description: Temporal.PlainDateTime.prototype.toLocaleString.name is "toLocaleString".
+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.PlainDateTime.prototype.toLocaleString, "name", {
+ value: "toLocaleString",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/not-a-constructor.js
new file mode 100644
index 0000000000..1016478185
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/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.plaindatetime.prototype.tolocalestring
+description: >
+ Temporal.PlainDateTime.prototype.toLocaleString 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.PlainDateTime.prototype.toLocaleString();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.prototype.toLocaleString), false,
+ "isConstructor(Temporal.PlainDateTime.prototype.toLocaleString)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/prop-desc.js
new file mode 100644
index 0000000000..09aa7925af
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/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.plaindatetime.prototype.tolocalestring
+description: The "toLocaleString" property of Temporal.PlainDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.prototype.toLocaleString,
+ "function",
+ "`typeof PlainDateTime.prototype.toLocaleString` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime.prototype, "toLocaleString", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/return-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/return-string.js
new file mode 100644
index 0000000000..e7285c35fa
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/return-string.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Kate Miháliková. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tolocalestring
+description: >
+ toLocaleString return a string.
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+assert.sameValue(typeof datetime.toLocaleString("en", { dateStyle: "short" }), "string");
+assert.sameValue(typeof datetime.toLocaleString("en", { timeStyle: "short" }), "string");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/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/PlainDateTime/prototype/toPlainDate/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/branding.js
new file mode 100644
index 0000000000..a0f01d67d7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/branding.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.plaindatetime.prototype.toplaindate
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toPlainDate = Temporal.PlainDateTime.prototype.toPlainDate;
+
+assert.sameValue(typeof toPlainDate, "function");
+
+assert.throws(TypeError, () => toPlainDate.call(undefined), "undefined");
+assert.throws(TypeError, () => toPlainDate.call(null), "null");
+assert.throws(TypeError, () => toPlainDate.call(true), "true");
+assert.throws(TypeError, () => toPlainDate.call(""), "empty string");
+assert.throws(TypeError, () => toPlainDate.call(Symbol()), "symbol");
+assert.throws(TypeError, () => toPlainDate.call(1), "1");
+assert.throws(TypeError, () => toPlainDate.call({}), "plain object");
+assert.throws(TypeError, () => toPlainDate.call(Temporal.PlainDateTime), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => toPlainDate.call(Temporal.PlainDateTime.prototype), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/builtin.js
new file mode 100644
index 0000000000..a76099be6f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/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.plaindatetime.prototype.toplaindate
+description: >
+ Tests that Temporal.PlainDateTime.prototype.toPlainDate
+ 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.PlainDateTime.prototype.toPlainDate),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.prototype.toPlainDate),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.prototype.toPlainDate),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.prototype.toPlainDate.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/length.js
new file mode 100644
index 0000000000..901088a6a1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/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.plaindatetime.prototype.toplaindate
+description: Temporal.PlainDateTime.prototype.toPlainDate.length is 0
+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.PlainDateTime.prototype.toPlainDate, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/limits.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/limits.js
new file mode 100644
index 0000000000..aece7cceb8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/limits.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.plaindatetime.prototype.toplaindate
+description: toPlainDate works at the edges of the supported range
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const min = Temporal.PlainDateTime.from('-271821-04-19T00:00:00.000000001');
+TemporalHelpers.assertPlainDate(min.toPlainDate(),
+ -271821, 4, "M04", 19, "min");
+
+const max = Temporal.PlainDateTime.from('+275760-09-13T23:59:59.999999999');
+TemporalHelpers.assertPlainDate(max.toPlainDate(),
+ 275760, 9, "M09", 13, "max");
+
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/name.js
new file mode 100644
index 0000000000..c63f347aa7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/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.plaindatetime.prototype.toplaindate
+description: Temporal.PlainDateTime.prototype.toPlainDate.name is "toPlainDate".
+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.PlainDateTime.prototype.toPlainDate, "name", {
+ value: "toPlainDate",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/not-a-constructor.js
new file mode 100644
index 0000000000..3f3871e91b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/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.plaindatetime.prototype.toplaindate
+description: >
+ Temporal.PlainDateTime.prototype.toPlainDate 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.PlainDateTime.prototype.toPlainDate();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.prototype.toPlainDate), false,
+ "isConstructor(Temporal.PlainDateTime.prototype.toPlainDate)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/prop-desc.js
new file mode 100644
index 0000000000..a6d5f85d15
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/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.plaindatetime.prototype.toplaindate
+description: The "toPlainDate" property of Temporal.PlainDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.prototype.toPlainDate,
+ "function",
+ "`typeof PlainDateTime.prototype.toPlainDate` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime.prototype, "toPlainDate", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/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/PlainDateTime/prototype/toPlainMonthDay/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/branding.js
new file mode 100644
index 0000000000..7ae3fd08d9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/branding.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.plaindatetime.prototype.toplainmonthday
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toPlainMonthDay = Temporal.PlainDateTime.prototype.toPlainMonthDay;
+
+assert.sameValue(typeof toPlainMonthDay, "function");
+
+assert.throws(TypeError, () => toPlainMonthDay.call(undefined), "undefined");
+assert.throws(TypeError, () => toPlainMonthDay.call(null), "null");
+assert.throws(TypeError, () => toPlainMonthDay.call(true), "true");
+assert.throws(TypeError, () => toPlainMonthDay.call(""), "empty string");
+assert.throws(TypeError, () => toPlainMonthDay.call(Symbol()), "symbol");
+assert.throws(TypeError, () => toPlainMonthDay.call(1), "1");
+assert.throws(TypeError, () => toPlainMonthDay.call({}), "plain object");
+assert.throws(TypeError, () => toPlainMonthDay.call(Temporal.PlainDateTime), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => toPlainMonthDay.call(Temporal.PlainDateTime.prototype), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..7b347a4d71
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/builtin-calendar-no-array-iteration.js
@@ -0,0 +1,23 @@
+// |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.plaindatetime.prototype.toplainmonthday
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.PlainDateTime(2023, 5, 2, 12, 34, 56, 987, 654, 321, "iso8601");
+instance.toPlainMonthDay();
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..0653113ec6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,37 @@
+// |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.plaindatetime.prototype.toplainmonthday
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const fieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "fields");
+Object.defineProperty(Temporal.Calendar.prototype, "fields", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("fields should not be looked up");
+ },
+});
+const monthDayFromFieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "monthDayFromFields");
+Object.defineProperty(Temporal.Calendar.prototype, "monthDayFromFields", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("monthDayFromFields should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, "iso8601");
+instance.toPlainMonthDay();
+
+Object.defineProperty(Temporal.Calendar.prototype, "fields", fieldsOriginal);
+Object.defineProperty(Temporal.Calendar.prototype, "monthDayFromFields", monthDayFromFieldsOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/builtin.js
new file mode 100644
index 0000000000..90003f6f26
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/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.plaindatetime.prototype.toplainmonthday
+description: >
+ Tests that Temporal.PlainDateTime.prototype.toPlainMonthDay
+ 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.PlainDateTime.prototype.toPlainMonthDay),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.prototype.toPlainMonthDay),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.prototype.toPlainMonthDay),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.prototype.toPlainMonthDay.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/calendar-arguments.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/calendar-arguments.js
new file mode 100644
index 0000000000..77368791ee
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/calendar-arguments.js
@@ -0,0 +1,32 @@
+// |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.plaindatetime.prototype.toplainmonthday
+description: Correct options value is passed to calendar method
+info: |
+ MonthDayFromFields ( calendar, fields [ , options ] )
+
+ 3. If options is not present, then
+ a. Set options to undefined.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ monthDayFromFields(...args) {
+ assert.sameValue(args.length, 2, "args.length");
+ assert.sameValue(typeof args[0], "object", "args[0]");
+ assert.sameValue(args[1], undefined, "args[1]");
+ return super.monthDayFromFields(...args);
+ }
+}
+const plainDateTime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 123, 456, 789, new CustomCalendar());
+const result = plainDateTime.toPlainMonthDay();
+TemporalHelpers.assertPlainMonthDay(result, "M05", 2);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/calendar-fields-iterable.js
new file mode 100644
index 0000000000..c8e11029a7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/calendar-fields-iterable.js
@@ -0,0 +1,30 @@
+// |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.plaindatetime.prototype.toplainmonthday
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plaindatetime.prototype.toplainmonthday step 4:
+ 4. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"monthCode"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "monthCode",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+datetime.toPlainMonthDay();
+
+assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/calendar-fromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/calendar-fromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..6a7537d9dd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/calendar-fromfields-called-with-null-prototype-fields.js
@@ -0,0 +1,18 @@
+// |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.plaindatetime.prototype.toplainmonthday
+description: >
+ Calendar.monthDayFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+instance.toPlainMonthDay();
+assert.sameValue(calendar.monthDayFromFieldsCallCount, 1, "monthDayFromFields should have been called on the calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/calendar-monthdayfromfields-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/calendar-monthdayfromfields-called-with-options-undefined.js
new file mode 100644
index 0000000000..d464b463c3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/calendar-monthdayfromfields-called-with-options-undefined.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.plaindatetime.prototype.toplainmonthday
+description: >
+ Calendar.monthDayFromFields method is called with undefined as the options
+ value when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+instance.toPlainMonthDay();
+assert.sameValue(calendar.monthDayFromFieldsCallCount, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..24fa34c241
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/constructor-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |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.plaindatetime.prototype.toplainmonthday
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const datetime = new Temporal.PlainDateTime(2023, 5, 1, 0, 0, 0, 0, 0, 0, calendar);
+
+assert.throws(RangeError, () => datetime.toPlainMonthDay());
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/duplicate-calendar-fields.js
new file mode 100644
index 0000000000..f0c50f3229
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/duplicate-calendar-fields.js
@@ -0,0 +1,18 @@
+// |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.plaindatetime.prototype.toplainmonthday
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['monthCode'], ['day']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const datetime = new Temporal.PlainDateTime(2023, 5, 1, 0, 0, 0, 0, 0, 0, calendar);
+
+ assert.throws(RangeError, () => datetime.toPlainMonthDay());
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/length.js
new file mode 100644
index 0000000000..923bb0098e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/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.plaindatetime.prototype.toplainmonthday
+description: Temporal.PlainDateTime.prototype.toPlainMonthDay.length is 0
+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.PlainDateTime.prototype.toPlainMonthDay, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/name.js
new file mode 100644
index 0000000000..e4451e029a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/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.plaindatetime.prototype.toplainmonthday
+description: Temporal.PlainDateTime.prototype.toPlainMonthDay.name is "toPlainMonthDay".
+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.PlainDateTime.prototype.toPlainMonthDay, "name", {
+ value: "toPlainMonthDay",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/not-a-constructor.js
new file mode 100644
index 0000000000..7a8546b6ac
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/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.plaindatetime.prototype.toplainmonthday
+description: >
+ Temporal.PlainDateTime.prototype.toPlainMonthDay 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.PlainDateTime.prototype.toPlainMonthDay();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.prototype.toPlainMonthDay), false,
+ "isConstructor(Temporal.PlainDateTime.prototype.toPlainMonthDay)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/prop-desc.js
new file mode 100644
index 0000000000..e240362c1e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/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.plaindatetime.prototype.toplainmonthday
+description: The "toPlainMonthDay" property of Temporal.PlainDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.prototype.toPlainMonthDay,
+ "function",
+ "`typeof PlainDateTime.prototype.toPlainMonthDay` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime.prototype, "toPlainMonthDay", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/proto-in-calendar-fields.js
new file mode 100644
index 0000000000..482bbf5603
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/proto-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |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.plaindatetime.prototype.toplainmonthday
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const datetime = new Temporal.PlainDateTime(2023, 5, 1, 0, 0, 0, 0, 0, 0, calendar);
+
+assert.throws(RangeError, () => datetime.toPlainMonthDay());
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/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/PlainDateTime/prototype/toPlainTime/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/basic.js
new file mode 100644
index 0000000000..319fc1ec88
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/basic.js
@@ -0,0 +1,15 @@
+// |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.plaindatetime.prototype.toplaintime
+description: Basic usage
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainDateTime = Temporal.PlainDateTime.from("2020-02-12T11:42:56.987654321+01:00[Europe/Amsterdam]");
+TemporalHelpers.assertPlainTime(plainDateTime.toPlainTime(), 11, 42, 56, 987, 654, 321);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/branding.js
new file mode 100644
index 0000000000..40602b80ff
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/branding.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.plaindatetime.prototype.toplaintime
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toPlainTime = Temporal.PlainDateTime.prototype.toPlainTime;
+
+assert.sameValue(typeof toPlainTime, "function");
+
+assert.throws(TypeError, () => toPlainTime.call(undefined), "undefined");
+assert.throws(TypeError, () => toPlainTime.call(null), "null");
+assert.throws(TypeError, () => toPlainTime.call(true), "true");
+assert.throws(TypeError, () => toPlainTime.call(""), "empty string");
+assert.throws(TypeError, () => toPlainTime.call(Symbol()), "symbol");
+assert.throws(TypeError, () => toPlainTime.call(1), "1");
+assert.throws(TypeError, () => toPlainTime.call({}), "plain object");
+assert.throws(TypeError, () => toPlainTime.call(Temporal.PlainDateTime), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => toPlainTime.call(Temporal.PlainDateTime.prototype), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/builtin.js
new file mode 100644
index 0000000000..86434ef219
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/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.plaindatetime.prototype.toplaintime
+description: >
+ Tests that Temporal.PlainDateTime.prototype.toPlainTime
+ 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.PlainDateTime.prototype.toPlainTime),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.prototype.toPlainTime),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.prototype.toPlainTime),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.prototype.toPlainTime.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/length.js
new file mode 100644
index 0000000000..46609cbece
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/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.plaindatetime.prototype.toplaintime
+description: Temporal.PlainDateTime.prototype.toPlainTime.length is 0
+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.PlainDateTime.prototype.toPlainTime, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/name.js
new file mode 100644
index 0000000000..690ec0d448
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/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.plaindatetime.prototype.toplaintime
+description: Temporal.PlainDateTime.prototype.toPlainTime.name is "toPlainTime".
+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.PlainDateTime.prototype.toPlainTime, "name", {
+ value: "toPlainTime",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/not-a-constructor.js
new file mode 100644
index 0000000000..6f82f65253
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/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.plaindatetime.prototype.toplaintime
+description: >
+ Temporal.PlainDateTime.prototype.toPlainTime 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.PlainDateTime.prototype.toPlainTime();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.prototype.toPlainTime), false,
+ "isConstructor(Temporal.PlainDateTime.prototype.toPlainTime)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/prop-desc.js
new file mode 100644
index 0000000000..33c7fe422b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/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.plaindatetime.prototype.toplaintime
+description: The "toPlainTime" property of Temporal.PlainDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.prototype.toPlainTime,
+ "function",
+ "`typeof PlainDateTime.prototype.toPlainTime` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime.prototype, "toPlainTime", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/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/PlainDateTime/prototype/toPlainYearMonth/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/branding.js
new file mode 100644
index 0000000000..ebd23a5016
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/branding.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.plaindatetime.prototype.toplainyearmonth
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toPlainYearMonth = Temporal.PlainDateTime.prototype.toPlainYearMonth;
+
+assert.sameValue(typeof toPlainYearMonth, "function");
+
+assert.throws(TypeError, () => toPlainYearMonth.call(undefined), "undefined");
+assert.throws(TypeError, () => toPlainYearMonth.call(null), "null");
+assert.throws(TypeError, () => toPlainYearMonth.call(true), "true");
+assert.throws(TypeError, () => toPlainYearMonth.call(""), "empty string");
+assert.throws(TypeError, () => toPlainYearMonth.call(Symbol()), "symbol");
+assert.throws(TypeError, () => toPlainYearMonth.call(1), "1");
+assert.throws(TypeError, () => toPlainYearMonth.call({}), "plain object");
+assert.throws(TypeError, () => toPlainYearMonth.call(Temporal.PlainDateTime), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => toPlainYearMonth.call(Temporal.PlainDateTime.prototype), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..d5a326f4d7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/builtin-calendar-no-array-iteration.js
@@ -0,0 +1,23 @@
+// |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.plaindatetime.prototype.toplainyearmonth
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.PlainDateTime(2023, 5, 2, 12, 34, 56, 987, 654, 321, "iso8601");
+instance.toPlainYearMonth();
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..bfcd5570e6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,37 @@
+// |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.plaindatetime.prototype.toplainyearmonth
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const fieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "fields");
+Object.defineProperty(Temporal.Calendar.prototype, "fields", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("fields should not be looked up");
+ },
+});
+const yearMonthFromFieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "yearMonthFromFields");
+Object.defineProperty(Temporal.Calendar.prototype, "yearMonthFromFields", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("yearMonthFromFields should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, "iso8601");
+instance.toPlainYearMonth();
+
+Object.defineProperty(Temporal.Calendar.prototype, "fields", fieldsOriginal);
+Object.defineProperty(Temporal.Calendar.prototype, "yearMonthFromFields", yearMonthFromFieldsOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/builtin.js
new file mode 100644
index 0000000000..6728798787
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/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.plaindatetime.prototype.toplainyearmonth
+description: >
+ Tests that Temporal.PlainDateTime.prototype.toPlainYearMonth
+ 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.PlainDateTime.prototype.toPlainYearMonth),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.prototype.toPlainYearMonth),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.prototype.toPlainYearMonth),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.prototype.toPlainYearMonth.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/calendar-arguments.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/calendar-arguments.js
new file mode 100644
index 0000000000..fb4b716c49
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/calendar-arguments.js
@@ -0,0 +1,32 @@
+// |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.plaindatetime.prototype.toplainyearmonth
+description: Correct options value is passed to calendar method
+info: |
+ YearMonthFromFields ( calendar, fields [ , options ] )
+
+ 3. If options is not present, then
+ a. Set options to undefined.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ yearMonthFromFields(...args) {
+ assert.sameValue(args.length, 2, "args.length");
+ assert.sameValue(typeof args[0], "object", "args[0]");
+ assert.sameValue(args[1], undefined, "args[1]");
+ return super.yearMonthFromFields(...args);
+ }
+}
+const plainDateTime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 123, 456, 789, new CustomCalendar());
+const result = plainDateTime.toPlainYearMonth();
+TemporalHelpers.assertPlainYearMonth(result, 2000, 5, "M05");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/calendar-fields-iterable.js
new file mode 100644
index 0000000000..d799f96e4b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/calendar-fields-iterable.js
@@ -0,0 +1,30 @@
+// |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.plaindatetime.prototype.toplainyearmonth
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plaindatetime.prototype.toplainyearmonth step 4:
+ 4. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "monthCode",
+ "year",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+datetime.toPlainYearMonth();
+
+assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/calendar-fromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/calendar-fromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..769b532980
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/calendar-fromfields-called-with-null-prototype-fields.js
@@ -0,0 +1,18 @@
+// |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.plaindatetime.prototype.toplainyearmonth
+description: >
+ Calendar.yearMonthFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+instance.toPlainYearMonth();
+assert.sameValue(calendar.yearMonthFromFieldsCallCount, 1, "yearMonthFromFields should have been called on the calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/calendar-yearmonthfromfields-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/calendar-yearmonthfromfields-called-with-options-undefined.js
new file mode 100644
index 0000000000..a51139bca0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/calendar-yearmonthfromfields-called-with-options-undefined.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.plaindatetime.prototype.toplainyearmonth
+description: >
+ Calendar.yearMonthFromFields method is called with undefined as the options
+ value when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+instance.toPlainYearMonth();
+assert.sameValue(calendar.yearMonthFromFieldsCallCount, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..ae3cdb87c0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/constructor-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |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.plaindatetime.prototype.toplainyearmonth
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const datetime = new Temporal.PlainDateTime(2023, 5, 1, 0, 0, 0, 0, 0, 0, calendar);
+
+assert.throws(RangeError, () => datetime.toPlainYearMonth());
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/duplicate-calendar-fields.js
new file mode 100644
index 0000000000..55446f83eb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/duplicate-calendar-fields.js
@@ -0,0 +1,18 @@
+// |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.plaindatetime.prototype.toplainyearmonth
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['monthCode'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const datetime = new Temporal.PlainDateTime(2023, 5, 1, 0, 0, 0, 0, 0, 0, calendar);
+
+ assert.throws(RangeError, () => datetime.toPlainYearMonth());
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/length.js
new file mode 100644
index 0000000000..1ae6d9efd2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/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.plaindatetime.prototype.toplainyearmonth
+description: Temporal.PlainDateTime.prototype.toPlainYearMonth.length is 0
+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.PlainDateTime.prototype.toPlainYearMonth, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/name.js
new file mode 100644
index 0000000000..b2365b219d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/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.plaindatetime.prototype.toplainyearmonth
+description: Temporal.PlainDateTime.prototype.toPlainYearMonth.name is "toPlainYearMonth".
+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.PlainDateTime.prototype.toPlainYearMonth, "name", {
+ value: "toPlainYearMonth",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/not-a-constructor.js
new file mode 100644
index 0000000000..d6bb102ce7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/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.plaindatetime.prototype.toplainyearmonth
+description: >
+ Temporal.PlainDateTime.prototype.toPlainYearMonth 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.PlainDateTime.prototype.toPlainYearMonth();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.prototype.toPlainYearMonth), false,
+ "isConstructor(Temporal.PlainDateTime.prototype.toPlainYearMonth)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/prop-desc.js
new file mode 100644
index 0000000000..955de19a0b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/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.plaindatetime.prototype.toplainyearmonth
+description: The "toPlainYearMonth" property of Temporal.PlainDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.prototype.toPlainYearMonth,
+ "function",
+ "`typeof PlainDateTime.prototype.toPlainYearMonth` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime.prototype, "toPlainYearMonth", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/proto-in-calendar-fields.js
new file mode 100644
index 0000000000..fb38edd6f2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/proto-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |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.plaindatetime.prototype.toplainyearmonth
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const datetime = new Temporal.PlainDateTime(2023, 5, 1, 0, 0, 0, 0, 0, 0, calendar);
+
+assert.throws(RangeError, () => datetime.toPlainYearMonth());
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/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/PlainDateTime/prototype/toString/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/basic.js
new file mode 100644
index 0000000000..274774ef8f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/basic.js
@@ -0,0 +1,16 @@
+// |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.plaindatetime.prototype.tostring
+description: Checking the string form of an explicitly constructed instance with all arguments
+features: [Temporal]
+---*/
+
+const calendar = Temporal.Calendar.from("iso8601");
+const datetime = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789, calendar);
+
+assert.sameValue(datetime.toString(), "1976-11-18T15:23:30.123456789", "check string value");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/branding.js
new file mode 100644
index 0000000000..2758458150
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/branding.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.plaindatetime.prototype.tostring
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toString = Temporal.PlainDateTime.prototype.toString;
+
+assert.sameValue(typeof toString, "function");
+
+assert.throws(TypeError, () => toString.call(undefined), "undefined");
+assert.throws(TypeError, () => toString.call(null), "null");
+assert.throws(TypeError, () => toString.call(true), "true");
+assert.throws(TypeError, () => toString.call(""), "empty string");
+assert.throws(TypeError, () => toString.call(Symbol()), "symbol");
+assert.throws(TypeError, () => toString.call(1), "1");
+assert.throws(TypeError, () => toString.call({}), "plain object");
+assert.throws(TypeError, () => toString.call(Temporal.PlainDateTime), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => toString.call(Temporal.PlainDateTime.prototype), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..a24cc7c036
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plaindatetime.prototype.tostring
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const idOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "id");
+Object.defineProperty(Temporal.Calendar.prototype, "id", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("id should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, "iso8601");
+instance.toString();
+
+Object.defineProperty(Temporal.Calendar.prototype, "id", idOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/builtin.js
new file mode 100644
index 0000000000..d6db5a1527
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/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.plaindatetime.prototype.tostring
+description: >
+ Tests that Temporal.PlainDateTime.prototype.toString
+ 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.PlainDateTime.prototype.toString),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.prototype.toString),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.prototype.toString),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.prototype.toString.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/calendar-tostring.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/calendar-tostring.js
new file mode 100644
index 0000000000..b64c3599cb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/calendar-tostring.js
@@ -0,0 +1,56 @@
+// |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.plaindatetime.protoype.tostring
+description: Number of observable 'toString' calls on the calendar for each value of calendarName
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+let calls;
+const customCalendar = {
+ get id() {
+ ++calls;
+ return "custom";
+ },
+ toString() {
+ TemporalHelpers.assertUnreachable('toString should not be called');
+ },
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+const date = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, customCalendar);
+[
+ ["always", "2000-05-02T12:34:56.987654321[u-ca=custom]", 1],
+ ["auto", "2000-05-02T12:34:56.987654321[u-ca=custom]", 1],
+ ["critical", "2000-05-02T12:34:56.987654321[!u-ca=custom]", 1],
+ ["never", "2000-05-02T12:34:56.987654321", 0],
+ [undefined, "2000-05-02T12:34:56.987654321[u-ca=custom]", 1],
+].forEach(([calendarName, expectedResult, expectedCalls]) => {
+ calls = 0;
+ const result = date.toString({ calendarName });
+ assert.sameValue(result, expectedResult, `id for calendarName = ${calendarName}`);
+ assert.sameValue(calls, expectedCalls, `calls to id getter for calendarName = ${calendarName}`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-always.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-always.js
new file mode 100644
index 0000000000..6c251461e3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-always.js
@@ -0,0 +1,48 @@
+// |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.plaindatetime.prototype.tostring
+description: If calendarName is "always", the calendar ID should be included.
+features: [Temporal]
+---*/
+
+const calendarMethods = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+
+const tests = [
+ [[], "1976-11-18T15:23:00[u-ca=iso8601]", "built-in ISO"],
+ [[{ id: "custom", ...calendarMethods }], "1976-11-18T15:23:00[u-ca=custom]", "custom"],
+ [[{ id: "iso8601", ...calendarMethods }], "1976-11-18T15:23:00[u-ca=iso8601]", "custom with iso8601 id"],
+ [[{ id: "ISO8601", ...calendarMethods }], "1976-11-18T15:23:00[u-ca=ISO8601]", "custom with caps id"],
+ [[{ id: "\u0131so8601", ...calendarMethods }], "1976-11-18T15:23:00[u-ca=\u0131so8601]", "custom with dotless i id"],
+];
+
+for (const [args, expected, description] of tests) {
+ const date = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 0, 0, 0, 0, ...args);
+ const result = date.toString({ calendarName: "always" });
+ assert.sameValue(result, expected, `${description} calendar for calendarName = always`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-auto.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-auto.js
new file mode 100644
index 0000000000..7b5ebf5e4c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-auto.js
@@ -0,0 +1,48 @@
+// |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.plaindatetime.prototype.tostring
+description: If calendarName is "auto", "iso8601" should be omitted.
+features: [Temporal]
+---*/
+
+const calendarMethods = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+
+const tests = [
+ [[], "1976-11-18T15:23:00", "built-in ISO"],
+ [[{ id: "custom", ...calendarMethods }], "1976-11-18T15:23:00[u-ca=custom]", "custom"],
+ [[{ id: "iso8601", ...calendarMethods }], "1976-11-18T15:23:00", "custom with iso8601 id"],
+ [[{ id: "ISO8601", ...calendarMethods }], "1976-11-18T15:23:00[u-ca=ISO8601]", "custom with caps id"],
+ [[{ id: "\u0131so8601", ...calendarMethods }], "1976-11-18T15:23:00[u-ca=\u0131so8601]", "custom with dotless i id"],
+];
+
+for (const [args, expected, description] of tests) {
+ const date = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 0, 0, 0, 0, ...args);
+ const result = date.toString({ calendarName: "auto" });
+ assert.sameValue(result, expected, `${description} calendar for calendarName = auto`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-critical.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-critical.js
new file mode 100644
index 0000000000..b1846de272
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-critical.js
@@ -0,0 +1,50 @@
+// |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.plaindatetime.prototype.tostring
+description: >
+ If calendarName is "calendar", the calendar ID should be included and prefixed
+ with "!".
+features: [Temporal]
+---*/
+
+const calendarMethods = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+
+const tests = [
+ [[], "1976-11-18T15:23:00[!u-ca=iso8601]", "built-in ISO"],
+ [[{ id: "custom", ...calendarMethods }], "1976-11-18T15:23:00[!u-ca=custom]", "custom"],
+ [[{ id: "iso8601", ...calendarMethods }], "1976-11-18T15:23:00[!u-ca=iso8601]", "custom with iso8601 id"],
+ [[{ id: "ISO8601", ...calendarMethods }], "1976-11-18T15:23:00[!u-ca=ISO8601]", "custom with caps id"],
+ [[{ id: "\u0131so8601", ...calendarMethods }], "1976-11-18T15:23:00[!u-ca=\u0131so8601]", "custom with dotless i id"],
+];
+
+for (const [args, expected, description] of tests) {
+ const date = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 0, 0, 0, 0, ...args);
+ const result = date.toString({ calendarName: "critical" });
+ assert.sameValue(result, expected, `${description} calendar for calendarName = critical`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-invalid-string.js
new file mode 100644
index 0000000000..bdd5ed36cb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-invalid-string.js
@@ -0,0 +1,29 @@
+// |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.plaindatetime.protoype.tostring
+description: RangeError thrown when calendarName option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-toshowcalendaroption step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"calendarName"*, « *"string"* », « *"auto"*, *"always"*, *"never"*, *"critical"* », *"auto"*).
+ sec-temporal.plaindatetime.protoype.tostring step 6:
+ 6. Let _showCalendar_ be ? ToShowCalendarOption(_options_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const invalidValues = ["ALWAYS", "sometimes", "other string", "auto\0"];
+
+for (const calendarName of invalidValues) {
+ assert.throws(
+ RangeError,
+ () => datetime.toString({ calendarName }),
+ `${calendarName} is an invalid value for calendarName option`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-never.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-never.js
new file mode 100644
index 0000000000..8c5215fa0b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-never.js
@@ -0,0 +1,48 @@
+// |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.plaindatetime.prototype.tostring
+description: If calendarName is "never", the calendar ID should be omitted.
+features: [Temporal]
+---*/
+
+const calendarMethods = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+
+const tests = [
+ [[], "1976-11-18T15:23:00", "built-in ISO"],
+ [[{ id: "custom", ...calendarMethods }], "1976-11-18T15:23:00", "custom"],
+ [[{ id: "iso8601", ...calendarMethods }], "1976-11-18T15:23:00", "custom with iso8601 id"],
+ [[{ id: "ISO8601", ...calendarMethods }], "1976-11-18T15:23:00", "custom with caps id"],
+ [[{ id: "\u0131so8601", ...calendarMethods }], "1976-11-18T15:23:00", "custom with dotless i id"],
+];
+
+for (const [args, expected, description] of tests) {
+ const date = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 0, 0, 0, 0, ...args);
+ const result = date.toString({ calendarName: "never" });
+ assert.sameValue(result, expected, `${description} calendar for calendarName = never`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-undefined.js
new file mode 100644
index 0000000000..a888d1c815
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-undefined.js
@@ -0,0 +1,56 @@
+// |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.plaindatetime.protoype.tostring
+description: Fallback value for calendarName option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-toshowcalendaroption step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"calendarName"*, « *"string"* », « *"auto"*, *"always"*, *"never"*, *"critical"* », *"auto"*).
+ sec-temporal.plaindatetime.protoype.tostring step 6:
+ 6. Let _showCalendar_ be ? ToShowCalendarOption(_options_).
+features: [Temporal]
+---*/
+
+const calendarMethods = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+
+const tests = [
+ [[], "1976-11-18T15:23:00", "built-in ISO"],
+ [[{ id: "custom", ...calendarMethods }], "1976-11-18T15:23:00[u-ca=custom]", "custom"],
+ [[{ id: "iso8601", ...calendarMethods }], "1976-11-18T15:23:00", "custom with iso8601 id"],
+ [[{ id: "ISO8601", ...calendarMethods }], "1976-11-18T15:23:00[u-ca=ISO8601]", "custom with caps id"],
+ [[{ id: "\u0131so8601", ...calendarMethods }], "1976-11-18T15:23:00[u-ca=\u0131so8601]", "custom with dotless i id"],
+];
+
+for (const [args, expected, description] of tests) {
+ const datetime = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 0, 0, 0, 0, ...args);
+ const result = datetime.toString({ calendarName: undefined });
+ assert.sameValue(result, expected, `default calendarName option is auto with ${description} calendar`);
+ // See options-object.js for {} and options-undefined.js for absent options arg
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-wrong-type.js
new file mode 100644
index 0000000000..12b13a24cb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-wrong-type.js
@@ -0,0 +1,49 @@
+// |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.plaindatetime.protoype.tostring
+description: Type conversions for calendarName option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-toshowcalendaroption step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"calendarName"*, « *"string"* », « *"auto"*, *"always"*, *"never"*, *"critical"* », *"auto"*).
+ sec-temporal.plaindatetime.protoype.tostring step 6:
+ 6. Let _showCalendar_ be ? ToShowCalendarOption(_options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = {
+ id: "custom",
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+
+TemporalHelpers.checkStringOptionWrongType("calendarName", "auto",
+ (calendarName) => datetime.toString({ calendarName }),
+ (result, descr) => assert.sameValue(result, "2000-05-02T12:34:56.987654321[u-ca=custom]", descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-auto.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-auto.js
new file mode 100644
index 0000000000..9bee10c937
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-auto.js
@@ -0,0 +1,23 @@
+// |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.plaindatetime.prototype.tostring
+description: auto value for fractionalSecondDigits option
+features: [Temporal]
+---*/
+
+const tests = [
+ [new Temporal.PlainDateTime(1976, 2, 4, 5, 3, 1), "1976-02-04T05:03:01"],
+ [new Temporal.PlainDateTime(1976, 11, 18, 15, 23), "1976-11-18T15:23:00"],
+ [new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30), "1976-11-18T15:23:30"],
+ [new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 400), "1976-11-18T15:23:30.1234"],
+];
+
+for (const [datetime, expected] of tests) {
+ assert.sameValue(datetime.toString(), expected, "default is to emit seconds and drop trailing zeroes");
+ assert.sameValue(datetime.toString({ fractionalSecondDigits: "auto" }), expected, "auto is the default");
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-invalid-string.js
new file mode 100644
index 0000000000..917c2d7a48
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-invalid-string.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.plaindatetime.prototype.tostring
+description: RangeError thrown when fractionalSecondDigits option not one of the allowed string values
+info: |
+ sec-getstringornumberoption step 4:
+ 4. If _stringValues_ is not *undefined* and _stringValues_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-tosecondsstringprecision step 9:
+ 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
+ sec-temporal.plaindatetime.prototype.tostring step 4:
+ 4. Let _precision_ be ? ToSecondsStringPrecision(_options_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 650, 0);
+
+for (const fractionalSecondDigits of ["other string", "AUTO", "not-auto", "autos", "auto\0"]) {
+ assert.throws(RangeError, () => datetime.toString({ fractionalSecondDigits }),
+ `"${fractionalSecondDigits}" is not a valid value for fractionalSecondDigits`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-nan.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-nan.js
new file mode 100644
index 0000000000..05e1133aa9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-nan.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.plaindatetime.prototype.tostring
+description: RangeError thrown when fractionalSecondDigits option is NaN
+info: |
+ sec-getoption step 8.b:
+ b. If _value_ is *NaN*, throw a *RangeError* exception.
+ sec-getstringornumberoption step 2:
+ 2. Let _value_ be ? GetOption(_options_, _property_, « Number, String », *undefined*, _fallback_).
+ sec-temporal-tosecondsstringprecision step 9:
+ 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
+ sec-temporal.plaindatetime.prototype.tostring step 4:
+ 4. Let _precision_ be ? ToSecondsStringPrecision(_options_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 650, 0);
+assert.throws(RangeError, () => datetime.toString({ fractionalSecondDigits: NaN }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-non-integer.js
new file mode 100644
index 0000000000..38444c4b52
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-non-integer.js
@@ -0,0 +1,32 @@
+// |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.plaindatetime.prototype.tostring
+description: Rounding for fractionalSecondDigits option
+info: |
+ sec-getstringornumberoption step 3.b:
+ b. Return floor(ℝ(_value_)).
+ sec-temporal-tosecondsstringprecision step 9:
+ 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
+ sec-temporal.plaindatetime.prototype.tostring step 4:
+ 4. Let _precision_ be ? ToSecondsStringPrecision(_options_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 650, 0);
+
+let string = datetime.toString({ fractionalSecondDigits: 2.5 });
+assert.sameValue(string, "2000-05-02T12:34:56.98", "fractionalSecondDigits 2.5 floors to 2");
+
+string = datetime.toString({ fractionalSecondDigits: 9.7 });
+assert.sameValue(string, "2000-05-02T12:34:56.987650000", "fractionalSecondDigits 9.7 floors to 9 and is not out of range");
+
+assert.throws(
+ RangeError,
+ () => datetime.toString({ fractionalSecondDigits: -0.6 }),
+ "fractionalSecondDigits -0.6 floors to -1 and is out of range"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-number.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-number.js
new file mode 100644
index 0000000000..41eb31d42d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-number.js
@@ -0,0 +1,39 @@
+// |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.plaindatetime.prototype.tostring
+description: Number for fractionalSecondDigits option
+features: [Temporal]
+---*/
+
+const fewSeconds = new Temporal.PlainDateTime(1976, 2, 4, 5, 3, 1);
+const zeroSeconds = new Temporal.PlainDateTime(1976, 11, 18, 15, 23);
+const wholeSeconds = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30);
+const subSeconds = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 400);
+
+assert.sameValue(fewSeconds.toString({ fractionalSecondDigits: 0 }), "1976-02-04T05:03:01",
+ "pads parts with 0");
+assert.sameValue(subSeconds.toString({ fractionalSecondDigits: 0 }), "1976-11-18T15:23:30",
+ "truncates 4 decimal places to 0");
+assert.sameValue(zeroSeconds.toString({ fractionalSecondDigits: 2 }), "1976-11-18T15:23:00.00",
+ "pads zero seconds to 2 decimal places");
+assert.sameValue(wholeSeconds.toString({ fractionalSecondDigits: 2 }), "1976-11-18T15:23:30.00",
+ "pads whole seconds to 2 decimal places");
+assert.sameValue(subSeconds.toString({ fractionalSecondDigits: 2 }), "1976-11-18T15:23:30.12",
+ "truncates 4 decimal places to 2");
+assert.sameValue(subSeconds.toString({ fractionalSecondDigits: 3 }), "1976-11-18T15:23:30.123",
+ "truncates 4 decimal places to 3");
+assert.sameValue(subSeconds.toString({ fractionalSecondDigits: 6 }), "1976-11-18T15:23:30.123400",
+ "pads 4 decimal places to 6");
+assert.sameValue(zeroSeconds.toString({ fractionalSecondDigits: 7 }), "1976-11-18T15:23:00.0000000",
+ "pads zero seconds to 7 decimal places");
+assert.sameValue(wholeSeconds.toString({ fractionalSecondDigits: 7 }), "1976-11-18T15:23:30.0000000",
+ "pads whole seconds to 7 decimal places");
+assert.sameValue(subSeconds.toString({ fractionalSecondDigits: 7 }), "1976-11-18T15:23:30.1234000",
+ "pads 4 decimal places to 7");
+assert.sameValue(subSeconds.toString({ fractionalSecondDigits: 9 }), "1976-11-18T15:23:30.123400000",
+ "pads 4 decimal places to 9");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-out-of-range.js
new file mode 100644
index 0000000000..945bad9c44
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-out-of-range.js
@@ -0,0 +1,29 @@
+// |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.plaindatetime.prototype.tostring
+description: RangeError thrown when fractionalSecondDigits option out of range
+info: |
+ sec-getstringornumberoption step 3.a:
+ a. If _value_ < _minimum_ or _value_ > _maximum_, throw a *RangeError* exception.
+ sec-temporal-tosecondsstringprecision step 9:
+ 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
+ sec-temporal.plaindatetime.prototype.tostring step 4:
+ 4. Let _precision_ be ? ToSecondsStringPrecision(_options_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 650, 0);
+
+assert.throws(RangeError, () => datetime.toString({ fractionalSecondDigits: -Infinity }),
+ "−∞ is out of range for fractionalSecondDigits");
+assert.throws(RangeError, () => datetime.toString({ fractionalSecondDigits: -1 }),
+ "−1 is out of range for fractionalSecondDigits");
+assert.throws(RangeError, () => datetime.toString({ fractionalSecondDigits: 10 }),
+ "10 is out of range for fractionalSecondDigits");
+assert.throws(RangeError, () => datetime.toString({ fractionalSecondDigits: Infinity }),
+ "∞ is out of range for fractionalSecondDigits");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-undefined.js
new file mode 100644
index 0000000000..a46b0f4e13
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-undefined.js
@@ -0,0 +1,38 @@
+// |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.plaindatetime.prototype.tostring
+description: Fallback value for fractionalSecondDigits option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-getstringornumberoption step 2:
+ 2. Let _value_ be ? GetOption(_options_, _property_, « Number, String », *undefined*, _fallback_).
+ sec-temporal-tosecondsstringprecision step 9:
+ 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
+ sec-temporal.plaindatetime.prototype.tostring step 4:
+ 4. Let _precision_ be ? ToSecondsStringPrecision(_options_).
+features: [Temporal]
+---*/
+
+const tests = [
+ [new Temporal.PlainDateTime(1976, 2, 4, 5, 3, 1), "1976-02-04T05:03:01"],
+ [new Temporal.PlainDateTime(1976, 11, 18, 15, 23), "1976-11-18T15:23:00"],
+ [new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30), "1976-11-18T15:23:30"],
+ [new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 400), "1976-11-18T15:23:30.1234"],
+];
+
+for (const [datetime, expected] of tests) {
+ const explicit = datetime.toString({ fractionalSecondDigits: undefined });
+ assert.sameValue(explicit, expected, "default fractionalSecondDigits is auto (property present but undefined)");
+
+ const implicit = datetime.toString({});
+ assert.sameValue(implicit, expected, "default fractionalSecondDigits is auto (property not present)");
+
+ const lambda = datetime.toString(() => {});
+ assert.sameValue(lambda, expected, "default fractionalSecondDigits is auto (property not present, function object)");
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-wrong-type.js
new file mode 100644
index 0000000000..f803a9978d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-wrong-type.js
@@ -0,0 +1,50 @@
+// |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.plaindatetime.prototype.tostring
+description: Type conversions for fractionalSecondDigits option
+info: |
+ sec-getoption steps 8–9:
+ 8. Else if _type_ is Number, then
+ a. Set _value_ to ? ToNumber(value).
+ b. ...
+ 9. Else,
+ a. Set _value_ to ? ToString(value).
+ sec-getstringornumberoption step 2:
+ 2. Let _value_ be ? GetOption(_options_, _property_, « Number, String », *undefined*, _fallback_).
+ sec-temporal-tosecondsstringprecision step 9:
+ 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
+ sec-temporal.plaindatetime.prototype.tostring step 4:
+ 4. Let _precision_ be ? ToSecondsStringPrecision(_options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 650, 0);
+
+assert.throws(RangeError, () => datetime.toString({ fractionalSecondDigits: null }),
+ "null is not a number and converts to the string 'null' which is not valid for fractionalSecondDigits");
+assert.throws(RangeError, () => datetime.toString({ fractionalSecondDigits: true }),
+ "true is not a number and converts to the string 'true' which is not valid for fractionalSecondDigits");
+assert.throws(RangeError, () => datetime.toString({ fractionalSecondDigits: false }),
+ "false is not a number and converts to the string 'false' which is not valid for fractionalSecondDigits");
+assert.throws(TypeError, () => datetime.toString({ fractionalSecondDigits: Symbol() }),
+ "symbols are not numbers and cannot convert to strings");
+assert.throws(RangeError, () => datetime.toString({ fractionalSecondDigits: 2n }),
+ "bigints are not numbers and convert to strings which are not valid for fractionalSecondDigits");
+assert.throws(RangeError, () => datetime.toString({ fractionalSecondDigits: {} }),
+ "plain objects are not numbers and convert to strings which are not valid for fractionalSecondDigits");
+
+const expected = [
+ "get fractionalSecondDigits.toString",
+ "call fractionalSecondDigits.toString",
+];
+const actual = [];
+const observer = TemporalHelpers.toPrimitiveObserver(actual, "auto", "fractionalSecondDigits");
+const result = datetime.toString({ fractionalSecondDigits: observer });
+assert.sameValue(result, "2000-05-02T12:34:56.98765", "object with toString uses toString return value");
+assert.compareArray(actual, expected, "object with toString calls toString and not valueOf");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/length.js
new file mode 100644
index 0000000000..9b5b4a9165
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/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.plaindatetime.prototype.tostring
+description: Temporal.PlainDateTime.prototype.toString.length is 0
+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.PlainDateTime.prototype.toString, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/name.js
new file mode 100644
index 0000000000..cd3fe445d4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/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.plaindatetime.prototype.tostring
+description: Temporal.PlainDateTime.prototype.toString.name is "toString".
+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.PlainDateTime.prototype.toString, "name", {
+ value: "toString",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/not-a-constructor.js
new file mode 100644
index 0000000000..82930cfe3b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/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.plaindatetime.prototype.tostring
+description: >
+ Temporal.PlainDateTime.prototype.toString 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.PlainDateTime.prototype.toString();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.prototype.toString), false,
+ "isConstructor(Temporal.PlainDateTime.prototype.toString)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/options-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/options-object.js
new file mode 100644
index 0000000000..d0c5ebe101
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/options-object.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.plaindatetime.prototype.tostring
+description: Empty or a function object may be used as options
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2);
+
+const result1 = instance.toString({});
+assert.sameValue(
+ result1, "2000-05-02T00:00:00",
+ "options may be an empty plain object"
+);
+
+const result2 = instance.toString(() => {});
+assert.sameValue(
+ result2, "2000-05-02T00:00:00",
+ "options may be a function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/options-undefined.js
new file mode 100644
index 0000000000..23248ad28c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/options-undefined.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.plaindatetime.prototype.tostring
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const calendar = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "custom",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+const datetime1 = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 650, 0);
+const datetime2 = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 650, 0, calendar);
+
+[
+ [datetime1, "2000-05-02T12:34:56.98765"],
+ [datetime2, "2000-05-02T12:34:56.98765[u-ca=custom]"],
+].forEach(([datetime, expected]) => {
+ const explicit = datetime.toString(undefined);
+ assert.sameValue(explicit, expected, "default calendarName option is auto, precision is auto, and no rounding");
+
+ const propertyImplicit = datetime.toString({});
+ assert.sameValue(propertyImplicit, expected, "default calendarName option is auto, precision is auto, and no rounding");
+
+ const implicit = datetime.toString();
+ assert.sameValue(implicit, expected, "default calendarName option is auto, precision is auto, and no rounding");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/options-wrong-type.js
new file mode 100644
index 0000000000..b473818edd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/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.plaindatetime.prototype.tostring
+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.PlainDateTime(2000, 5, 2);
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.toString(value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/order-of-operations.js
new file mode 100644
index 0000000000..fa47dcb179
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/order-of-operations.js
@@ -0,0 +1,70 @@
+// |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.plaindatetime.prototype.tostring
+description: Properties on objects passed to toString() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "get options.calendarName",
+ "get options.calendarName.toString",
+ "call options.calendarName.toString",
+ "get options.fractionalSecondDigits",
+ "get options.fractionalSecondDigits.toString",
+ "call options.fractionalSecondDigits.toString",
+ "get options.roundingMode",
+ "get options.roundingMode.toString",
+ "call options.roundingMode.toString",
+ "get options.smallestUnit",
+ "get options.smallestUnit.toString",
+ "call options.smallestUnit.toString",
+ "get this.calendar.id",
+];
+const actual = [];
+
+const calendar = TemporalHelpers.calendarObserver(actual, "this.calendar");
+const instance = new Temporal.PlainDateTime(1990, 11, 3, 15, 54, 37, 123, 456, 789, calendar);
+// clear observable operations that occurred during the constructor call
+actual.splice(0);
+
+instance.toString(
+ TemporalHelpers.propertyBagObserver(actual, {
+ fractionalSecondDigits: "auto",
+ roundingMode: "halfExpand",
+ smallestUnit: "millisecond",
+ calendarName: "auto",
+ }, "options"),
+);
+assert.compareArray(actual, expected, "order of operations");
+actual.splice(0); // clear
+
+// Same as above but without options.smallestUnit.toString
+const expectedForFractionalSecondDigits = [
+ "get options.calendarName",
+ "get options.calendarName.toString",
+ "call options.calendarName.toString",
+ "get options.fractionalSecondDigits",
+ "get options.fractionalSecondDigits.toString",
+ "call options.fractionalSecondDigits.toString",
+ "get options.roundingMode",
+ "get options.roundingMode.toString",
+ "call options.roundingMode.toString",
+ "get options.smallestUnit",
+ "get this.calendar.id",
+];
+
+instance.toString(
+ TemporalHelpers.propertyBagObserver(actual, {
+ fractionalSecondDigits: "auto",
+ roundingMode: "halfExpand",
+ smallestUnit: undefined,
+ calendarName: "auto",
+ }, "options"),
+);
+assert.compareArray(actual, expectedForFractionalSecondDigits, "order of operations with smallestUnit undefined");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/prop-desc.js
new file mode 100644
index 0000000000..8206cc471b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/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.plaindatetime.prototype.tostring
+description: The "toString" property of Temporal.PlainDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.prototype.toString,
+ "function",
+ "`typeof PlainDateTime.prototype.toString` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime.prototype, "toString", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/rounding-cross-midnight.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/rounding-cross-midnight.js
new file mode 100644
index 0000000000..a2c73e27e4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/rounding-cross-midnight.js
@@ -0,0 +1,16 @@
+// |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.plaindatetime.prototype.tostring
+description: Rounding can cross midnight
+features: [Temporal]
+---*/
+
+const plainDateTime = new Temporal.PlainDateTime(1999, 12, 31, 23, 59, 59, 999, 999, 999); // one nanosecond before 2000-01-01T00:00:00
+for (const roundingMode of ["ceil", "halfExpand"]) {
+ assert.sameValue(plainDateTime.toString({ fractionalSecondDigits: 8, roundingMode }), "2000-01-01T00:00:00.00000000");
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/rounding-direction.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/rounding-direction.js
new file mode 100644
index 0000000000..334db9466f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/rounding-direction.js
@@ -0,0 +1,33 @@
+// |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.plaindatetime.prototype.tostring
+description: Rounding down is towards the Big Bang, not the epoch or 1 BCE
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(-99, 12, 15, 12, 0, 0, 500);
+assert.sameValue(
+ instance.toString({ smallestUnit: "second", roundingMode: "floor" }),
+ "-000099-12-15T12:00:00",
+ "Rounding down is towards the Big Bang, not the epoch or 1 BCE"
+);
+assert.sameValue(
+ instance.toString({ smallestUnit: "second", roundingMode: "trunc" }),
+ "-000099-12-15T12:00:00",
+ "Rounding down is towards the Big Bang, not the epoch or 1 BCE (roundingMode trunc)"
+);
+assert.sameValue(
+ instance.toString({ smallestUnit: "second", roundingMode: "ceil" }),
+ "-000099-12-15T12:00:01",
+ "Rounding up is away from the Big Bang, not the epoch or 1 BCE (roundingMode ceil)"
+);
+assert.sameValue(
+ instance.toString({ smallestUnit: "second", roundingMode: "halfExpand" }),
+ "-000099-12-15T12:00:01",
+ "Rounding up is away from the Big Bang, not the epoch or 1 BCE (roundingMode halfExpand)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-ceil.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-ceil.js
new file mode 100644
index 0000000000..4a60e4aaa9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-ceil.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.plaindatetime.prototype.tostring
+description: ceil value for roundingMode option
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 123, 987, 500);
+
+const result1 = datetime.toString({ smallestUnit: "microsecond", roundingMode: "ceil" });
+assert.sameValue(result1, "2000-05-02T12:34:56.123988",
+ "roundingMode is ceil (with 6 digits from smallestUnit)");
+
+const result2 = datetime.toString({ fractionalSecondDigits: 6, roundingMode: "ceil" });
+assert.sameValue(result2, "2000-05-02T12:34:56.123988",
+ "roundingMode is ceil (with 6 digits from fractionalSecondDigits)");
+
+const result3 = datetime.toString({ smallestUnit: "millisecond", roundingMode: "ceil" });
+assert.sameValue(result3, "2000-05-02T12:34:56.124",
+ "roundingMode is ceil (with 3 digits from smallestUnit)");
+
+const result4 = datetime.toString({ fractionalSecondDigits: 3, roundingMode: "ceil" });
+assert.sameValue(result4, "2000-05-02T12:34:56.124",
+ "roundingMode is ceil (with 3 digits from fractionalSecondDigits)");
+
+const result5 = datetime.toString({ smallestUnit: "second", roundingMode: "ceil" });
+assert.sameValue(result5, "2000-05-02T12:34:57",
+ "roundingMode is ceil (with 0 digits from smallestUnit)");
+
+const result6 = datetime.toString({ fractionalSecondDigits: 0, roundingMode: "ceil" });
+assert.sameValue(result6, "2000-05-02T12:34:57",
+ "roundingMode is ceil (with 0 digits from fractionalSecondDigits)");
+
+const result7 = datetime.toString({ smallestUnit: "minute", roundingMode: "ceil" });
+assert.sameValue(result7, "2000-05-02T12:35", "roundingMode is ceil (round to minute)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-expand.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-expand.js
new file mode 100644
index 0000000000..44ef84dea2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-expand.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.plaindatetime.prototype.tostring
+description: expand value for roundingMode option
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 123, 987, 500);
+
+const result1 = datetime.toString({ smallestUnit: "microsecond", roundingMode: "expand" });
+assert.sameValue(result1, "2000-05-02T12:34:56.123988",
+ "roundingMode is expand (with 6 digits from smallestUnit)");
+
+const result2 = datetime.toString({ fractionalSecondDigits: 6, roundingMode: "expand" });
+assert.sameValue(result2, "2000-05-02T12:34:56.123988",
+ "roundingMode is expand (with 6 digits from fractionalSecondDigits)");
+
+const result3 = datetime.toString({ smallestUnit: "millisecond", roundingMode: "expand" });
+assert.sameValue(result3, "2000-05-02T12:34:56.124",
+ "roundingMode is expand (with 3 digits from smallestUnit)");
+
+const result4 = datetime.toString({ fractionalSecondDigits: 3, roundingMode: "expand" });
+assert.sameValue(result4, "2000-05-02T12:34:56.124",
+ "roundingMode is expand (with 3 digits from fractionalSecondDigits)");
+
+const result5 = datetime.toString({ smallestUnit: "second", roundingMode: "expand" });
+assert.sameValue(result5, "2000-05-02T12:34:57",
+ "roundingMode is expand (with 0 digits from smallestUnit)");
+
+const result6 = datetime.toString({ fractionalSecondDigits: 0, roundingMode: "expand" });
+assert.sameValue(result6, "2000-05-02T12:34:57",
+ "roundingMode is expand (with 0 digits from fractionalSecondDigits)");
+
+const result7 = datetime.toString({ smallestUnit: "minute", roundingMode: "expand" });
+assert.sameValue(result7, "2000-05-02T12:35", "roundingMode is expand (round to minute)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-floor.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-floor.js
new file mode 100644
index 0000000000..1505b99ac5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-floor.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.plaindatetime.prototype.tostring
+description: floor value for roundingMode option
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 123, 987, 500);
+
+const result1 = datetime.toString({ smallestUnit: "microsecond", roundingMode: "floor" });
+assert.sameValue(result1, "2000-05-02T12:34:56.123987",
+ "roundingMode is floor (with 6 digits from smallestUnit)");
+
+const result2 = datetime.toString({ fractionalSecondDigits: 6, roundingMode: "floor" });
+assert.sameValue(result2, "2000-05-02T12:34:56.123987",
+ "roundingMode is floor (with 6 digits from fractionalSecondDigits)");
+
+const result3 = datetime.toString({ smallestUnit: "millisecond", roundingMode: "floor" });
+assert.sameValue(result3, "2000-05-02T12:34:56.123",
+ "roundingMode is floor (with 3 digits from smallestUnit)");
+
+const result4 = datetime.toString({ fractionalSecondDigits: 3, roundingMode: "floor" });
+assert.sameValue(result4, "2000-05-02T12:34:56.123",
+ "roundingMode is floor (with 3 digits from fractionalSecondDigits)");
+
+const result5 = datetime.toString({ smallestUnit: "second", roundingMode: "floor" });
+assert.sameValue(result5, "2000-05-02T12:34:56",
+ "roundingMode is floor (with 0 digits from smallestUnit)");
+
+const result6 = datetime.toString({ fractionalSecondDigits: 0, roundingMode: "floor" });
+assert.sameValue(result6, "2000-05-02T12:34:56",
+ "roundingMode is floor (with 0 digits from fractionalSecondDigits)");
+
+const result7 = datetime.toString({ smallestUnit: "minute", roundingMode: "floor" });
+assert.sameValue(result7, "2000-05-02T12:34", "roundingMode is floor (round to minute)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-halfCeil.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-halfCeil.js
new file mode 100644
index 0000000000..4db75c3bc6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-halfCeil.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.plaindatetime.prototype.tostring
+description: halfCeil value for roundingMode option
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 123, 987, 500);
+
+const result1 = datetime.toString({ smallestUnit: "microsecond", roundingMode: "halfCeil" });
+assert.sameValue(result1, "2000-05-02T12:34:56.123988",
+ "roundingMode is halfCeil (with 6 digits from smallestUnit)");
+
+const result2 = datetime.toString({ fractionalSecondDigits: 6, roundingMode: "halfCeil" });
+assert.sameValue(result2, "2000-05-02T12:34:56.123988",
+ "roundingMode is halfCeil (with 6 digits from fractionalSecondDigits)");
+
+const result3 = datetime.toString({ smallestUnit: "millisecond", roundingMode: "halfCeil" });
+assert.sameValue(result3, "2000-05-02T12:34:56.124",
+ "roundingMode is halfCeil (with 3 digits from smallestUnit)");
+
+const result4 = datetime.toString({ fractionalSecondDigits: 3, roundingMode: "halfCeil" });
+assert.sameValue(result4, "2000-05-02T12:34:56.124",
+ "roundingMode is halfCeil (with 3 digits from fractionalSecondDigits)");
+
+const result5 = datetime.toString({ smallestUnit: "second", roundingMode: "halfCeil" });
+assert.sameValue(result5, "2000-05-02T12:34:56",
+ "roundingMode is halfCeil (with 0 digits from smallestUnit)");
+
+const result6 = datetime.toString({ fractionalSecondDigits: 0, roundingMode: "halfCeil" });
+assert.sameValue(result6, "2000-05-02T12:34:56",
+ "roundingMode is halfCeil (with 0 digits from fractionalSecondDigits)");
+
+const result7 = datetime.toString({ smallestUnit: "minute", roundingMode: "halfCeil" });
+assert.sameValue(result7, "2000-05-02T12:35", "roundingMode is halfCeil (round to minute)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-halfEven.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-halfEven.js
new file mode 100644
index 0000000000..84d09a9bae
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-halfEven.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.plaindatetime.prototype.tostring
+description: halfEven value for roundingMode option
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 123, 987, 500);
+
+const result1 = datetime.toString({ smallestUnit: "microsecond", roundingMode: "halfEven" });
+assert.sameValue(result1, "2000-05-02T12:34:56.123988",
+ "roundingMode is halfEven (with 6 digits from smallestUnit)");
+
+const result2 = datetime.toString({ fractionalSecondDigits: 6, roundingMode: "halfEven" });
+assert.sameValue(result2, "2000-05-02T12:34:56.123988",
+ "roundingMode is halfEven (with 6 digits from fractionalSecondDigits)");
+
+const result3 = datetime.toString({ smallestUnit: "millisecond", roundingMode: "halfEven" });
+assert.sameValue(result3, "2000-05-02T12:34:56.124",
+ "roundingMode is halfEven (with 3 digits from smallestUnit)");
+
+const result4 = datetime.toString({ fractionalSecondDigits: 3, roundingMode: "halfEven" });
+assert.sameValue(result4, "2000-05-02T12:34:56.124",
+ "roundingMode is halfEven (with 3 digits from fractionalSecondDigits)");
+
+const result5 = datetime.toString({ smallestUnit: "second", roundingMode: "halfEven" });
+assert.sameValue(result5, "2000-05-02T12:34:56",
+ "roundingMode is halfEven (with 0 digits from smallestUnit)");
+
+const result6 = datetime.toString({ fractionalSecondDigits: 0, roundingMode: "halfEven" });
+assert.sameValue(result6, "2000-05-02T12:34:56",
+ "roundingMode is halfEven (with 0 digits from fractionalSecondDigits)");
+
+const result7 = datetime.toString({ smallestUnit: "minute", roundingMode: "halfEven" });
+assert.sameValue(result7, "2000-05-02T12:35", "roundingMode is halfEven (round to minute)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-halfExpand.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-halfExpand.js
new file mode 100644
index 0000000000..4ef2194248
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-halfExpand.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.plaindatetime.prototype.tostring
+description: halfExpand value for roundingMode option
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 123, 987, 500);
+
+const result1 = datetime.toString({ smallestUnit: "microsecond", roundingMode: "halfExpand" });
+assert.sameValue(result1, "2000-05-02T12:34:56.123988",
+ "roundingMode is halfExpand (with 6 digits from smallestUnit)");
+
+const result2 = datetime.toString({ fractionalSecondDigits: 6, roundingMode: "halfExpand" });
+assert.sameValue(result2, "2000-05-02T12:34:56.123988",
+ "roundingMode is halfExpand (with 6 digits from fractionalSecondDigits)");
+
+const result3 = datetime.toString({ smallestUnit: "millisecond", roundingMode: "halfExpand" });
+assert.sameValue(result3, "2000-05-02T12:34:56.124",
+ "roundingMode is halfExpand (with 3 digits from smallestUnit)");
+
+const result4 = datetime.toString({ fractionalSecondDigits: 3, roundingMode: "halfExpand" });
+assert.sameValue(result4, "2000-05-02T12:34:56.124",
+ "roundingMode is halfExpand (with 3 digits from fractionalSecondDigits)");
+
+const result5 = datetime.toString({ smallestUnit: "second", roundingMode: "halfExpand" });
+assert.sameValue(result5, "2000-05-02T12:34:56",
+ "roundingMode is halfExpand (with 0 digits from smallestUnit)");
+
+const result6 = datetime.toString({ fractionalSecondDigits: 0, roundingMode: "halfExpand" });
+assert.sameValue(result6, "2000-05-02T12:34:56",
+ "roundingMode is halfExpand (with 0 digits from fractionalSecondDigits)");
+
+const result7 = datetime.toString({ smallestUnit: "minute", roundingMode: "halfExpand" });
+assert.sameValue(result7, "2000-05-02T12:35", "roundingMode is halfExpand (round to minute)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-halfFloor.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-halfFloor.js
new file mode 100644
index 0000000000..eb10bd3589
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-halfFloor.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.plaindatetime.prototype.tostring
+description: halfFloor value for roundingMode option
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 123, 987, 500);
+
+const result1 = datetime.toString({ smallestUnit: "microsecond", roundingMode: "halfFloor" });
+assert.sameValue(result1, "2000-05-02T12:34:56.123987",
+ "roundingMode is halfFloor (with 6 digits from smallestUnit)");
+
+const result2 = datetime.toString({ fractionalSecondDigits: 6, roundingMode: "halfFloor" });
+assert.sameValue(result2, "2000-05-02T12:34:56.123987",
+ "roundingMode is halfFloor (with 6 digits from fractionalSecondDigits)");
+
+const result3 = datetime.toString({ smallestUnit: "millisecond", roundingMode: "halfFloor" });
+assert.sameValue(result3, "2000-05-02T12:34:56.124",
+ "roundingMode is halfFloor (with 3 digits from smallestUnit)");
+
+const result4 = datetime.toString({ fractionalSecondDigits: 3, roundingMode: "halfFloor" });
+assert.sameValue(result4, "2000-05-02T12:34:56.124",
+ "roundingMode is halfFloor (with 3 digits from fractionalSecondDigits)");
+
+const result5 = datetime.toString({ smallestUnit: "second", roundingMode: "halfFloor" });
+assert.sameValue(result5, "2000-05-02T12:34:56",
+ "roundingMode is halfFloor (with 0 digits from smallestUnit)");
+
+const result6 = datetime.toString({ fractionalSecondDigits: 0, roundingMode: "halfFloor" });
+assert.sameValue(result6, "2000-05-02T12:34:56",
+ "roundingMode is halfFloor (with 0 digits from fractionalSecondDigits)");
+
+const result7 = datetime.toString({ smallestUnit: "minute", roundingMode: "halfFloor" });
+assert.sameValue(result7, "2000-05-02T12:35", "roundingMode is halfFloor (round to minute)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-halfTrunc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-halfTrunc.js
new file mode 100644
index 0000000000..ecad87495c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-halfTrunc.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.plaindatetime.prototype.tostring
+description: halfTrunc value for roundingMode option
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 123, 987, 500);
+
+const result1 = datetime.toString({ smallestUnit: "microsecond", roundingMode: "halfTrunc" });
+assert.sameValue(result1, "2000-05-02T12:34:56.123987",
+ "roundingMode is halfTrunc (with 6 digits from smallestUnit)");
+
+const result2 = datetime.toString({ fractionalSecondDigits: 6, roundingMode: "halfTrunc" });
+assert.sameValue(result2, "2000-05-02T12:34:56.123987",
+ "roundingMode is halfTrunc (with 6 digits from fractionalSecondDigits)");
+
+const result3 = datetime.toString({ smallestUnit: "millisecond", roundingMode: "halfTrunc" });
+assert.sameValue(result3, "2000-05-02T12:34:56.124",
+ "roundingMode is halfTrunc (with 3 digits from smallestUnit)");
+
+const result4 = datetime.toString({ fractionalSecondDigits: 3, roundingMode: "halfTrunc" });
+assert.sameValue(result4, "2000-05-02T12:34:56.124",
+ "roundingMode is halfTrunc (with 3 digits from fractionalSecondDigits)");
+
+const result5 = datetime.toString({ smallestUnit: "second", roundingMode: "halfTrunc" });
+assert.sameValue(result5, "2000-05-02T12:34:56",
+ "roundingMode is halfTrunc (with 0 digits from smallestUnit)");
+
+const result6 = datetime.toString({ fractionalSecondDigits: 0, roundingMode: "halfTrunc" });
+assert.sameValue(result6, "2000-05-02T12:34:56",
+ "roundingMode is halfTrunc (with 0 digits from fractionalSecondDigits)");
+
+const result7 = datetime.toString({ smallestUnit: "minute", roundingMode: "halfTrunc" });
+assert.sameValue(result7, "2000-05-02T12:35", "roundingMode is halfTrunc (round to minute)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-invalid-string.js
new file mode 100644
index 0000000000..f9e6668145
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-invalid-string.js
@@ -0,0 +1,16 @@
+// |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.plaindatetime.prototype.tostring
+description: RangeError thrown when roundingMode option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 123, 987, 500);
+for (const roundingMode of ["other string", "cile", "CEIL", "ce\u0131l", "auto", "halfexpand", "floor\0"]) {
+ assert.throws(RangeError, () => datetime.toString({ smallestUnit: "microsecond", roundingMode }));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-trunc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-trunc.js
new file mode 100644
index 0000000000..12080ca5cb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-trunc.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.plaindatetime.prototype.tostring
+description: trunc value for roundingMode option
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 123, 987, 500);
+
+const result1 = datetime.toString({ smallestUnit: "microsecond", roundingMode: "trunc" });
+assert.sameValue(result1, "2000-05-02T12:34:56.123987",
+ "roundingMode is trunc (with 6 digits from smallestUnit)");
+
+const result2 = datetime.toString({ fractionalSecondDigits: 6, roundingMode: "trunc" });
+assert.sameValue(result2, "2000-05-02T12:34:56.123987",
+ "roundingMode is trunc (with 6 digits from fractionalSecondDigits)");
+
+const result3 = datetime.toString({ smallestUnit: "millisecond", roundingMode: "trunc" });
+assert.sameValue(result3, "2000-05-02T12:34:56.123",
+ "roundingMode is trunc (with 3 digits from smallestUnit)");
+
+const result4 = datetime.toString({ fractionalSecondDigits: 3, roundingMode: "trunc" });
+assert.sameValue(result4, "2000-05-02T12:34:56.123",
+ "roundingMode is trunc (with 3 digits from fractionalSecondDigits)");
+
+const result5 = datetime.toString({ smallestUnit: "second", roundingMode: "trunc" });
+assert.sameValue(result5, "2000-05-02T12:34:56",
+ "roundingMode is trunc (with 0 digits from smallestUnit)");
+
+const result6 = datetime.toString({ fractionalSecondDigits: 0, roundingMode: "trunc" });
+assert.sameValue(result6, "2000-05-02T12:34:56",
+ "roundingMode is trunc (with 0 digits from fractionalSecondDigits)");
+
+const result7 = datetime.toString({ smallestUnit: "minute", roundingMode: "trunc" });
+assert.sameValue(result7, "2000-05-02T12:34", "roundingMode is trunc (round to minute)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-undefined.js
new file mode 100644
index 0000000000..9cd034c843
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-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.plaindatetime.prototype.tostring
+description: Fallback value for roundingMode option
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 123, 987, 500);
+
+const explicit1 = datetime.toString({ smallestUnit: "microsecond", roundingMode: undefined });
+assert.sameValue(explicit1, "2000-05-02T12:34:56.123987", "default roundingMode is trunc");
+const implicit1 = datetime.toString({ smallestUnit: "microsecond" });
+assert.sameValue(implicit1, "2000-05-02T12:34:56.123987", "default roundingMode is trunc");
+
+const explicit2 = datetime.toString({ smallestUnit: "millisecond", roundingMode: undefined });
+assert.sameValue(explicit2, "2000-05-02T12:34:56.123", "default roundingMode is trunc");
+const implicit2 = datetime.toString({ smallestUnit: "millisecond" });
+assert.sameValue(implicit2, "2000-05-02T12:34:56.123", "default roundingMode is trunc");
+
+const explicit3 = datetime.toString({ smallestUnit: "second", roundingMode: undefined });
+assert.sameValue(explicit3, "2000-05-02T12:34:56", "default roundingMode is trunc");
+const implicit3 = datetime.toString({ smallestUnit: "second" });
+assert.sameValue(implicit3, "2000-05-02T12:34:56", "default roundingMode is trunc");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-wrong-type.js
new file mode 100644
index 0000000000..c876d5e65a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-wrong-type.js
@@ -0,0 +1,18 @@
+// |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.plaindatetime.prototype.tostring
+description: Type conversions for roundingMode option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 123, 987, 500);
+TemporalHelpers.checkStringOptionWrongType("roundingMode", "trunc",
+ (roundingMode) => datetime.toString({ smallestUnit: "microsecond", roundingMode }),
+ (result, descr) => assert.sameValue(result, "2000-05-02T12:34:56.123987", descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/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/PlainDateTime/prototype/toString/smallestunit-fractionalseconddigits.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/smallestunit-fractionalseconddigits.js
new file mode 100644
index 0000000000..fae1c3a786
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/smallestunit-fractionalseconddigits.js
@@ -0,0 +1,33 @@
+// |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.plaindatetime.prototype.tostring
+description: fractionalSecondDigits option is not used with smallestUnit present
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(1976, 11, 18, 12, 34, 56, 789, 999, 999);
+const tests = [
+ ["minute", "1976-11-18T12:34"],
+ ["second", "1976-11-18T12:34:56"],
+ ["millisecond", "1976-11-18T12:34:56.789"],
+ ["microsecond", "1976-11-18T12:34:56.789999"],
+ ["nanosecond", "1976-11-18T12:34:56.789999999"],
+];
+
+for (const [smallestUnit, expected] of tests) {
+ const string = datetime.toString({
+ smallestUnit,
+ fractionalSecondDigits: 5,
+ });
+ assert.sameValue(string, expected, `smallestUnit: "${smallestUnit}" overrides fractionalSecondDigits`);
+}
+
+assert.throws(RangeError, () => datetime.toString({
+ smallestUnit: "hour",
+ fractionalSecondDigits: 5,
+}), "hour is an invalid smallestUnit but still overrides fractionalSecondDigits");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/smallestunit-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/smallestunit-invalid-string.js
new file mode 100644
index 0000000000..81d11967a3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/smallestunit-invalid-string.js
@@ -0,0 +1,40 @@
+// |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.plaindatetime.prototype.tostring
+description: RangeError thrown when smallestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 123, 987, 500);
+const badValues = [
+ "era",
+ "eraYear",
+ "year",
+ "month",
+ "week",
+ "day",
+ "hour",
+ "millisecond\0",
+ "mill\u0131second",
+ "SECOND",
+ "eras",
+ "eraYears",
+ "years",
+ "months",
+ "weeks",
+ "days",
+ "hours",
+ "milliseconds\0",
+ "mill\u0131seconds",
+ "SECONDS",
+ "other string",
+];
+for (const smallestUnit of badValues) {
+ assert.throws(RangeError, () => datetime.toString({ smallestUnit }),
+ `"${smallestUnit}" is not a valid value for smallest unit`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/smallestunit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/smallestunit-plurals-accepted.js
new file mode 100644
index 0000000000..f5eb54beea
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/smallestunit-plurals-accepted.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.plaindatetime.prototype.tostring
+description: Plural units are accepted as well for the smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 789, 999, 999);
+const validUnits = [
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+];
+TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => datetime.toString({ smallestUnit }), validUnits);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/smallestunit-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/smallestunit-undefined.js
new file mode 100644
index 0000000000..b10e795149
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/smallestunit-undefined.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.plaindatetime.prototype.tostring
+description: Fallback value for smallestUnit option
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 123, 987, 500);
+
+const explicit1 = datetime.toString({ smallestUnit: undefined, fractionalSecondDigits: 6 });
+assert.sameValue(explicit1, "2000-05-02T12:34:56.123987", "default smallestUnit defers to fractionalSecondDigits");
+const implicit1 = datetime.toString({ fractionalSecondDigits: 6 });
+assert.sameValue(implicit1, "2000-05-02T12:34:56.123987", "default smallestUnit defers to fractionalSecondDigits");
+
+const explicit2 = datetime.toString({ smallestUnit: undefined, fractionalSecondDigits: 3 });
+assert.sameValue(explicit2, "2000-05-02T12:34:56.123", "default smallestUnit defers to fractionalSecondDigits");
+const implicit2 = datetime.toString({ fractionalSecondDigits: 3 });
+assert.sameValue(implicit2, "2000-05-02T12:34:56.123", "default smallestUnit defers to fractionalSecondDigits");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/smallestunit-valid-units.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/smallestunit-valid-units.js
new file mode 100644
index 0000000000..d766cba089
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/smallestunit-valid-units.js
@@ -0,0 +1,58 @@
+// |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.plaindatetime.prototype.tostring
+description: Valid units for the smallestUnit option
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 123, 456, 789);
+
+function test(instance, expectations, description) {
+ for (const [smallestUnit, expectedResult] of expectations) {
+ assert.sameValue(instance.toString({ smallestUnit }), expectedResult,
+ `${description} with smallestUnit "${smallestUnit}"`);
+ }
+}
+
+test(
+ datetime,
+ [
+ ["minute", "2000-05-02T12:34"],
+ ["second", "2000-05-02T12:34:56"],
+ ["millisecond", "2000-05-02T12:34:56.123"],
+ ["microsecond", "2000-05-02T12:34:56.123456"],
+ ["nanosecond", "2000-05-02T12:34:56.123456789"],
+ ],
+ "subseconds toString"
+);
+
+test(
+ new Temporal.PlainDateTime(2000, 5, 2, 12, 34),
+ [
+ ["minute", "2000-05-02T12:34"],
+ ["second", "2000-05-02T12:34:00"],
+ ["millisecond", "2000-05-02T12:34:00.000"],
+ ["microsecond", "2000-05-02T12:34:00.000000"],
+ ["nanosecond", "2000-05-02T12:34:00.000000000"],
+ ],
+ "whole minutes toString"
+);
+
+const notValid = [
+ "era",
+ "year",
+ "month",
+ "week",
+ "day",
+ "hour",
+];
+
+notValid.forEach((smallestUnit) => {
+ assert.throws(RangeError, () => datetime.toString({ smallestUnit }),
+ `"${smallestUnit}" is not a valid unit for the smallestUnit option`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/smallestunit-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/smallestunit-wrong-type.js
new file mode 100644
index 0000000000..3a6a294126
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/smallestunit-wrong-type.js
@@ -0,0 +1,18 @@
+// |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.plaindatetime.prototype.tostring
+description: Type conversions for smallestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 123, 987, 500);
+TemporalHelpers.checkStringOptionWrongType("smallestUnit", "microsecond",
+ (smallestUnit) => datetime.toString({ smallestUnit }),
+ (result, descr) => assert.sameValue(result, "2000-05-02T12:34:56.123987", descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/year-format.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/year-format.js
new file mode 100644
index 0000000000..5815ef6a99
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/year-format.js
@@ -0,0 +1,50 @@
+// |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.plaindatetime.prototype.tostring
+description: Verify that the year is appropriately formatted as 4 or 6 digits
+features: [Temporal]
+---*/
+
+let instance = new Temporal.PlainDateTime(-100000, 12, 3, 4, 56, 7, 890);
+assert.sameValue(instance.toString(), "-100000-12-03T04:56:07.89", "large negative year formatted as 6-digit");
+
+instance = new Temporal.PlainDateTime(-10000, 4, 5, 6, 7, 8, 910);
+assert.sameValue(instance.toString(), "-010000-04-05T06:07:08.91", "smallest 5-digit negative year formatted as 6-digit");
+
+instance = new Temporal.PlainDateTime(-9999, 6, 7, 8, 9, 10, 987);
+assert.sameValue(instance.toString(), "-009999-06-07T08:09:10.987", "largest 4-digit negative year formatted as 6-digit");
+
+instance = new Temporal.PlainDateTime(-1000, 8, 9, 10, 9, 8, 765);
+assert.sameValue(instance.toString(), "-001000-08-09T10:09:08.765", "smallest 4-digit negative year formatted as 6-digit");
+
+instance = new Temporal.PlainDateTime(-999, 10, 9, 8, 7, 6, 543);
+assert.sameValue(instance.toString(), "-000999-10-09T08:07:06.543", "largest 3-digit negative year formatted as 6-digit");
+
+instance = new Temporal.PlainDateTime(-1, 8, 7, 6, 54, 32, 100);
+assert.sameValue(instance.toString(), "-000001-08-07T06:54:32.1", "year -1 formatted as 6-digit");
+
+instance = new Temporal.PlainDateTime(0, 6, 5, 4, 32, 10, 123);
+assert.sameValue(instance.toString(), "0000-06-05T04:32:10.123", "year 0 formatted as 4-digit");
+
+instance = new Temporal.PlainDateTime(1, 4, 3, 21, 0, 12, 345);
+assert.sameValue(instance.toString(), "0001-04-03T21:00:12.345", "year 1 formatted as 4-digit");
+
+instance = new Temporal.PlainDateTime(999, 2, 10, 12, 34, 56, 789);
+assert.sameValue(instance.toString(), "0999-02-10T12:34:56.789", "largest 3-digit positive year formatted as 4-digit");
+
+instance = new Temporal.PlainDateTime(1000, 1, 23, 4, 56, 7, 890);
+assert.sameValue(instance.toString(), "1000-01-23T04:56:07.89", "smallest 4-digit positive year formatted as 4-digit");
+
+instance = new Temporal.PlainDateTime(9999, 4, 5, 6, 7, 8, 910);
+assert.sameValue(instance.toString(), "9999-04-05T06:07:08.91", "largest 4-digit positive year formatted as 4-digit");
+
+instance = new Temporal.PlainDateTime(10000, 6, 7, 8, 9, 10, 987);
+assert.sameValue(instance.toString(), "+010000-06-07T08:09:10.987", "smallest 5-digit positive year formatted as 6-digit");
+
+instance = new Temporal.PlainDateTime(100000, 8, 9, 10, 9, 8, 765);
+assert.sameValue(instance.toString(), "+100000-08-09T10:09:08.765", "large positive year formatted as 6-digit");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toStringTag/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toStringTag/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toStringTag/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toStringTag/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toStringTag/prop-desc.js
new file mode 100644
index 0000000000..cf2327d830
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toStringTag/prop-desc.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.plaindatetime.prototype-@@tostringtag
+description: The @@toStringTag property of Temporal.PlainDateTime
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype, Symbol.toStringTag, {
+ value: "Temporal.PlainDateTime",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toStringTag/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toStringTag/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toStringTag/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/balance-negative-time-units.js
new file mode 100644
index 0000000000..29ab26b63e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/balance-negative-time-units.js
@@ -0,0 +1,48 @@
+// |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.plaindatetime.prototype.tozoneddatetime
+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-addtime step 8:
+ 8. Return ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_).
+ sec-temporal-adddatetime step 1:
+ 1. Let _timeResult_ be ? AddTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_, _hours_, _minutes_, _seconds_, _milliseconds_, _microseconds_, _nanoseconds_).
+ sec-temporal-builtintimezonegetinstantfor step 13.a:
+ a. Let _earlier_ be ? AddDateTime(_dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], _dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], 0, 0, 0, 0, 0, 0, 0, 0, 0, −_nanoseconds_, *"constrain"*).
+ sec-temporal.plaindatetime.prototype.tozoneddatetime step 6:
+ 6. Let _instant_ be BuiltinTimeZoneGetInstantFor(_timeZone_, _dateTime_, _disambiguation_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const shiftInstant = new Temporal.Instant(3661_001_001_001n);
+const tz = TemporalHelpers.oneShiftTimeZone(shiftInstant, 2);
+const datetime = new Temporal.PlainDateTime(1970, 1, 1, 1, 1, 1, 1, 1, 1);
+
+// This code path is encountered if disambiguation is `earlier` and the shift is
+// a spring-forward change
+datetime.toZonedDateTime(tz, { disambiguation: "earlier" });
+
+const expected = [
+ "1970-01-01T01:01:01.001001001",
+ "1970-01-01T01:01:01.001000999",
+];
+assert.compareArray(tz.getPossibleInstantsForCalledWith, expected);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/basic.js
new file mode 100644
index 0000000000..9abaecb1c5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/basic.js
@@ -0,0 +1,18 @@
+// |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.plaindatetime.prototype.tozoneddatetime
+description: Straightforward case of using UTC
+features: [Temporal]
+---*/
+
+const dt = new Temporal.PlainDateTime(2020, 1, 1, 0, 0);
+const zdt = dt.toZonedDateTime("UTC");
+
+assert.sameValue(zdt.epochNanoseconds, 1577836800000000000n, "nanoseconds");
+assert.sameValue(zdt.calendarId, "iso8601", "calendar");
+assert.sameValue(zdt.timeZoneId, "UTC", "timezone");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/branding.js
new file mode 100644
index 0000000000..19ebf25c4a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/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.plaindatetime.prototype.tozoneddatetime
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toZonedDateTime = Temporal.PlainDateTime.prototype.toZonedDateTime;
+
+assert.sameValue(typeof toZonedDateTime, "function");
+
+const args = [new Temporal.TimeZone("UTC")];
+
+assert.throws(TypeError, () => toZonedDateTime.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => toZonedDateTime.apply(null, args), "null");
+assert.throws(TypeError, () => toZonedDateTime.apply(true, args), "true");
+assert.throws(TypeError, () => toZonedDateTime.apply("", args), "empty string");
+assert.throws(TypeError, () => toZonedDateTime.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => toZonedDateTime.apply(1, args), "1");
+assert.throws(TypeError, () => toZonedDateTime.apply({}, args), "plain object");
+assert.throws(TypeError, () => toZonedDateTime.apply(Temporal.PlainDateTime, args), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => toZonedDateTime.apply(Temporal.PlainDateTime.prototype, args), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/builtin.js
new file mode 100644
index 0000000000..0480fffbb9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/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.plaindatetime.prototype.tozoneddatetime
+description: >
+ Tests that Temporal.PlainDateTime.prototype.toZonedDateTime
+ 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.PlainDateTime.prototype.toZonedDateTime),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.prototype.toZonedDateTime),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.prototype.toZonedDateTime),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.prototype.toZonedDateTime.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/disambiguate-empty-possible-instants-with-datetime-near-limits.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/disambiguate-empty-possible-instants-with-datetime-near-limits.js
new file mode 100644
index 0000000000..a164297267
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/disambiguate-empty-possible-instants-with-datetime-near-limits.js
@@ -0,0 +1,33 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tozoneddatetime
+description: >
+ Throws when at minimum resp. maximum value and possible instants is an empty List.
+info: |
+ DisambiguatePossibleInstants ( possibleInstants, timeZone, dateTime, disambiguation )
+
+ ...
+ 9. If ! IsValidEpochNanoseconds(dayBeforeNs) is false, throw a RangeError exception.
+ ...
+ 12. If ! IsValidEpochNanoseconds(dayAfterNs) is false, throw a RangeError exception.
+ ...
+features: [Temporal]
+---*/
+
+class TZ extends Temporal.TimeZone {
+ getPossibleInstantsFor() {
+ return [];
+ }
+}
+
+var tz = new TZ("UTC");
+var min = new Temporal.PlainDateTime(-271821, 4, 20);
+var max = new Temporal.PlainDateTime(275760, 9, 13);
+
+assert.throws(RangeError, () => min.toZonedDateTime(tz), "minimum date-time");
+assert.throws(RangeError, () => max.toZonedDateTime(tz), "maximum date-time");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/disambiguation-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/disambiguation-invalid-string.js
new file mode 100644
index 0000000000..45d0f0f4c6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/disambiguation-invalid-string.js
@@ -0,0 +1,29 @@
+// |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.plaindatetime.prototype.tozoneddatetime
+description: RangeError thrown when disambiguation option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaldisambiguation step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"disambiguation"*, « String », « *"compatible"*, *"earlier"*, *"later"*, *"reject"* », *"compatible"*).
+ sec-temporal.plaindatetime.prototype.tozoneddatetime step 5:
+ 5. Let _disambiguation_ be ? ToTemporalDisambiguation(_options_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2001, 9, 9, 1, 46, 40, 987, 654, 321);
+const timeZone = new Temporal.TimeZone("UTC");
+const invalidStrings = ["obviously bad", "", "EARLIER", "earlıer", "late\u0131r", "reject\0"];
+invalidStrings.forEach((s) => {
+ assert.throws(
+ RangeError,
+ () => datetime.toZonedDateTime(timeZone, { disambiguation: s }),
+ `invalid disambiguation string (${s})`);
+});
+
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/disambiguation-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/disambiguation-undefined.js
new file mode 100644
index 0000000000..81ae9ac9f9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/disambiguation-undefined.js
@@ -0,0 +1,33 @@
+// |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.plaindatetime.prototype.tozoneddatetime
+description: Fallback value for disambiguation option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaldisambiguation step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"disambiguation"*, « String », « *"compatible"*, *"earlier"*, *"later"*, *"reject"* », *"compatible"*).
+ sec-temporal.plaindatetime.prototype.tozoneddatetime step 5:
+ 5. Let _disambiguation_ be ? ToTemporalDisambiguation(_options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const timeZone = TemporalHelpers.springForwardFallBackTimeZone();
+const springForwardDatetime = new Temporal.PlainDateTime(2000, 4, 2, 2, 30);
+const fallBackDatetime = new Temporal.PlainDateTime(2000, 10, 29, 1, 30);
+
+[
+ [springForwardDatetime, 954671400_000_000_000n],
+ [fallBackDatetime, 972808200_000_000_000n],
+].forEach(([datetime, expected]) => {
+ const explicit = datetime.toZonedDateTime(timeZone, { disambiguation: undefined });
+ assert.sameValue(explicit.epochNanoseconds, expected, "default disambiguation is compatible");
+ const implicit = datetime.toZonedDateTime(timeZone, {});
+ assert.sameValue(implicit.epochNanoseconds, expected, "default disambiguation is compatible");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/disambiguation-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/disambiguation-wrong-type.js
new file mode 100644
index 0000000000..8f60ebf750
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/disambiguation-wrong-type.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.plaindatetime.prototype.tozoneddatetime
+description: Type conversions for disambiguation option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaldisambiguation step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"disambiguation"*, « String », « *"compatible"*, *"earlier"*, *"later"*, *"reject"* », *"compatible"*).
+ sec-temporal.plaindatetime.prototype.tozoneddatetime step 5:
+ 5. Let _disambiguation_ be ? ToTemporalDisambiguation(_options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2001, 9, 9, 1, 46, 40, 987, 654, 321);
+const timeZone = new Temporal.TimeZone("UTC");
+TemporalHelpers.checkStringOptionWrongType("disambiguation", "compatible",
+ (disambiguation) => datetime.toZonedDateTime(timeZone, { disambiguation }),
+ (result, descr) => assert.sameValue(result.epochNanoseconds, 1_000_000_000_987_654_321n, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/getpossibleinstantsfor-called-with-iso8601-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/getpossibleinstantsfor-called-with-iso8601-calendar.js
new file mode 100644
index 0000000000..c0fa3c570d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/getpossibleinstantsfor-called-with-iso8601-calendar.js
@@ -0,0 +1,58 @@
+// |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.plaindatetime.prototype.tozoneddatetime
+description: >
+ Time zone's getPossibleInstantsFor is called with a PlainDateTime with the
+ built-in ISO 8601 calendar
+features: [Temporal]
+info: |
+ DisambiguatePossibleInstants:
+ 2. Let _n_ be _possibleInstants_'s length.
+ ...
+ 5. Assert: _n_ = 0.
+ ...
+ 19. If _disambiguation_ is *"earlier"*, then
+ ...
+ c. Let _earlierDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*).
+ d. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _earlierDateTime_).
+ ...
+ 20. Assert: _disambiguation_ is *"compatible"* or *"later"*.
+ ...
+ 23. Let _laterDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*).
+ 24. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _laterDateTime_).
+---*/
+
+class SkippedDateTime extends Temporal.TimeZone {
+ constructor() {
+ super("UTC");
+ this.calls = 0;
+ }
+
+ getPossibleInstantsFor(dateTime) {
+ // Calls occur in pairs. For the first one return no possible instants so
+ // that DisambiguatePossibleInstants will call it again
+ if (this.calls++ % 2 == 0) {
+ return [];
+ }
+
+ assert.sameValue(
+ dateTime.getISOFields().calendar,
+ "iso8601",
+ "getPossibleInstantsFor called with dateTime with built-in ISO 8601 calendar"
+ );
+ return super.getPossibleInstantsFor(dateTime);
+ }
+}
+
+const nonBuiltinISOCalendar = new Temporal.Calendar("iso8601");
+const timeZone = new SkippedDateTime();
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0, nonBuiltinISOCalendar);
+instance.toZonedDateTime(timeZone);
+
+assert.sameValue(timeZone.calls, 2, "getPossibleInstantsFor should have been called 2 times");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/invalid-instant.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/invalid-instant.js
new file mode 100644
index 0000000000..db7708bac3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/invalid-instant.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.plaindatetime.prototype.tozoneddatetime
+description: Convert to zoned datetime outside valid range
+features: [Temporal]
+---*/
+
+const max = new Temporal.PlainDateTime(275760, 9, 13, 23, 59, 59, 999, 999, 999);
+const min = new Temporal.PlainDateTime(-271821, 4, 19, 0, 0, 0, 0, 0, 1);
+
+assert.throws(
+ RangeError,
+ () => max.toZonedDateTime("UTC"),
+ "outside of Instant range (too big)"
+);
+
+assert.throws(
+ RangeError,
+ () => min.toZonedDateTime("UTC"),
+ "outside of Instant range (too small)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/length.js
new file mode 100644
index 0000000000..e568058c93
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/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.plaindatetime.prototype.tozoneddatetime
+description: Temporal.PlainDateTime.prototype.toZonedDateTime.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.PlainDateTime.prototype.toZonedDateTime, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/multiple-instants.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/multiple-instants.js
new file mode 100644
index 0000000000..c8ddda8755
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/multiple-instants.js
@@ -0,0 +1,50 @@
+// |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.plaindatetime.prototype.tozoneddatetime
+description: Checking disambiguation options for daylight savings time changes
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tz = TemporalHelpers.springForwardFallBackTimeZone();
+
+const dt1 = new Temporal.PlainDateTime(2000, 4, 2, 2);
+
+const zdt1 = dt1.toZonedDateTime(tz);
+const zdt1_compatible = dt1.toZonedDateTime(tz, { disambiguation: "compatible" });
+const zdt1_earlier = dt1.toZonedDateTime(tz, { disambiguation: "earlier" });
+const zdt1_later = dt1.toZonedDateTime(tz, { disambiguation: "later" });
+
+assert.sameValue(zdt1.epochNanoseconds, 954669600000000000n, "Fall DST (no disambiguation)");
+assert.sameValue(zdt1_compatible.epochNanoseconds, 954669600000000000n, "Fall DST (disambiguation = compatible)");
+assert.sameValue(zdt1_earlier.epochNanoseconds, 954666000000000000n, "Fall DST (disambiguation = earlier)");
+assert.sameValue(zdt1_later.epochNanoseconds, 954669600000000000n, "Fall DST (disambiguation = later)");
+
+assert.throws(
+ RangeError,
+ () => dt1.toZonedDateTime(tz, { disambiguation: "reject" }),
+ "Fall DST (disambiguation = reject)"
+);
+
+const dt2 = new Temporal.PlainDateTime(2000, 10, 29, 1);
+
+const zdt2 = dt2.toZonedDateTime(tz);
+const zdt2_compatible = dt2.toZonedDateTime(tz, { disambiguation: "compatible" });
+const zdt2_earlier = dt2.toZonedDateTime(tz, { disambiguation: "earlier" });
+const zdt2_later = dt2.toZonedDateTime(tz, { disambiguation: "later" });
+
+assert.sameValue(zdt2.epochNanoseconds, 972806400000000000n, "Spring DST (no disambiguation)");
+assert.sameValue(zdt2_compatible.epochNanoseconds, 972806400000000000n, "Spring DST (disambiguation = compatible)");
+assert.sameValue(zdt2_earlier.epochNanoseconds, 972806400000000000n, "Spring DST (disambiguation = earlier)");
+assert.sameValue(zdt2_later.epochNanoseconds, 972810000000000000n, "Spring DST (disambiguation = later)");
+
+assert.throws(
+ RangeError,
+ () => dt2.toZonedDateTime(tz, { disambiguation: "reject" }),
+ "Spring DST (disambiguation = reject)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/name.js
new file mode 100644
index 0000000000..20ff417853
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/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.plaindatetime.prototype.tozoneddatetime
+description: Temporal.PlainDateTime.prototype.toZonedDateTime.name is "toZonedDateTime".
+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.PlainDateTime.prototype.toZonedDateTime, "name", {
+ value: "toZonedDateTime",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/not-a-constructor.js
new file mode 100644
index 0000000000..2aaece804b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/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.plaindatetime.prototype.tozoneddatetime
+description: >
+ Temporal.PlainDateTime.prototype.toZonedDateTime 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.PlainDateTime.prototype.toZonedDateTime();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.prototype.toZonedDateTime), false,
+ "isConstructor(Temporal.PlainDateTime.prototype.toZonedDateTime)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/options-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/options-object.js
new file mode 100644
index 0000000000..d6d317e545
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/options-object.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.plaindatetime.prototype.tozoneddatetime
+description: Empty or a function object may be used as options
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2);
+
+const result1 = instance.toZonedDateTime("UTC", {});
+assert.sameValue(
+ result1.epochNanoseconds, 957225600000000000n,
+ "options may be an empty plain object"
+);
+
+const result2 = instance.toZonedDateTime("UTC", () => {});
+assert.sameValue(
+ result2.epochNanoseconds, 957225600000000000n,
+ "options may be a function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/options-undefined.js
new file mode 100644
index 0000000000..567cd149f4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/options-undefined.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.plaindate.prototype.tozoneddatetime
+includes: [temporalHelpers.js]
+description: Verify that undefined options are handled correctly.
+features: [BigInt, Temporal]
+---*/
+
+const datetimeEarlier = new Temporal.PlainDateTime(2000, 10, 29, 1, 34, 56, 987, 654, 321);
+const datetimeLater = new Temporal.PlainDateTime(2000, 4, 2, 2, 34, 56, 987, 654, 321);
+const timeZone = TemporalHelpers.springForwardFallBackTimeZone();
+
+[
+ [datetimeEarlier, 972808496987654321n],
+ [datetimeLater, 954671696987654321n],
+].forEach(([datetime, expected]) => {
+ const explicit = datetime.toZonedDateTime(timeZone, undefined);
+ assert.sameValue(explicit.epochNanoseconds, expected, "default disambiguation is compatible");
+
+ const implicit = datetime.toZonedDateTime(timeZone);
+ assert.sameValue(implicit.epochNanoseconds, expected, "default disambiguation is compatible");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/options-wrong-type.js
new file mode 100644
index 0000000000..255c024a64
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/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.plaindatetime.prototype.tozoneddatetime
+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.PlainDateTime(2000, 5, 2);
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.toZonedDateTime("UTC", value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/order-of-operations.js
new file mode 100644
index 0000000000..8b01064161
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/order-of-operations.js
@@ -0,0 +1,65 @@
+// |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.plaindatetime.prototype.tozoneddatetime
+description: Properties on an object passed to toZonedDateTime() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ // ToTemporalTimeZoneSlotValue
+ "has timeZone.getOffsetNanosecondsFor",
+ "has timeZone.getPossibleInstantsFor",
+ "has timeZone.id",
+ // ToTemporalDisambiguation
+ "get options.disambiguation",
+ "get options.disambiguation.toString",
+ "call options.disambiguation.toString",
+ // lookup in PlainDateTime.p.toZonedDateTime
+ "get timeZone.getOffsetNanosecondsFor",
+ "get timeZone.getPossibleInstantsFor",
+ // GetInstantFor
+ "call timeZone.getPossibleInstantsFor",
+];
+const actual = [];
+
+const calendar = TemporalHelpers.calendarObserver(actual, "this.calendar");
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+const fallBackInstance = new Temporal.PlainDateTime(2000, 10, 29, 1, 30, 0, 0, 0, 0, calendar);
+const springForwardInstance = new Temporal.PlainDateTime(2000, 4, 2, 2, 30, 0, 0, 0, 0, calendar);
+// clear observable operations that occurred during the constructor calls
+actual.splice(0);
+
+const dstTimeZone = TemporalHelpers.springForwardFallBackTimeZone();
+const timeZone = TemporalHelpers.timeZoneObserver(actual, "timeZone", {
+ getOffsetNanosecondsFor: dstTimeZone.getOffsetNanosecondsFor,
+ getPossibleInstantsFor: dstTimeZone.getPossibleInstantsFor,
+});
+
+const options = TemporalHelpers.propertyBagObserver(actual, { disambiguation: "compatible" }, "options");
+
+instance.toZonedDateTime(timeZone, options);
+assert.compareArray(actual, expected, "order of operations at normal wall-clock time");
+actual.splice(0); // clear
+
+fallBackInstance.toZonedDateTime(timeZone, options);
+assert.compareArray(actual, expected, "order of operations at repeated wall-clock time");
+actual.splice(0); // clear
+
+springForwardInstance.toZonedDateTime(timeZone, options);
+assert.compareArray(actual, expected.concat([
+ "call timeZone.getOffsetNanosecondsFor",
+ "call timeZone.getOffsetNanosecondsFor",
+ "call timeZone.getPossibleInstantsFor",
+]), "order of operations at skipped wall-clock time");
+actual.splice(0); // clear
+
+const rejectOptions = TemporalHelpers.propertyBagObserver(actual, { disambiguation: "reject" }, "options");
+assert.throws(RangeError, () => springForwardInstance.toZonedDateTime(timeZone, rejectOptions));
+assert.compareArray(actual, expected, "order of operations at skipped wall-clock time with disambiguation: reject");
+actual.splice(0); // clear
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/plain-custom-timezone.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/plain-custom-timezone.js
new file mode 100644
index 0000000000..15191200d0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/plain-custom-timezone.js
@@ -0,0 +1,52 @@
+// |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.plaindatetime.prototype.tozoneddatetime
+description: TimeZone.getPossibleInstantsFor called after processing timeZone and options
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const expected = [
+ "has timeZone.getOffsetNanosecondsFor",
+ "has timeZone.getPossibleInstantsFor",
+ "has timeZone.id",
+ "get options.disambiguation",
+ "get options.disambiguation.toString",
+ "call options.disambiguation.toString",
+ "get timeZone.getOffsetNanosecondsFor",
+ "get timeZone.getPossibleInstantsFor",
+ "call timeZone.getPossibleInstantsFor",
+];
+
+Object.defineProperty(Temporal.TimeZone, "from", {
+ get() {
+ actual.push("get Temporal.TimeZone.from");
+ return undefined;
+ },
+});
+
+const dateTime = Temporal.PlainDateTime.from("1975-02-02T14:25:36.123456789");
+const instant = Temporal.Instant.fromEpochNanoseconds(-205156799012345679n);
+
+const options = TemporalHelpers.propertyBagObserver(actual, { disambiguation: "reject" }, "options");
+
+const timeZone = TemporalHelpers.timeZoneObserver(actual, "timeZone", {
+ getPossibleInstantsFor(dateTimeArg) {
+ assert.sameValue(dateTimeArg, dateTime);
+ return [instant];
+ },
+});
+
+const result = dateTime.toZonedDateTime(timeZone, options);
+assert.sameValue(result.epochNanoseconds, instant.epochNanoseconds);
+assert.sameValue(result.getTimeZone(), timeZone);
+
+assert.compareArray(actual, expected);
+
+assert.sameValue(result.getISOFields().calendar, dateTime.getISOFields().calendar);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/plain-date-time-near-limits.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/plain-date-time-near-limits.js
new file mode 100644
index 0000000000..75a3510938
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/plain-date-time-near-limits.js
@@ -0,0 +1,37 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tozoneddatetime
+description: >
+ Throws a RangeError if the date/time value is outside the instant limits
+info: |
+ Temporal.PlainDateTime.prototype.toZonedDateTime ( temporalTimeZoneLike [ , options ] )
+ ...
+ 6. Let instant be ? BuiltinTimeZoneGetInstantFor(timeZone, dateTime, disambiguation).
+ ...
+features: [Temporal]
+---*/
+
+// Try to create from the minimum date-time.
+{
+ let dt = new Temporal.PlainDateTime(-271821, 4, 19, 0, 0, 0, 0, 0, 1);
+ assert.throws(RangeError, () => dt.toZonedDateTime("UTC"));
+}
+{
+ let dt = new Temporal.PlainDateTime(-271821, 4, 19, 1, 0, 0, 0, 0, 0);
+ assert.throws(RangeError, () => dt.toZonedDateTime("UTC"));
+}
+
+// Try to create from the maximum date-time.
+{
+ let dt = new Temporal.PlainDateTime(275760, 9, 13, 0, 0, 0, 0, 0, 1);
+ assert.throws(RangeError, () => dt.toZonedDateTime("UTC"));
+}
+{
+ let dt = new Temporal.PlainDateTime(275760, 9, 13, 1, 0, 0, 0, 0, 0);
+ assert.throws(RangeError, () => dt.toZonedDateTime("UTC"));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/prop-desc.js
new file mode 100644
index 0000000000..f485224197
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/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.plaindatetime.prototype.tozoneddatetime
+description: The "toZonedDateTime" property of Temporal.PlainDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.prototype.toZonedDateTime,
+ "function",
+ "`typeof PlainDateTime.prototype.toZonedDateTime` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime.prototype, "toZonedDateTime", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/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/PlainDateTime/prototype/toZonedDateTime/timezone-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-case-insensitive.js
new file mode 100644
index 0000000000..06e6ad0128
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-case-insensitive.js
@@ -0,0 +1,17 @@
+// |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.plaindatetime.prototype.tozoneddatetime
+description: Time zone names are case insensitive
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2);
+
+const timeZone = 'uTc';
+const result = instance.toZonedDateTime(timeZone);
+assert.sameValue(result.timeZoneId, 'UTC', `Time zone created from string "${timeZone}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..b57c3cf7f8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-non-integer.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.plaindatetime.prototype.tozoneddatetime
+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 datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ timeZone.getPossibleInstantsFor = function () {
+ return [];
+ };
+ assert.throws(RangeError, () => datetime.toZonedDateTime(timeZone));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..047ccfaa4f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-not-callable.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.plaindatetime.prototype.tozoneddatetime
+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 datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ timeZone.getPossibleInstantsFor = function () {
+ return [];
+ };
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => datetime.toZonedDateTime(timeZone),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..17f0be600c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-out-of-range.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.plaindatetime.prototype.tozoneddatetime
+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 datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ timeZone.getPossibleInstantsFor = function () {
+ return [];
+ };
+ assert.throws(RangeError, () => datetime.toZonedDateTime(timeZone));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..bcba7af74a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,30 @@
+// |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.plaindatetime.prototype.tozoneddatetime
+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 datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ timeZone.getPossibleInstantsFor = function () {
+ return [];
+ };
+ assert.throws(TypeError, () => datetime.toZonedDateTime(timeZone));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-getpossibleinstantsfor-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-getpossibleinstantsfor-iterable.js
new file mode 100644
index 0000000000..a2b6ec45ac
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-getpossibleinstantsfor-iterable.js
@@ -0,0 +1,45 @@
+// |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.plaindatetime.prototype.tozoneddatetime
+description: An iterable returned from timeZone.getPossibleInstantsFor is consumed after each call
+info: |
+ sec-temporal.plaindatetime.prototype.tozoneddatetime step 6:
+ 6. Let _instant_ be ? BuiltinTimeZoneGetInstantFor(_timeZone_, _temporalDateTime_, *"compatible"*).
+ sec-temporal-builtintimezonegetinstantfor step 1:
+ 1. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_).
+ sec-temporal-builtintimezonegetinstantfor step 14:
+ 14. Assert: _disambiguation_ is *"compatible"* or *"later"*.
+ sec-temporal-builtintimezonegetinstantfor step 16:
+ 16. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _later_).
+ sec-temporal-getpossibleinstantsfor step 2:
+ 2. Let _list_ be ? IterableToList(_possibleInstants_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected1 = [
+ "2000-05-02T12:34:56.987654321",
+];
+
+TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => {
+ const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ datetime.toZonedDateTime(timeZone);
+}, expected1);
+
+// Same, but test the other path where the time doesn't exist and
+// GetPossibleInstantsFor is called again on a later time
+
+const expected2 = [
+ "2030-01-01T00:30:00",
+ "2030-01-01T01:30:00",
+];
+
+TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => {
+ const datetime = new Temporal.PlainDateTime(2030, 1, 1, 0, 30);
+ datetime.toZonedDateTime(timeZone);
+}, expected2);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-string-datetime.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-string-datetime.js
new file mode 100644
index 0000000000..c4e5a05eec
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-string-datetime.js
@@ -0,0 +1,65 @@
+// |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.plaindatetime.prototype.tozoneddatetime
+description: Conversion of ISO date-time strings to Temporal.TimeZone instances
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2);
+
+let timeZone = "2021-08-19T17:30";
+assert.throws(RangeError, () => instance.toZonedDateTime(timeZone), "bare date-time string is not a time zone");
+
+[
+ "2021-08-19T17:30-07:00:01",
+ "2021-08-19T17:30-07:00:00",
+ "2021-08-19T17:30-07:00:00.1",
+ "2021-08-19T17:30-07:00:00.0",
+ "2021-08-19T17:30-07:00:00.01",
+ "2021-08-19T17:30-07:00:00.00",
+ "2021-08-19T17:30-07:00:00.001",
+ "2021-08-19T17:30-07:00:00.000",
+ "2021-08-19T17:30-07:00:00.0001",
+ "2021-08-19T17:30-07:00:00.0000",
+ "2021-08-19T17:30-07:00:00.00001",
+ "2021-08-19T17:30-07:00:00.00000",
+ "2021-08-19T17:30-07:00:00.000001",
+ "2021-08-19T17:30-07:00:00.000000",
+ "2021-08-19T17:30-07:00:00.0000001",
+ "2021-08-19T17:30-07:00:00.0000000",
+ "2021-08-19T17:30-07:00:00.00000001",
+ "2021-08-19T17:30-07:00:00.00000000",
+ "2021-08-19T17:30-07:00:00.000000001",
+ "2021-08-19T17:30-07:00:00.000000000",
+].forEach((timeZone) => {
+ assert.throws(
+ RangeError,
+ () => instance.toZonedDateTime(timeZone),
+ `ISO string ${timeZone} with a sub-minute offset is not a valid time zone`
+ );
+});
+
+timeZone = "2021-08-19T17:30Z";
+const result1 = instance.toZonedDateTime(timeZone);
+assert.sameValue(result1.timeZoneId, "UTC", "date-time + Z is UTC time zone");
+
+timeZone = "2021-08-19T17:30-07:00";
+const result2 = instance.toZonedDateTime(timeZone);
+assert.sameValue(result2.timeZoneId, "-07:00", "date-time + offset is the offset time zone");
+
+timeZone = "2021-08-19T17:30[UTC]";
+const result3 = instance.toZonedDateTime(timeZone);
+assert.sameValue(result3.timeZoneId, "UTC", "date-time + IANA annotation is the IANA time zone");
+
+timeZone = "2021-08-19T17:30Z[UTC]";
+const result4 = instance.toZonedDateTime(timeZone);
+assert.sameValue(result4.timeZoneId, "UTC", "date-time + Z + IANA annotation is the IANA time zone");
+
+timeZone = "2021-08-19T17:30-07:00[UTC]";
+const result5 = instance.toZonedDateTime(timeZone);
+assert.sameValue(result5.timeZoneId, "UTC", "date-time + offset + IANA annotation is the IANA time zone");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-string-leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-string-leap-second.js
new file mode 100644
index 0000000000..82b361d540
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-string-leap-second.js
@@ -0,0 +1,20 @@
+// |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.plaindatetime.prototype.tozoneddatetime
+description: Leap second is a valid ISO string for TimeZone
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2);
+let timeZone = "2016-12-31T23:59:60+00:00[UTC]";
+
+const result = instance.toZonedDateTime(timeZone);
+assert.sameValue(result.timeZoneId, "UTC", "leap second is a valid ISO string for TimeZone");
+
+timeZone = "2021-08-19T17:30:45.123456789+23:59[+23:59:60]";
+assert.throws(RangeError, () => instance.toZonedDateTime(timeZone), "leap second in time zone name not valid");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-string-multiple-offsets.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-string-multiple-offsets.js
new file mode 100644
index 0000000000..a31838d6a0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-string-multiple-offsets.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.plaindatetime.prototype.tozoneddatetime
+description: Time zone parsing from ISO strings uses the bracketed offset, not the ISO string offset
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2);
+const timeZone = "2021-08-19T17:30:45.123456789-12:12[+01:46]";
+
+const result = instance.toZonedDateTime(timeZone);
+assert.sameValue(result.timeZoneId, "+01:46", "Time zone string determined from bracket name");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-string-year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-string-year-zero.js
new file mode 100644
index 0000000000..0861ca32a0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-string-year-zero.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.plaindatetime.prototype.tozoneddatetime
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+const instance = new Temporal.PlainDateTime(2000, 5, 2);
+invalidStrings.forEach((timeZone) => {
+ assert.throws(
+ RangeError,
+ () => instance.toZonedDateTime(timeZone),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-string.js
new file mode 100644
index 0000000000..52ed9f56d8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-string.js
@@ -0,0 +1,39 @@
+// |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.plaindatetime.prototype.tozoneddatetime
+description: Time zone IDs are valid input for a time zone
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getPossibleInstantsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getPossibleInstantsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be looked up");
+ },
+});
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2);
+
+["UTC", "+01:30"].forEach((timeZone) => {
+ const result = instance.toZonedDateTime(timeZone);
+ assert.sameValue(result.getISOFields().timeZone, timeZone, `time zone slot should store string "${timeZone}"`);
+});
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", getPossibleInstantsForOriginal);
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-wrong-type.js
new file mode 100644
index 0000000000..80f945b8bb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-wrong-type.js
@@ -0,0 +1,42 @@
+// |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.plaindatetime.prototype.tozoneddatetime
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or object for TimeZone
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2);
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [19761118, "number that would convert to a valid ISO string in other contexts"],
+ [1n, "bigint"],
+];
+
+for (const [timeZone, description] of primitiveTests) {
+ assert.throws(
+ typeof timeZone === 'string' ? RangeError : TypeError,
+ () => instance.toZonedDateTime(timeZone),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "object not implementing time zone protocol"],
+ [new Temporal.Calendar("iso8601"), "calendar instance"],
+];
+
+for (const [timeZone, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.toZonedDateTime(timeZone), `${description} is not a valid object and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..3cb35dfea1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-builtin-calendar-no-array-iteration.js
@@ -0,0 +1,24 @@
+// |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.plaindatetime.prototype.until
+description: >
+ Calling the method with a property bag argument with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const arg = { year: 2000, month: 5, day: 2, hour: 21, minute: 43, second: 5, calendar: "iso8601" };
+instance.until(arg);
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-number.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-number.js
new file mode 100644
index 0000000000..bed336d5b5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/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.plaindatetime.prototype.until
+description: A number cannot be used in place of a Temporal.PlainDateTime
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18);
+
+const numbers = [
+ 1,
+ 19761118,
+ -19761118,
+ 1234567890,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => instance.until(arg),
+ `A number (${arg}) is not a valid ISO string for PlainDateTime`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-object.js
new file mode 100644
index 0000000000..6f71ba66ad
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-object.js
@@ -0,0 +1,20 @@
+// |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.plaindatetime.prototype.until
+description: Plain objects are accepted as an argument
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dt = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789);
+
+TemporalHelpers.assertDuration(
+ dt.until({ year: 2019, month: 10, day: 29, hour: 10 }),
+ 0, 0, 0, 15684, 18, 36, 29, 876, 543, 211,
+ "casts argument (plain object)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-plaindate.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-plaindate.js
new file mode 100644
index 0000000000..3a9f46bbd6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-plaindate.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.plaindatetime.prototype.until
+description: Fast path for converting Temporal.PlainDate to Temporal.PlainDateTime by reading internal slots
+info: |
+ sec-temporal.plaindatetime.prototype.until step 3:
+ 3. Set _other_ to ? ToTemporalDateTime(_other_).
+ sec-temporal-totemporaldatetime step 2.b:
+ b. If _item_ has an [[InitializedTemporalDate]] internal slot, then
+ i. Return ? CreateTemporalDateTime(_item_.[[ISOYear]], _item_.[[ISOMonth]], _item_.[[ISODay]], 0, 0, 0, 0, 0, 0, _item_.[[Calendar]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalPlainDateTimeFastPath((date, calendar) => {
+ const datetime = new Temporal.PlainDateTime(2000, 5, 2, 0, 0, 0, 987, 654, 321, calendar);
+ const result = datetime.until(date);
+ assert.sameValue(result.total({ unit: "nanoseconds" }), -987654321, "PlainDate is converted to midnight");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-propertybag-calendar-case-insensitive.js
new file mode 100644
index 0000000000..fd713acf45
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-propertybag-calendar-case-insensitive.js
@@ -0,0 +1,20 @@
+// |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.plaindatetime.prototype.until
+description: The calendar name is case-insensitive
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18);
+
+const calendar = "IsO8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.until(arg);
+TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-propertybag-calendar-leap-second.js
new file mode 100644
index 0000000000..7ecf13b3a7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-propertybag-calendar-leap-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.plaindatetime.prototype.until
+description: Leap second is a valid ISO string for a calendar in a property bag
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18);
+
+const calendar = "2016-12-31T23:59:60";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.until(arg);
+TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "leap second is a valid ISO string for calendar"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-propertybag-calendar-number.js
new file mode 100644
index 0000000000..cf43c5a168
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-propertybag-calendar-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.plaindatetime.prototype.until
+description: A number as calendar in a property bag is not accepted
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(1976, 11, 18);
+
+const numbers = [
+ 1,
+ 19970327,
+ -19970327,
+ 1234567890,
+];
+for (const calendar of numbers) {
+ const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+ assert.throws(
+ TypeError,
+ () => instance.until(arg),
+ "Numbers cannot be used as a calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-propertybag-calendar-string.js
new file mode 100644
index 0000000000..a9879a2e99
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-propertybag-calendar-string.js
@@ -0,0 +1,20 @@
+// |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.plaindatetime.prototype.until
+description: A calendar ID is valid input for Calendar
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18);
+
+const calendar = "iso8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.until(arg);
+TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, `Calendar created from string "${calendar}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-propertybag-calendar-wrong-type.js
new file mode 100644
index 0000000000..d3d8cb74f4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-propertybag-calendar-wrong-type.js
@@ -0,0 +1,46 @@
+// |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.plaindatetime.prototype.until
+description: >
+ Appropriate error thrown when a calendar property from a property bag cannot
+ be converted to a calendar object or string
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [calendar, description] of primitiveTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ typeof calendar === 'string' ? RangeError : TypeError,
+ () => instance.until(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+ [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields()
+];
+
+for (const [calendar, description] of typeErrorTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(TypeError, () => instance.until(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/PlainDateTime/prototype/until/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-propertybag-calendar-year-zero.js
new file mode 100644
index 0000000000..099de48b51
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-propertybag-calendar-year-zero.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.plaindatetime.prototype.until
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T17:45",
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+01:00",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.until(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..e65aab926c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-calendar-annotation.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.plaindatetime.prototype.until
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["1976-11-18T15:23[u-ca=iso8601]", "without time zone"],
+ ["1976-11-18T15:23[UTC][u-ca=iso8601]", "with time zone"],
+ ["1976-11-18T15:23[!u-ca=iso8601]", "with ! and no time zone"],
+ ["1976-11-18T15:23[UTC][!u-ca=iso8601]", "with ! and time zone"],
+ ["1976-11-18T15:23[u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 15, 23);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.until(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/PlainDateTime/prototype/until/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..16d8c27c45
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-critical-unknown-annotation.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.plaindatetime.prototype.until
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "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.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.until(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..eb9a8f4dd4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-date-with-utc-offset.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.plaindatetime.prototype.until
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 15, 23);
+
+const validStrings = [
+ "1976-11-18T15:23+00:00",
+ "1976-11-18T15:23+00:00[UTC]",
+ "1976-11-18T15:23+00:00[!UTC]",
+ "1976-11-18T15:23-02:30[America/St_Johns]",
+];
+
+for (const arg of validStrings) {
+ const result = instance.until(arg);
+
+ TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ `"${arg}" is a valid UTC offset with time for PlainDateTime`
+ );
+}
+
+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.until(arg),
+ `"${arg}" UTC offset without time is not valid for PlainDateTime`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..6c373cfff4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/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.plaindatetime.prototype.until
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[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.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.until(arg),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..3d9b9e28f5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-multiple-time-zone.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.plaindatetime.prototype.until
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "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.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.until(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-time-separators.js
new file mode 100644
index 0000000000..320238e6a2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-time-separators.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.plaindatetime.prototype.until
+description: Time separator in string argument can vary
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["1976-11-18T15:23", "uppercase T"],
+ ["1976-11-18t15:23", "lowercase T"],
+ ["1976-11-18 15:23", "space between date and time"],
+];
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 15, 23);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.until(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/PlainDateTime/prototype/until/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..f20fd0bdcd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-time-zone-annotation.js
@@ -0,0 +1,35 @@
+// |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.plaindatetime.prototype.until
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["1976-11-18T15:23[Asia/Kolkata]", "named, with no offset"],
+ ["1976-11-18T15:23[!Europe/Vienna]", "named, with ! and no offset"],
+ ["1976-11-18T15:23[+00:00]", "numeric, with no offset"],
+ ["1976-11-18T15:23[!-02:30]", "numeric, with ! and no offset"],
+ ["1976-11-18T15:23+00:00[UTC]", "named, with offset"],
+ ["1976-11-18T15:23+00:00[!Africa/Abidjan]", "named, with offset and !"],
+ ["1976-11-18T15:23+00:00[+01:00]", "numeric, with offset"],
+ ["1976-11-18T15:23+00:00[!-08:00]", "numeric, with offset and !"],
+];
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 15, 23);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.until(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/PlainDateTime/prototype/until/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..a438aec643
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-unknown-annotation.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.plaindatetime.prototype.until
+description: Various forms of unknown annotation
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["1976-11-18T15:23[foo=bar]", "alone"],
+ ["1976-11-18T15:23[UTC][foo=bar]", "with time zone"],
+ ["1976-11-18T15:23[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["1976-11-18T15:23[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["1976-11-18T15:23[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 15, 23);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.until(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/PlainDateTime/prototype/until/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..97129a5ba3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-with-utc-designator.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.plaindatetime.prototype.until
+description: RangeError thrown if a string with UTC designator is used as a PlainDateTime
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "2019-10-01T09:00:00Z",
+ "2019-10-01T09:00:00Z[UTC]",
+];
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.until(arg),
+ "String with UTC designator should not be valid as a PlainDateTime"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string.js
new file mode 100644
index 0000000000..714f69d177
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string.js
@@ -0,0 +1,20 @@
+// |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.plaindatetime.prototype.until
+description: Date-like strings are accepted
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dt = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789);
+
+TemporalHelpers.assertDuration(
+ dt.until("2019-10-29T10:46:38.271986102"),
+ 0, 0, 0, 15684, 19, 23, 8, 148, 529, 313,
+ "casts argument (string)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-wrong-type.js
new file mode 100644
index 0000000000..58247233eb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/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.plaindatetime.prototype.until
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainDateTime
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 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.until(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.PlainDateTime, "Temporal.PlainDateTime, object"],
+ [Temporal.PlainDateTime.prototype, "Temporal.PlainDateTime.prototype, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.until(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/PlainDateTime/prototype/until/argument-zoneddatetime-balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-zoneddatetime-balance-negative-time-units.js
new file mode 100644
index 0000000000..29b515f78c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/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.plaindatetime.prototype.until
+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-totemporaldatetime step 3.b:
+ b. If _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ ...
+ ii. 1. Return ? BuiltinTimeZoneGetPlainDateTimeFor(_item_.[[TimeZone]], _instant_, _item_.[[Calendar]]).
+ sec-temporal.plaindatetime.prototype.until step 3:
+ 3. Set _other_ ? ToTemporalDateTime(_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.PlainDateTime(1970, 1, 1).until(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/PlainDateTime/prototype/until/argument-zoneddatetime-negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-zoneddatetime-negative-epochnanoseconds.js
new file mode 100644
index 0000000000..9f2b2bcea0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/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.plaindatetime.prototype.until
+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.PlainDateTime(2000, 5, 2, 15, 30, 45, 987, 654, 321);
+const result = instance.until(datetime);
+TemporalHelpers.assertDuration(result, 0, 0, 0, -11239, -22, -40, -10, -987, -654, -320);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..99535f976c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/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.plaindatetime.prototype.until
+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 plain = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ const zoned = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => plain.until(zoned));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..0455bd5317
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/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.plaindatetime.prototype.until
+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 plain = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ const zoned = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => plain.until(zoned),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..459ee0a25d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/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.plaindatetime.prototype.until
+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 plain = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ const zoned = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => plain.until(zoned));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..5d5a71b0da
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/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.plaindatetime.prototype.until
+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 plain = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ const zoned = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => plain.until(zoned));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/balance-negative-duration.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/balance-negative-duration.js
new file mode 100644
index 0000000000..9b3abb95dc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/balance-negative-duration.js
@@ -0,0 +1,37 @@
+// |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.plaindatetime.prototype.until
+description: Negative durations are balanced correctly by the modulo operation in NanosecondsToDays
+info: |
+ sec-temporal-nanosecondstodays step 6:
+ 6. If Type(_relativeTo_) is not Object or _relativeTo_ does not have an [[InitializedTemporalZonedDateTime]] internal slot, then
+ a. Return the new Record { ..., [[Nanoseconds]]: abs(_nanoseconds_) modulo _dayLengthNs_ × _sign_, ... }.
+ sec-temporal-balanceduration step 4:
+ 4. If _largestUnit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*, then
+ a. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _relativeTo_).
+ sec-temporal-differenceisodatetime steps 7 and 13:
+ 7. If _timeSign_ is -_dateSign_, then
+ ...
+ b. Set _timeDifference_ to ? BalanceDuration(-_timeSign_, _timeDifference_.[[Hours]], _timeDifference_.[[Minutes]], _timeDifference_.[[Seconds]], _timeDifference_.[[Milliseconds]], _timeDifference_.[[Microseconds]], _timeDifference_.[[Nanoseconds]], _largestUnit_).
+ ...
+ 13. Return ? BalanceDuration(_dateDifference_.[[Years]], _dateDifference_.[[Months]], _dateDifference_.[[Weeks]], _dateDifference_.[[Days]], _timeDifference_.[[Hours]], _timeDifference_.[[Minutes]], _timeDifference_.[[Seconds]], _timeDifference_.[[Milliseconds]], _timeDifference_.[[Microseconds]], _timeDifference_.[[Nanoseconds]], _largestUnit_).
+ sec-temporal.plaindatetime.prototype.until step 13:
+ 13. Let _diff_ be ? DifferenceISODateTime(_dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], _dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], _other_.[[ISOYear]], _other_.[[ISOMonth]], _other_.[[ISODay]], _other_.[[ISOHour]], _other_.[[ISOMinute]], _other_.[[ISOSecond]], _other_.[[ISOMillisecond]], _other_.[[ISOMicrosecond]], _other_.[[ISONanosecond]], _dateTime_.[[Calendar]], _largestUnit_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier1 = new Temporal.PlainDateTime(2000, 5, 2, 9);
+const later1 = new Temporal.PlainDateTime(2000, 5, 5, 10);
+const result1 = later1.until(earlier1, { largestUnit: "day" });
+TemporalHelpers.assertDuration(result1, 0, 0, 0, -3, -1, 0, 0, 0, 0, 0, "date sign == time sign");
+
+const earlier2 = new Temporal.PlainDateTime(2000, 5, 2, 10);
+const later2 = new Temporal.PlainDateTime(2000, 5, 5, 9);
+const result2 = later2.until(earlier2, { largestUnit: "day" });
+TemporalHelpers.assertDuration(result2, 0, 0, 0, -2, -23, 0, 0, 0, 0, 0, "date sign != time sign");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/balance-negative-time-units.js
new file mode 100644
index 0000000000..82cd4276bf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/balance-negative-time-units.js
@@ -0,0 +1,53 @@
+// |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.plaindatetime.prototype.until
+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-differenceisodatetime step 2:
+ 2. Let _timeDifference_ be ? DifferenceTime(_h1_, _min1_, _s1_, _ms1_, _mus1_, _ns1_, _h2_, _min2_, _s2_, _ms2_, _mus2_, _ns2_).
+ sec-temporal.plaindatetime.prototype.until step 13:
+ 13. Let _diff_ be ? DifferenceISODateTime(_dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], _dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], _other_.[[ISOYear]], _other_.[[ISOMonth]], _other_.[[ISODay]], _other_.[[ISOHour]], _other_.[[ISOMinute]], _other_.[[ISOSecond]], _other_.[[ISOMillisecond]], _other_.[[ISOMicrosecond]], _other_.[[ISONanosecond]], _dateTime_.[[Calendar]], _largestUnit_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(1996, 5, 2, 1, 1, 1, 1, 1, 1);
+
+const result1 = new Temporal.PlainDateTime(1996, 5, 2, 0, 0, 0, 0, 0, 2).until(datetime);
+TemporalHelpers.assertDuration(result1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 999, "nanoseconds balance");
+
+const result2 = new Temporal.PlainDateTime(1996, 5, 2, 0, 0, 0, 0, 2).until(datetime);
+TemporalHelpers.assertDuration(result2, 0, 0, 0, 0, 1, 1, 1, 0, 999, 1, "microseconds balance");
+
+const result3 = new Temporal.PlainDateTime(1996, 5, 2, 0, 0, 0, 2).until(datetime);
+TemporalHelpers.assertDuration(result3, 0, 0, 0, 0, 1, 1, 0, 999, 1, 1, "milliseconds balance");
+
+const result4 = new Temporal.PlainDateTime(1996, 5, 2, 0, 0, 2).until(datetime);
+TemporalHelpers.assertDuration(result4, 0, 0, 0, 0, 1, 0, 59, 1, 1, 1, "seconds balance");
+
+const result5 = new Temporal.PlainDateTime(1996, 5, 2, 0, 2).until(datetime);
+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 = new Temporal.PlainDateTime(1996, 5, 2, 2).until(datetime);
+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/PlainDateTime/prototype/until/balance.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/balance.js
new file mode 100644
index 0000000000..1950557975
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/balance.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.plaindatetime.prototype.until
+description: Results with opposite-sign components (e.g. months=1, hours=-1) are balanced correctly
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const a = Temporal.PlainDateTime.from("2017-10-05T08:07:14+00:00[UTC]");
+const b = Temporal.PlainDateTime.from("2021-03-05T03:32:45+00:00[UTC]");
+const c = Temporal.PlainDateTime.from("2021-03-05T09:32:45+00:00[UTC]");
+
+const r1 = a.until(b, { largestUnit: "months" });
+TemporalHelpers.assertDuration(r1, 0, 40, 0, 27, 19, 25, 31, 0, 0, 0, "r1");
+assert.sameValue(a.add(r1).toString(), b.toString(), "a.add(r1)");
+
+const r2 = b.until(a, { largestUnit: "months" });
+TemporalHelpers.assertDuration(r2, 0, -40, 0, -30, -19, -25, -31, 0, 0, 0, "r2");
+assert.sameValue(b.add(r2).toString(), a.toString(), "b.add(r2)");
+
+const r3 = c.until(a, { largestUnit: "months" });
+TemporalHelpers.assertDuration(r3, 0, -41, 0, 0, -1, -25, -31, 0, 0, 0, "r3");
+assert.sameValue(c.add(r3).toString(), a.toString(), "c.add(r3)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/branding.js
new file mode 100644
index 0000000000..72c29bc1d7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/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.plaindatetime.prototype.until
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const until = Temporal.PlainDateTime.prototype.until;
+
+assert.sameValue(typeof until, "function");
+
+const args = [new Temporal.PlainDateTime(2022, 6, 22)];
+
+assert.throws(TypeError, () => until.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => until.apply(null, args), "null");
+assert.throws(TypeError, () => until.apply(true, args), "true");
+assert.throws(TypeError, () => until.apply("", args), "empty string");
+assert.throws(TypeError, () => until.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => until.apply(1, args), "1");
+assert.throws(TypeError, () => until.apply({}, args), "plain object");
+assert.throws(TypeError, () => until.apply(Temporal.PlainDateTime, args), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => until.apply(Temporal.PlainDateTime.prototype, args), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..d36ba8a1cd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plaindatetime.prototype.until
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dateUntilOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dateUntil");
+Object.defineProperty(Temporal.Calendar.prototype, "dateUntil", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("dateUntil should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, "iso8601");
+instance.until(new Temporal.PlainDateTime(2001, 6, 13));
+
+Object.defineProperty(Temporal.Calendar.prototype, "dateUntil", dateUntilOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/builtin.js
new file mode 100644
index 0000000000..8db60e1aab
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/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.plaindatetime.prototype.until
+description: >
+ Tests that Temporal.PlainDateTime.prototype.until
+ 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.PlainDateTime.prototype.until),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.prototype.until),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.prototype.until),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.prototype.until.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/calendar-dateadd-called-with-plaindate-instance.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/calendar-dateadd-called-with-plaindate-instance.js
new file mode 100644
index 0000000000..11882b5dca
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/calendar-dateadd-called-with-plaindate-instance.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.plaindatetime.prototype.until
+description: >
+ relativeTo parameters that are not ZonedDateTime or undefined, are always
+ converted to PlainDate for observable calendar calls
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarDateAddPlainDateInstance();
+const instance = new Temporal.PlainDateTime(1970, 1, 1, 0, 0, 0, 0, 0, 0, calendar);
+instance.until(new Temporal.PlainDateTime(2000, 5, 2, 0, 0, 0, 0, 0, 0, calendar), { smallestUnit: "month" });
+assert(calendar.dateAddCallCount > 0, "assertions in calendar.dateAdd() should have been tested");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/calendar-datefromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..0614563612
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/calendar-datefromfields-called-with-null-prototype-fields.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.plaindatetime.prototype.until
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const arg = { year: 2000, month: 5, day: 2, calendar };
+instance.until(arg);
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/calendar-dateuntil-called-with-copy-of-options.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/calendar-dateuntil-called-with-copy-of-options.js
new file mode 100644
index 0000000000..2bf2e54661
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/calendar-dateuntil-called-with-copy-of-options.js
@@ -0,0 +1,35 @@
+// |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.plaindatetime.prototype.until
+description: The dateUntil() method on the calendar is called with a copy of the options bag
+features: [Temporal]
+---*/
+
+const originalOptions = {
+ largestUnit: "year",
+ shouldBeCopied: {},
+};
+let called = false;
+
+class Calendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+
+ dateUntil(d1, d2, options) {
+ called = true;
+ assert.notSameValue(options, originalOptions, "options bag should be a copy");
+ assert.sameValue(options.shouldBeCopied, originalOptions.shouldBeCopied, "options bag should be a shallow copy");
+ return new Temporal.Duration();
+ }
+}
+const calendar = new Calendar();
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+const later = new Temporal.PlainDateTime(2001, 6, 3, 13, 35, 57, 988, 655, 322, calendar);
+earlier.until(later, originalOptions);
+assert(called, "calendar.dateUntil must be called");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/calendar-dateuntil-called-with-null-prototype-options.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/calendar-dateuntil-called-with-null-prototype-options.js
new file mode 100644
index 0000000000..c57b59dcdb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/calendar-dateuntil-called-with-null-prototype-options.js
@@ -0,0 +1,20 @@
+// |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.plaindatetime.prototype.until
+description: >
+ Calendar.dateUntil method is called with a null-prototype object as the
+ options value when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckOptionsPrototypePollution();
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+const argument = new Temporal.PlainDateTime(2022, 6, 14, 18, 21, 36, 660, 690, 387, calendar);
+instance.until(argument, { largestUnit: "months" });
+assert.sameValue(calendar.dateUntilCallCount, 1, "dateUntil should have been called on the calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/calendar-dateuntil-called-with-plaindate-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/calendar-dateuntil-called-with-plaindate-calendar.js
new file mode 100644
index 0000000000..cba7264915
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/calendar-dateuntil-called-with-plaindate-calendar.js
@@ -0,0 +1,34 @@
+// |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.plaindatetime.prototype.until
+description: calendar.dateUntil() is passed PlainDate objects with the receiver's calendar
+info: |
+ DifferenceISODateTime ( y1, mon1, d1, h1, min1, s1, ms1, mus1, ns1, y2, mon2, d2, h2, min2, s2, ms2, mus2, ns2, calendar, largestUnit [ , options ] )
+
+ 8. Let _date1_ be ? CreateTemporalDate(_balanceResult_.[[Year]], _balanceResult_.[[Month]], _balanceResult_.[[Day]], _calendar_).
+ 9. Let _date2_ be ? CreateTemporalDate(_y2_, _mon2_, _d2_, _calendar_).
+ 12. Let _dateDifference_ be ? CalendarDateUntil(_calendar_, _date1_, _date2_, _untilOptions_).
+features: [Temporal]
+---*/
+
+class Calendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+
+ dateUntil(d1, d2) {
+ assert.sameValue(d1.getCalendar(), this, "d1.calendar");
+ assert.sameValue(d2.getCalendar(), this, "d2.calendar");
+ return new Temporal.Duration();
+ }
+}
+const calendar = new Calendar();
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+const later = new Temporal.PlainDateTime(2001, 6, 3, 13, 35, 57, 988, 655, 322, calendar);
+const result = earlier.until(later);
+assert(result instanceof Temporal.Duration, "result");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/calendar-dateuntil-called-with-singular-largestunit.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/calendar-dateuntil-called-with-singular-largestunit.js
new file mode 100644
index 0000000000..6659a56d21
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/calendar-dateuntil-called-with-singular-largestunit.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.plaindatetime.prototype.until
+description: The options object passed to calendar.dateUntil has a largestUnit property with its value in the singular form
+info: |
+ sec-temporal.plaindatetime.prototype.until step 13:
+ 13. Let _diff_ be ? DifferenceISODateTime(_dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], _dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], _other_.[[ISOYear]], _other_.[[ISOMonth]], _other_.[[ISODay]], _other_.[[ISOHour]], _other_.[[ISOMinute]], _other_.[[ISOSecond]], _other_.[[ISOMillisecond]], _other_.[[ISOMicrosecond]], _other_.[[ISONanosecond]], _dateTime_.[[Calendar]], _largestUnit_, _options_).
+ sec-temporal-differenceisodatetime steps 9–11:
+ 9. Let _dateLargestUnit_ be ! LargerOfTwoTemporalUnits(*"day"*, _largestUnit_).
+ 10. Let _untilOptions_ be ? MergeLargestUnitOption(_options_, _dateLargestUnit_).
+ 11. Let _dateDifference_ be ? CalendarDateUntil(_calendar_, _date1_, _date2_, _untilOptions_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkCalendarDateUntilLargestUnitSingular(
+ (calendar, largestUnit) => {
+ const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+ const later = new Temporal.PlainDateTime(2001, 6, 3, 13, 35, 57, 988, 655, 322, calendar);
+ earlier.until(later, { largestUnit });
+ },
+ {
+ years: ["year"],
+ months: ["month"],
+ weeks: ["week"],
+ days: [],
+ hours: [],
+ minutes: [],
+ seconds: [],
+ milliseconds: [],
+ microseconds: [],
+ nanoseconds: []
+ }
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/calendar-fields-iterable.js
new file mode 100644
index 0000000000..1b41916350
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/calendar-fields-iterable.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.plaindatetime.prototype.until
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plaindatetime.prototype.until step 3:
+ 3. Set _other_ to ? ToTemporalDateTime(_other_).
+ sec-temporal-totemporaldatetime step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"hour"*, *"microsecond"*, *"millisecond"*, *"minute"*, *"month"*, *"monthCode"*, *"nanosecond"*, *"second"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar1);
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+datetime.until({ year: 2005, month: 6, day: 2, calendar: calendar2 });
+
+assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/calendar-temporal-object.js
new file mode 100644
index 0000000000..f44a965dda
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/calendar-temporal-object.js
@@ -0,0 +1,29 @@
+// |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.plaindatetime.prototype.until
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plaindatetime.prototype.until step 3:
+ 3. Set _other_ to ? ToTemporalDateTime(_other_).
+ sec-temporal-totemporaldatetime step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const date = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, temporalObject);
+ date.until({ year: 2005, month: 6, day: 2, calendar: temporalObject });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/casts-argument.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/casts-argument.js
new file mode 100644
index 0000000000..855e0ad714
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/casts-argument.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.plaindatetime.prototype.until
+description: String and object arguments get cast
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789);
+
+TemporalHelpers.assertDuration(
+ datetime.until({ year: 2019, month: 10, day: 29, hour: 10 }),
+ 0, 0, 0, 15684, 18, 36, 29, 876, 543, 211,
+ "plain object argument"
+);
+
+TemporalHelpers.assertDuration(
+ datetime.until("2019-10-29T10:46:38.271986102"),
+ 0, 0, 0, 15684, 19, 23, 8, 148, 529, 313,
+ "string argument gets cast"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..648b1afd9b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/constructor-in-calendar-fields.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.plaindatetime.prototype.until
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+assert.throws(RangeError, () => instance.until(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/different-calendars-throws.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/different-calendars-throws.js
new file mode 100644
index 0000000000..24c5e2e39b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/different-calendars-throws.js
@@ -0,0 +1,42 @@
+// |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.plaindatetime.prototype.until
+description: Using different calendars is not acceptable
+features: [Temporal]
+---*/
+
+const dt1 = new Temporal.PlainDateTime(2000, 1, 1, 0, 0, 0, 0, 0, 0);
+const dt2 = new Temporal.PlainDateTime(2000, 1, 1, 0, 0, 0, 0, 0, 0, {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "custom",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+});
+
+assert.throws(
+ RangeError,
+ () => dt1.until(dt2),
+ "cannot use until with PDTs having different calendars"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/duplicate-calendar-fields.js
new file mode 100644
index 0000000000..3beced06e9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/duplicate-calendar-fields.js
@@ -0,0 +1,19 @@
+// |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.plaindatetime.prototype.until
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['day'], ['hour'], ['microsecond'], ['millisecond'], ['minute'], ['month'], ['monthCode'], ['nanosecond'], ['second'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar };
+ const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+ assert.throws(RangeError, () => instance.until(arg));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..70e5f82ce0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/infinity-throws-rangeerror.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.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plaindatetime.prototype.until
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 15);
+const base = { year: 2000, month: 5, day: 2, hour: 15, minute: 30, second: 45, millisecond: 987, microsecond: 654, nanosecond: 321 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.until({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.until({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/inverse.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/inverse.js
new file mode 100644
index 0000000000..8c77539901
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/inverse.js
@@ -0,0 +1,17 @@
+// |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.plaindatetime.prototype.until
+description: The since and until operations act as inverses
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dt = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789);
+const later = new Temporal.PlainDateTime(2016, 3, 3, 18);
+
+TemporalHelpers.assertDurationsEqual(dt.until(later), later.since(dt), "until and since act as inverses");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/largestunit-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/largestunit-invalid-string.js
new file mode 100644
index 0000000000..68f7aadcdc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/largestunit-invalid-string.js
@@ -0,0 +1,31 @@
+// |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.plaindatetime.prototype.until
+description: RangeError thrown when largestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2001, 6, 3, 13, 35, 57, 987, 654, 321);
+const badValues = [
+ "era",
+ "eraYear",
+ "millisecond\0",
+ "mill\u0131second",
+ "SECOND",
+ "eras",
+ "eraYears",
+ "milliseconds\0",
+ "mill\u0131seconds",
+ "SECONDS",
+ "other string"
+];
+for (const largestUnit of badValues) {
+ assert.throws(RangeError, () => earlier.until(later, { largestUnit }),
+ `"${largestUnit}" is not a valid value for largestUnit`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/largestunit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/largestunit-plurals-accepted.js
new file mode 100644
index 0000000000..744be9778a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/largestunit-plurals-accepted.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.plaindatetime.prototype.until
+description: Plural units are accepted as well for the largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const later = new Temporal.PlainDateTime(2001, 6, 12, 13, 35, 57, 988, 655, 322);
+const validUnits = [
+ "year",
+ "month",
+ "week",
+ "day",
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+];
+TemporalHelpers.checkPluralUnitsAccepted((largestUnit) => earlier.until(later, { largestUnit }), validUnits);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/largestunit-smallestunit-mismatch.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/largestunit-smallestunit-mismatch.js
new file mode 100644
index 0000000000..a281439e64
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/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.plaindatetime.prototype.until
+description: RangeError thrown when smallestUnit is larger than largestUnit
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2001, 6, 3, 13, 35, 57, 987, 654, 321);
+const units = ["years", "months", "weeks", "days", "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, () => earlier.until(later, { largestUnit, smallestUnit }));
+ }
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/largestunit-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/largestunit-undefined.js
new file mode 100644
index 0000000000..5d5f650430
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/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.plaindatetime.prototype.until
+description: Fallback value for largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2001, 6, 3, 13, 35, 57, 987, 654, 321);
+
+const explicit = earlier.until(later, { largestUnit: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 397, 1, 1, 1, 987, 654, 321, "default largestUnit is day");
+const implicit = earlier.until(later, {});
+TemporalHelpers.assertDuration(implicit, 0, 0, 0, 397, 1, 1, 1, 987, 654, 321, "default largestUnit is day");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/largestunit-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/largestunit-wrong-type.js
new file mode 100644
index 0000000000..a9bdaf9695
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/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.plaindatetime.prototype.until
+description: Type conversions for largestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2001, 6, 3, 13, 35, 57, 987, 654, 321);
+TemporalHelpers.checkStringOptionWrongType("largestUnit", "year",
+ (largestUnit) => earlier.until(later, { largestUnit }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 1, 1, 0, 1, 1, 1, 1, 987, 654, 321, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/leap-second.js
new file mode 100644
index 0000000000..7d319f4a4b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/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.plaindatetime.prototype.until
+description: Leap second is a valid ISO string for PlainDateTime
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2016, 12, 31, 23, 59, 59);
+
+let arg = "2016-12-31T23:59:60";
+const result1 = instance.until(arg);
+TemporalHelpers.assertDuration(
+ result1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "leap second is a valid ISO string for PlainDateTime"
+);
+
+arg = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+const result2 = instance.until(arg);
+TemporalHelpers.assertDuration(
+ result2,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "second: 60 is ignored in property bag for PlainDateTime"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/length.js
new file mode 100644
index 0000000000..ae05bf5d55
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/length.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.plaindatetime.prototype.until
+description: Temporal.PlainDateTime.prototype.until.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.PlainDateTime.prototype.until, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/name.js
new file mode 100644
index 0000000000..c07e663445
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/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.plaindatetime.prototype.until
+description: Temporal.PlainDateTime.prototype.until.name is "until".
+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.PlainDateTime.prototype.until, "name", {
+ value: "until",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/no-unnecessary-units.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/no-unnecessary-units.js
new file mode 100644
index 0000000000..7e311f8dca
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/no-unnecessary-units.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.plaindatetime.prototype.until
+description: Do not return Durations with unnecessary units
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const feb29 = new Temporal.PlainDateTime(2020, 2, 29, 0, 0);
+const feb28 = new Temporal.PlainDateTime(2021, 2, 28, 0, 0);
+
+TemporalHelpers.assertDuration(
+ feb29.until(feb28, { largestUnit: "months" }),
+ 0, 12, 0, 0, 0, 0, 0, 0, 0, 0,
+ "does not include higher units than necessary (largest unit = months)"
+);
+
+TemporalHelpers.assertDuration(
+ feb29.until(feb28, { largestUnit: "years" }),
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "does not include higher units than necessary (largest unit = years)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/not-a-constructor.js
new file mode 100644
index 0000000000..9d88cc5ceb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/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.plaindatetime.prototype.until
+description: >
+ Temporal.PlainDateTime.prototype.until 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.PlainDateTime.prototype.until();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.prototype.until), false,
+ "isConstructor(Temporal.PlainDateTime.prototype.until)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/options-empty.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/options-empty.js
new file mode 100644
index 0000000000..05233e55f6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/options-empty.js
@@ -0,0 +1,23 @@
+// |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.plaindatetime.prototype.until
+description: Empty options are valid
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const feb20 = new Temporal.PlainDateTime(2020, 2, 1, 0, 0);
+const feb21 = new Temporal.PlainDateTime(2021, 2, 1, 0, 0);
+
+TemporalHelpers.assertDuration(feb20.until(feb21, {}),
+ 0, 0, 0, 366, 0, 0, 0, 0, 0, 0,
+ "empty options (plain object) are acceptable");
+
+TemporalHelpers.assertDuration(feb20.until(feb21, () => {}),
+ 0, 0, 0, 366, 0, 0, 0, 0, 0, 0,
+ "empty options (function object) are acceptable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/options-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/options-invalid.js
new file mode 100644
index 0000000000..cecac27183
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/options-invalid.js
@@ -0,0 +1,23 @@
+// |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.plaindatetime.prototype.until
+description: A variety of invalid option arguments
+features: [Temporal, Symbol]
+---*/
+
+const feb20 = new Temporal.PlainDateTime(2020, 2, 1, 0, 0);
+const feb21 = new Temporal.PlainDateTime(2021, 2, 1, 0, 0);
+
+const badOptions = [null, 1, 'obviously invalid', true, Symbol('foo'), 1n];
+badOptions.forEach((bad) => {
+ assert.throws(
+ TypeError,
+ () => feb20.until(feb21, bad),
+ `unacceptable options (${typeof bad})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/options-undefined.js
new file mode 100644
index 0000000000..df89d800c2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/options-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.plaindatetime.prototype.until
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const later = new Temporal.PlainDateTime(2000, 6, 12, 12, 34, 56, 987, 654, 322);
+
+const explicit = earlier.until(later, undefined);
+assert.sameValue(explicit.years, 0, "default largest unit is days");
+assert.sameValue(explicit.months, 0, "default largest unit is days");
+assert.sameValue(explicit.weeks, 0, "default largest unit is days");
+assert.sameValue(explicit.days, 41, "default largest unit is days");
+assert.sameValue(explicit.nanoseconds, 1, "default smallest unit is nanoseconds and no rounding");
+
+const implicit = earlier.until(later);
+assert.sameValue(implicit.years, 0, "default largest unit is days");
+assert.sameValue(implicit.months, 0, "default largest unit is days");
+assert.sameValue(implicit.weeks, 0, "default largest unit is days");
+assert.sameValue(implicit.days, 41, "default largest unit is days");
+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/PlainDateTime/prototype/until/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/options-wrong-type.js
new file mode 100644
index 0000000000..f5098cf0eb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/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.plaindatetime.prototype.until
+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.PlainDateTime(2000, 5, 2);
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.until(new Temporal.PlainDateTime(1976, 11, 18), value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/order-of-operations.js
new file mode 100644
index 0000000000..0cc2af79e3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/order-of-operations.js
@@ -0,0 +1,250 @@
+// |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.plaindatetime.prototype.until
+description: Properties on objects passed to until() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ // ToTemporalDateTime
+ "get other.calendar",
+ "has other.calendar.dateAdd",
+ "has other.calendar.dateFromFields",
+ "has other.calendar.dateUntil",
+ "has other.calendar.day",
+ "has other.calendar.dayOfWeek",
+ "has other.calendar.dayOfYear",
+ "has other.calendar.daysInMonth",
+ "has other.calendar.daysInWeek",
+ "has other.calendar.daysInYear",
+ "has other.calendar.fields",
+ "has other.calendar.id",
+ "has other.calendar.inLeapYear",
+ "has other.calendar.mergeFields",
+ "has other.calendar.month",
+ "has other.calendar.monthCode",
+ "has other.calendar.monthDayFromFields",
+ "has other.calendar.monthsInYear",
+ "has other.calendar.weekOfYear",
+ "has other.calendar.year",
+ "has other.calendar.yearMonthFromFields",
+ "has other.calendar.yearOfWeek",
+ "get other.calendar.dateFromFields",
+ "get other.calendar.fields",
+ "call other.calendar.fields",
+ "get other.day",
+ "get other.day.valueOf",
+ "call other.day.valueOf",
+ "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.month",
+ "get other.month.valueOf",
+ "call other.month.valueOf",
+ "get other.monthCode",
+ "get other.monthCode.toString",
+ "call other.monthCode.toString",
+ "get other.nanosecond",
+ "get other.nanosecond.valueOf",
+ "call other.nanosecond.valueOf",
+ "get other.second",
+ "get other.second.valueOf",
+ "call other.second.valueOf",
+ "get other.year",
+ "get other.year.valueOf",
+ "call other.year.valueOf",
+ "call other.calendar.dateFromFields",
+ // CalendarEquals
+ "get this.calendar.id",
+ "get other.calendar.id",
+ // 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 ownCalendar = TemporalHelpers.calendarObserver(actual, "this.calendar");
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, ownCalendar);
+
+const otherDateTimePropertyBag = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2001,
+ month: 6,
+ monthCode: "M06",
+ day: 2,
+ hour: 1,
+ minute: 46,
+ second: 40,
+ millisecond: 250,
+ microsecond: 500,
+ nanosecond: 750,
+ calendar: TemporalHelpers.calendarObserver(actual, "other.calendar"),
+}, "other");
+
+function createOptionsObserver({ smallestUnit = "nanoseconds", largestUnit = "auto", roundingMode = "halfExpand", roundingIncrement = 1 } = {}) {
+ return TemporalHelpers.propertyBagObserver(actual, {
+ // order is significant, due to iterating through properties in order to
+ // copy them to an internal null-prototype object:
+ roundingIncrement,
+ roundingMode,
+ largestUnit,
+ smallestUnit,
+ additional: "property",
+ }, "options");
+}
+
+// clear any observable things that happened while constructing the objects
+actual.splice(0);
+
+// basic order of observable operations with calendar call, without rounding:
+instance.until(otherDateTimePropertyBag, createOptionsObserver({ largestUnit: "years" }));
+assert.compareArray(actual, expected.concat([
+ // lookup
+ "get this.calendar.dateAdd",
+ "get this.calendar.dateUntil",
+ // CalendarDateUntil
+ "call this.calendar.dateUntil",
+]), "order of operations");
+actual.splice(0); // clear
+
+// short-circuit for identical objects:
+const identicalPropertyBag = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2000,
+ month: 5,
+ monthCode: "M05",
+ day: 2,
+ hour: 12,
+ minute: 34,
+ second: 56,
+ millisecond: 987,
+ microsecond: 654,
+ nanosecond: 321,
+ calendar: TemporalHelpers.calendarObserver(actual, "other.calendar"),
+}, "other");
+
+instance.until(identicalPropertyBag, createOptionsObserver());
+assert.compareArray(actual, expected, "order of operations with identical datetimes");
+actual.splice(0); // clear
+
+// code path through RoundDuration that rounds to the nearest year:
+const expectedOpsForYearRounding = expected.concat([
+ // lookup
+ "get this.calendar.dateAdd",
+ "get this.calendar.dateUntil",
+ // CalendarDateUntil
+ "call this.calendar.dateUntil",
+ // RoundDuration
+ "call this.calendar.dateAdd", // 12.d
+ "call this.calendar.dateAdd", // 12.f
+ "call this.calendar.dateUntil", // 12.n
+ "call this.calendar.dateAdd", // 12.x MoveRelativeDate
+ // (12.r not called because other units can't add up to >1 year at this point)
+ // BalanceDateDurationRelative
+ "call this.calendar.dateAdd", // 9.c
+ "call this.calendar.dateUntil" // 9.d
+]);
+instance.until(otherDateTimePropertyBag, createOptionsObserver({ smallestUnit: "years" }));
+assert.compareArray(actual, expectedOpsForYearRounding, "order of operations with smallestUnit = years");
+actual.splice(0); // clear
+
+// code path through RoundDuration that rounds to the nearest year and skips a DateUntil call:
+const otherDatePropertyBagSameMonth = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2001,
+ month: 5,
+ monthCode: "M05",
+ day: 2,
+ hour: 12,
+ minute: 34,
+ second: 56,
+ millisecond: 987,
+ microsecond: 654,
+ nanosecond: 321,
+ calendar: TemporalHelpers.calendarObserver(actual, "other.calendar"),
+}, "other");
+const expectedOpsForYearRoundingSameMonth = expected.concat([
+ // lookup
+ "get this.calendar.dateAdd",
+ "get this.calendar.dateUntil",
+ // CalendarDateUntil
+ "call this.calendar.dateUntil",
+ // RoundDuration
+ "call this.calendar.dateAdd", // 12.d
+ "call this.calendar.dateAdd", // 12.f
+ "call this.calendar.dateAdd", // 12.x MoveRelativeDate
+ // (12.n not called because months and weeks == 0)
+ // BalanceDateDurationRelative
+ "call this.calendar.dateAdd", // 9.c
+ "call this.calendar.dateUntil" // 9.d
+]);
+instance.until(otherDatePropertyBagSameMonth, createOptionsObserver({ smallestUnit: "years" }));
+assert.compareArray(actual, expectedOpsForYearRoundingSameMonth, "order of operations with smallestUnit = years and no excess months/weeks");
+actual.splice(0); // clear
+
+// code path through RoundDuration that rounds to the nearest month:
+const expectedOpsForMonthRounding = expected.concat([
+ // lookup
+ "get this.calendar.dateAdd",
+ "get this.calendar.dateUntil",
+ // CalendarDateUntil
+ "call this.calendar.dateUntil",
+ // RoundDuration
+ "call this.calendar.dateAdd", // 13.c
+ "call this.calendar.dateAdd", // 13.e
+ "call this.calendar.dateUntil", // 13.m
+ "call this.calendar.dateAdd", // 13.w MoveRelativeDate
+ // BalanceDateDurationRelative
+ "call this.calendar.dateAdd", // 10.d
+ "call this.calendar.dateUntil" // 10.e
+]);
+instance.until(otherDateTimePropertyBag, createOptionsObserver({ smallestUnit: "months" }));
+assert.compareArray(actual, expectedOpsForMonthRounding, "order of operations with smallestUnit = years");
+actual.splice(0); // clear
+
+// code path through RoundDuration that rounds to the nearest week:
+const expectedOpsForWeekRounding = expected.concat([
+ // lookup
+ "get this.calendar.dateAdd",
+ "get this.calendar.dateUntil",
+ // CalendarDateUntil
+ "call this.calendar.dateUntil",
+ // RoundDuration
+ "call this.calendar.dateUntil", // 14.f
+ "call this.calendar.dateAdd", // 14.p MoveRelativeDate
+ // BalanceDateDurationRelative
+ "call this.calendar.dateAdd", // 16
+ "call this.calendar.dateUntil" // 17
+]);
+instance.until(otherDateTimePropertyBag, createOptionsObserver({ smallestUnit: "weeks" }));
+assert.compareArray(actual, expectedOpsForWeekRounding, "order of operations with smallestUnit = weeks");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/prop-desc.js
new file mode 100644
index 0000000000..4b537789d8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/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.plaindatetime.prototype.until
+description: The "until" property of Temporal.PlainDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.prototype.until,
+ "function",
+ "`typeof PlainDateTime.prototype.until` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime.prototype, "until", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/proto-in-calendar-fields.js
new file mode 100644
index 0000000000..dd01f8a68b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/proto-in-calendar-fields.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.plaindatetime.prototype.until
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+assert.throws(RangeError, () => instance.until(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/read-time-fields-before-datefromfields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/read-time-fields-before-datefromfields.js
new file mode 100644
index 0000000000..8b50fbddf6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/read-time-fields-before-datefromfields.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.plaindatetime.prototype.until
+description: The time fields are read from the object before being passed to dateFromFields().
+info: |
+ sec-temporal.plaindatetime.prototype.until step 3:
+ 3. Set _other_ to ? ToTemporalDateTime(_other_).
+ sec-temporal-totemporaldatetime step 2.e:
+ e. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+ sec-temporal-interprettemporaldatetimefields steps 1–2:
+ 1. Let _timeResult_ be ? ToTemporalTimeRecord(_fields_).
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarMakeInfinityTime();
+const datetime = new Temporal.PlainDateTime(2021, 3, 31, 12, 34, 56, 987, 654, 321);
+const duration = datetime.until({ year: 2021, month: 3, day: 31, calendar });
+
+TemporalHelpers.assertDuration(duration, 0, 0, 0, 0, -12, -34, -56, -987, -654, -321);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/returns-days.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/returns-days.js
new file mode 100644
index 0000000000..f43542929b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/returns-days.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.plaindatetime.prototype.until
+description: Return days by default
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const feb20 = new Temporal.PlainDateTime(2020, 2, 1, 0, 0);
+const feb21 = new Temporal.PlainDateTime(2021, 2, 1, 0, 0);
+
+TemporalHelpers.assertDuration(
+ feb20.until(feb21, { largestUnit: "auto" }),
+ 0, 0, 0, 366, 0, 0, 0, 0, 0, 0,
+ "defaults to returning days (largest unit = auto)"
+);
+
+TemporalHelpers.assertDuration(
+ feb20.until(feb21, { largestUnit: "days" }),
+ 0, 0, 0, 366, 0, 0, 0, 0, 0, 0,
+ "defaults to returning days (largest unit = days)"
+);
+
+TemporalHelpers.assertDuration(
+ feb20.until(new Temporal.PlainDateTime(2021, 2, 1, 0, 0, 0, 0, 0, 1)),
+ 0, 0, 0, 366, 0, 0, 0, 0, 0, 1,
+ "returns nanoseconds if argument is PDT with non-zero nanoseconds"
+);
+
+const dt = new Temporal.PlainDateTime(2020, 2, 1, 0, 0, 0, 0, 0, 1);
+
+TemporalHelpers.assertDuration(
+ dt.until(feb21),
+ 0, 0, 0, 365, 23, 59, 59, 999, 999, 999,
+ "one nanosecond away from one year away"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/round-cross-unit-boundary.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/round-cross-unit-boundary.js
new file mode 100644
index 0000000000..4a687a2d70
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/round-cross-unit-boundary.js
@@ -0,0 +1,36 @@
+// |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.plaindatetime.prototype.until
+description: Rounding can cross unit boundaries up to largestUnit
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+// Date units
+{
+ const earlier = new Temporal.PlainDateTime(2022, 1, 1);
+ const later = new Temporal.PlainDateTime(2023, 12, 25);
+ const duration = earlier.until(later, { largestUnit: "years", smallestUnit: "months", roundingMode: "expand" });
+ TemporalHelpers.assertDuration(duration, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, "1 year 11 months balances to 2 years");
+}
+
+// Time units
+{
+ const earlier = new Temporal.PlainDateTime(2000, 5, 2);
+ const later = new Temporal.PlainDateTime(2000, 5, 2, 1, 59, 59);
+ const duration = earlier.until(later, { largestUnit: "hours", smallestUnit: "minutes", roundingMode: "expand" });
+ TemporalHelpers.assertDuration(duration, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, "1:59 balances to 2 hours");
+}
+
+// Both
+{
+ const earlier = new Temporal.PlainDateTime(1970, 1, 1);
+ const later = new Temporal.PlainDateTime(1971, 12, 31, 23, 59, 59, 999, 999, 999);
+ const duration = earlier.until(later, { largestUnit: "years", smallestUnit: "microseconds", roundingMode: "expand" });
+ TemporalHelpers.assertDuration(duration, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, "rounding up 1 ns balances to 2 years");
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/round-negative-duration.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/round-negative-duration.js
new file mode 100644
index 0000000000..16bd2b8838
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/round-negative-duration.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.plaindatetime.prototype.until
+description: Negative durations are rounded correctly by the modulo operation in NanosecondsToDays
+info: |
+ sec-temporal-nanosecondstodays step 6:
+ 6. If Type(_relativeTo_) is not Object or _relativeTo_ does not have an [[InitializedTemporalZonedDateTime]] internal slot, then
+ a. Return the new Record { ..., [[Nanoseconds]]: abs(_nanoseconds_) modulo _dayLengthNs_ × _sign_, ... }.
+ sec-temporal-roundduration step 6:
+ 6. If _unit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*, then
+ ...
+ d. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _intermediate_).
+ sec-temporal.plaindatetime.prototype.until step 14:
+ 14. Let _roundResult_ be ? RoundDuration(_diff_.[[Years]], _diff_.[[Months]], _diff_.[[Weeks]], _diff_.[[Days]], _diff_.[[Hours]], _diff_.[[Minutes]], _diff_.[[Seconds]], _diff_.[[Milliseconds]], _diff_.[[Microseconds]], _diff_.[[Nanoseconds]], _roundingIncrement_, _smallestUnit_, _roundingMode_, _dateTime_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12);
+const later = new Temporal.PlainDateTime(2000, 5, 5);
+const result = later.until(earlier, { smallestUnit: "day", roundingIncrement: 2 });
+TemporalHelpers.assertDuration(result, 0, 0, 0, -2, 0, 0, 0, 0, 0, 0);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/round-relative-to-receiver.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/round-relative-to-receiver.js
new file mode 100644
index 0000000000..413112eab3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/round-relative-to-receiver.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.plaindatetime.prototype.until
+description: Rounding happens relative to receiver
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dt1 = new Temporal.PlainDateTime(2019, 1, 1);
+const dt2 = new Temporal.PlainDateTime(2020, 7, 2);
+const options = { smallestUnit: "years", roundingMode: "halfExpand" };
+
+TemporalHelpers.assertDuration(
+ dt1.until(dt2, options),
+ 2, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "rounds relative to the receiver (positive case)"
+);
+
+TemporalHelpers.assertDuration(
+ dt2.until(dt1, options),
+ -1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "rounds relative to the receiver (negative case)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/rounding-zero-year-month-week-length.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/rounding-zero-year-month-week-length.js
new file mode 100644
index 0000000000..893e50889c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/rounding-zero-year-month-week-length.js
@@ -0,0 +1,34 @@
+// |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.plaindatetime.prototype.until
+description: >
+ A malicious calendar resulting in a year, month, or week length of zero is
+ handled correctly
+info: |
+ RoundDuration
+ 10.z. If _oneYearDays_ = 0, throw a *RangeError* exception.
+ ...
+ 11.z. If _oneMonthDays_ = 0, throw a *RangeError* exception.
+ ...
+ 12.s. If _oneWeekDays_ = 0, throw a *RangeError* exception.
+features: [Temporal]
+---*/
+
+const cal = new class extends Temporal.Calendar {
+ dateAdd(date, duration, options) {
+ // Called several times, last call sets oneYear/Month/WeekDays to 0
+ return new Temporal.PlainDate(1970, 1, 1);
+ }
+}("iso8601");
+
+const dt1 = new Temporal.PlainDateTime(1970, 1, 1, 0, 0, 0, 0, 0, 0, cal);
+const dt2 = new Temporal.PlainDateTime(1971, 1, 1, 0, 0, 0, 0, 0, 1, cal);
+
+assert.throws(RangeError, () => dt1.until(dt2, { smallestUnit: "years" }), "zero year length handled correctly");
+assert.throws(RangeError, () => dt1.until(dt2, { smallestUnit: "months" }), "zero month length handled correctly");
+assert.throws(RangeError, () => dt1.until(dt2, { smallestUnit: "weeks" }), "zero week length handled correctly");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-basic.js
new file mode 100644
index 0000000000..3196277706
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-basic.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.plaindatetime.prototype.until
+description: A variety of rounding increments
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2019, 1, 8, 8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainDateTime(2021, 9, 7, 12, 39, 40, 987, 654, 321);
+
+TemporalHelpers.assertDuration(
+ earlier.until(later, {smallestUnit: "hours", roundingIncrement: 3, roundingMode: "halfExpand"}),
+ 0, 0, 0, 973, 3, 0, 0, 0, 0, 0,
+ "rounds to an increment of hours"
+);
+
+TemporalHelpers.assertDuration(
+ earlier.until(later, {smallestUnit: "minutes", roundingIncrement: 30, roundingMode: "halfExpand"}),
+ 0, 0, 0, 973, 4, 30, 0, 0, 0, 0,
+ "rounds to an increment of minutes"
+);
+
+TemporalHelpers.assertDuration(
+ earlier.until(later, {smallestUnit: "seconds", roundingIncrement: 15, roundingMode: "halfExpand"}),
+ 0, 0, 0, 973, 4, 17, 0, 0, 0, 0,
+ "rounds to an increment of seconds"
+);
+
+TemporalHelpers.assertDuration(
+ earlier.until(later, {smallestUnit: "milliseconds", roundingIncrement: 10, roundingMode: "halfExpand"}),
+ 0, 0, 0, 973, 4, 17, 4, 860, 0, 0,
+ "rounds to an increment of milliseconds"
+);
+
+TemporalHelpers.assertDuration(
+ earlier.until(later, {smallestUnit: "microseconds", roundingIncrement: 10, roundingMode: "halfExpand"}),
+ 0, 0, 0, 973, 4, 17, 4, 864, 200, 0,
+ "rounds to an increment of microseconds"
+);
+
+TemporalHelpers.assertDuration(
+ earlier.until(later, {smallestUnit: "nanoseconds", roundingIncrement: 10, roundingMode: "halfExpand"}),
+ 0, 0, 0, 973, 4, 17, 4, 864, 197, 530,
+ "rounds to an increment of nanoseconds"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-cleanly-divides.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-cleanly-divides.js
new file mode 100644
index 0000000000..d9ee3df308
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-cleanly-divides.js
@@ -0,0 +1,42 @@
+// |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.plaindatetime.prototype.until
+description: Rounding increments that cleanly divide relevant units
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2019, 1, 8, 8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainDateTime(2021, 9, 7, 12, 39, 40, 987, 654, 321);
+
+[1, 2, 3, 4, 6, 8, 12].forEach((roundingIncrement) => {
+ const options = {smallestUnit: "hours", roundingIncrement};
+ assert(
+ earlier.until(later, options) instanceof Temporal.Duration,
+ `valid hour increments divide 24 (rounding increment = ${roundingIncrement})`
+ );
+});
+
+["minutes", "seconds"].forEach((smallestUnit) => {
+ [1, 2, 3, 4, 5, 6, 10, 12, 15, 20, 30].forEach((roundingIncrement) => {
+ const options = {smallestUnit, roundingIncrement};
+ assert(
+ earlier.until(later, options) instanceof Temporal.Duration,
+ `valid ${smallestUnit} increments divide 60 (rounding increment = ${roundingIncrement})`
+ );
+ });
+});
+
+["milliseconds", "microseconds", "nanoseconds"].forEach((smallestUnit) => {
+ [1, 2, 4, 5, 8, 10, 20, 25, 40, 50, 100, 125, 200, 250, 500].forEach((roundingIncrement) => {
+ const options = {smallestUnit, roundingIncrement};
+ assert(
+ earlier.until(later, options) instanceof Temporal.Duration,
+ `valid ${smallestUnit} increments divide 1000 (rounding increment = ${roundingIncrement}`
+ );
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-does-not-divide.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-does-not-divide.js
new file mode 100644
index 0000000000..92c300c98d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-does-not-divide.js
@@ -0,0 +1,48 @@
+// |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.plaindatetime.prototype.until
+description: Rounding increments that do not cleanly divide the relevant unit
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2019, 1, 8, 8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainDateTime(2021, 9, 7, 12, 39, 40, 987, 654, 321);
+
+const nondivisibleUnits = {
+ "hours": 11,
+ "minutes": 29,
+ "seconds": 29,
+ "milliseconds": 29,
+ "microseconds": 29,
+ "nanoseconds": 29
+};
+
+Object.entries(nondivisibleUnits).forEach(([unit, increment]) => {
+ assert.throws(
+ RangeError,
+ () => earlier.until(later, {smallestUnit: unit, roundingIncrement: increment}),
+ `throws on increments that do not divide evenly into the next highest (unit = ${unit}, increment = ${increment})`
+ );
+});
+
+const equalDivisibleUnits = {
+ "hours": 24,
+ "minutes": 60,
+ "seconds": 60,
+ "milliseconds": 1000,
+ "microseconds": 1000,
+ "nanoseconds": 1000
+};
+
+Object.entries(equalDivisibleUnits).forEach(([unit, increment]) => {
+ assert.throws(
+ RangeError,
+ () => earlier.until(later, {smallestUnit: unit, roundingIncrement: increment}),
+ `throws on increments that are equal to the next highest (unit = ${unit}, rounding increment = ${increment})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-nan.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-nan.js
new file mode 100644
index 0000000000..80f4f779a5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/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.plaindatetime.prototype.until
+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.plaindatetime.prototype.until step 12:
+ 12. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*).
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const later = new Temporal.PlainDateTime(2001, 6, 3, 13, 35, 57, 988, 655, 322);
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: NaN }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-non-integer.js
new file mode 100644
index 0000000000..859509878a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-non-integer.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.plaindatetime.prototype.until
+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.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 5);
+const result = earlier.until(later, { roundingIncrement: 2.5, roundingMode: "trunc" });
+TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, "roundingIncrement 2.5 truncates to 2");
+const result2 = earlier.until(later, { smallestUnit: "days", roundingIncrement: 1e9 + 0.5, roundingMode: "expand" });
+TemporalHelpers.assertDuration(result2, 0, 0, 0, 1e9, 0, 0, 0, 0, 0, 0, "roundingIncrement 1e9 + 0.5 truncates to 1e9");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-out-of-range.js
new file mode 100644
index 0000000000..8b5bf55cda
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/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.plaindatetime.prototype.until
+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.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 5);
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: -Infinity }));
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: -1 }));
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: 0 }));
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: 0.9 }));
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: 1e9 + 1 }));
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: Infinity }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-undefined.js
new file mode 100644
index 0000000000..a93a4e6b36
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/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.plaindatetime.prototype.until
+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.plaindatetime.prototype.until step 12:
+ 12. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const later = new Temporal.PlainDateTime(2001, 6, 3, 13, 35, 57, 988, 655, 322);
+
+const explicit = earlier.until(later, { roundingIncrement: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 397, 1, 1, 1, 1, 1, 1, "default roundingIncrement is 1");
+
+const implicit = earlier.until(later, {});
+TemporalHelpers.assertDuration(implicit, 0, 0, 0, 397, 1, 1, 1, 1, 1, 1, "default roundingIncrement is 1");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-wrong-type.js
new file mode 100644
index 0000000000..7ac6ac6d1f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/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.plaindatetime.prototype.until
+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.plaindatetime.prototype.until step 12:
+ 12. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*).
+includes: [temporalHelpers.js, compareArray.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const later = new Temporal.PlainDateTime(2001, 6, 3, 13, 35, 57, 988, 655, 322);
+
+TemporalHelpers.checkRoundingIncrementOptionWrongType(
+ (roundingIncrement) => earlier.until(later, { roundingIncrement }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 397, 1, 1, 1, 1, 1, 1, descr),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 397, 1, 1, 1, 1, 1, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-ceil.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-ceil.js
new file mode 100644
index 0000000000..b5b23655c0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-ceil.js
@@ -0,0 +1,45 @@
+// |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.plaindatetime.prototype.until
+description: Tests calculations with roundingMode "ceil".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2019, 1, 8, 8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainDateTime(2021, 9, 7, 12, 39, 40, 987, 654, 289);
+
+const expected = [
+ ["years", [3], [-2]],
+ ["months", [0, 32], [0, -31]],
+ ["weeks", [0, 0, 140], [0, 0, -139]],
+ ["days", [0, 0, 0, 974], [0, 0, 0, -973]],
+ ["hours", [0, 0, 0, 973, 5], [0, 0, 0, -973, -4]],
+ ["minutes", [0, 0, 0, 973, 4, 18], [0, 0, 0, -973, -4, -17]],
+ ["seconds", [0, 0, 0, 973, 4, 17, 5], [0, 0, 0, -973, -4, -17, -4]],
+ ["milliseconds", [0, 0, 0, 973, 4, 17, 4, 865], [0, 0, 0, -973, -4, -17, -4, -864]],
+ ["microseconds", [0, 0, 0, 973, 4, 17, 4, 864, 198], [0, 0, 0, -973, -4, -17, -4, -864, -197]],
+ ["nanoseconds", [0, 0, 0, 973, 4, 17, 4, 864, 197, 500], [0, 0, 0, -973, -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(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { 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/PlainDateTime/prototype/until/roundingmode-expand.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-expand.js
new file mode 100644
index 0000000000..63eb3bba0c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-expand.js
@@ -0,0 +1,45 @@
+// |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.plaindatetime.prototype.until
+description: Tests calculations with roundingMode "expand".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2019, 1, 8, 8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainDateTime(2021, 9, 7, 12, 39, 40, 987, 654, 289);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [0, 32], [0, -32]],
+ ["weeks", [0, 0, 140], [0, 0, -140]],
+ ["days", [0, 0, 0, 974], [0, 0, 0, -974]],
+ ["hours", [0, 0, 0, 973, 5], [0, 0, 0, -973, -5]],
+ ["minutes", [0, 0, 0, 973, 4, 18], [0, 0, 0, -973, -4, -18]],
+ ["seconds", [0, 0, 0, 973, 4, 17, 5], [0, 0, 0, -973, -4, -17, -5]],
+ ["milliseconds", [0, 0, 0, 973, 4, 17, 4, 865], [0, 0, 0, -973, -4, -17, -4, -865]],
+ ["microseconds", [0, 0, 0, 973, 4, 17, 4, 864, 198], [0, 0, 0, -973, -4, -17, -4, -864, -198]],
+ ["nanoseconds", [0, 0, 0, 973, 4, 17, 4, 864, 197, 500], [0, 0, 0, -973, -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(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { 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/PlainDateTime/prototype/until/roundingmode-floor.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-floor.js
new file mode 100644
index 0000000000..4fc41c1b12
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-floor.js
@@ -0,0 +1,45 @@
+// |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.plaindatetime.prototype.until
+description: Tests calculations with roundingMode "floor".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2019, 1, 8, 8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainDateTime(2021, 9, 7, 12, 39, 40, 987, 654, 289);
+
+const expected = [
+ ["years", [2], [-3]],
+ ["months", [0, 31], [0, -32]],
+ ["weeks", [0, 0, 139], [0, 0, -140]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -974]],
+ ["hours", [0, 0, 0, 973, 4], [0, 0, 0, -973, -5]],
+ ["minutes", [0, 0, 0, 973, 4, 17], [0, 0, 0, -973, -4, -18]],
+ ["seconds", [0, 0, 0, 973, 4, 17, 4], [0, 0, 0, -973, -4, -17, -5]],
+ ["milliseconds", [0, 0, 0, 973, 4, 17, 4, 864], [0, 0, 0, -973, -4, -17, -4, -865]],
+ ["microseconds", [0, 0, 0, 973, 4, 17, 4, 864, 197], [0, 0, 0, -973, -4, -17, -4, -864, -198]],
+ ["nanoseconds", [0, 0, 0, 973, 4, 17, 4, 864, 197, 500], [0, 0, 0, -973, -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(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { 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/PlainDateTime/prototype/until/roundingmode-halfCeil.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-halfCeil.js
new file mode 100644
index 0000000000..c9e6d10c97
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-halfCeil.js
@@ -0,0 +1,45 @@
+// |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.plaindatetime.prototype.until
+description: Tests calculations with roundingMode "halfCeil".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2019, 1, 8, 8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainDateTime(2021, 9, 7, 12, 39, 40, 987, 654, 289);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [0, 32], [0, -32]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+ ["hours", [0, 0, 0, 973, 4], [0, 0, 0, -973, -4]],
+ ["minutes", [0, 0, 0, 973, 4, 17], [0, 0, 0, -973, -4, -17]],
+ ["seconds", [0, 0, 0, 973, 4, 17, 5], [0, 0, 0, -973, -4, -17, -5]],
+ ["milliseconds", [0, 0, 0, 973, 4, 17, 4, 864], [0, 0, 0, -973, -4, -17, -4, -864]],
+ ["microseconds", [0, 0, 0, 973, 4, 17, 4, 864, 198], [0, 0, 0, -973, -4, -17, -4, -864, -197]],
+ ["nanoseconds", [0, 0, 0, 973, 4, 17, 4, 864, 197, 500], [0, 0, 0, -973, -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(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { 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/PlainDateTime/prototype/until/roundingmode-halfEven.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-halfEven.js
new file mode 100644
index 0000000000..b8c1b5ee20
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-halfEven.js
@@ -0,0 +1,45 @@
+// |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.plaindatetime.prototype.until
+description: Tests calculations with roundingMode "halfEven".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2019, 1, 8, 8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainDateTime(2021, 9, 7, 12, 39, 40, 987, 654, 289);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [0, 32], [0, -32]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+ ["hours", [0, 0, 0, 973, 4], [0, 0, 0, -973, -4]],
+ ["minutes", [0, 0, 0, 973, 4, 17], [0, 0, 0, -973, -4, -17]],
+ ["seconds", [0, 0, 0, 973, 4, 17, 5], [0, 0, 0, -973, -4, -17, -5]],
+ ["milliseconds", [0, 0, 0, 973, 4, 17, 4, 864], [0, 0, 0, -973, -4, -17, -4, -864]],
+ ["microseconds", [0, 0, 0, 973, 4, 17, 4, 864, 198], [0, 0, 0, -973, -4, -17, -4, -864, -198]],
+ ["nanoseconds", [0, 0, 0, 973, 4, 17, 4, 864, 197, 500], [0, 0, 0, -973, -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(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { 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/PlainDateTime/prototype/until/roundingmode-halfExpand.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-halfExpand.js
new file mode 100644
index 0000000000..1a833d6d0f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-halfExpand.js
@@ -0,0 +1,45 @@
+// |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.plaindatetime.prototype.until
+description: Tests calculations with roundingMode "halfExpand".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2019, 1, 8, 8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainDateTime(2021, 9, 7, 12, 39, 40, 987, 654, 289);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [0, 32], [0, -32]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+ ["hours", [0, 0, 0, 973, 4], [0, 0, 0, -973, -4]],
+ ["minutes", [0, 0, 0, 973, 4, 17], [0, 0, 0, -973, -4, -17]],
+ ["seconds", [0, 0, 0, 973, 4, 17, 5], [0, 0, 0, -973, -4, -17, -5]],
+ ["milliseconds", [0, 0, 0, 973, 4, 17, 4, 864], [0, 0, 0, -973, -4, -17, -4, -864]],
+ ["microseconds", [0, 0, 0, 973, 4, 17, 4, 864, 198], [0, 0, 0, -973, -4, -17, -4, -864, -198]],
+ ["nanoseconds", [0, 0, 0, 973, 4, 17, 4, 864, 197, 500], [0, 0, 0, -973, -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(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { 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/PlainDateTime/prototype/until/roundingmode-halfFloor.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-halfFloor.js
new file mode 100644
index 0000000000..96d4eb34e5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-halfFloor.js
@@ -0,0 +1,45 @@
+// |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.plaindatetime.prototype.until
+description: Tests calculations with roundingMode "halfFloor".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2019, 1, 8, 8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainDateTime(2021, 9, 7, 12, 39, 40, 987, 654, 289);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [0, 32], [0, -32]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+ ["hours", [0, 0, 0, 973, 4], [0, 0, 0, -973, -4]],
+ ["minutes", [0, 0, 0, 973, 4, 17], [0, 0, 0, -973, -4, -17]],
+ ["seconds", [0, 0, 0, 973, 4, 17, 5], [0, 0, 0, -973, -4, -17, -5]],
+ ["milliseconds", [0, 0, 0, 973, 4, 17, 4, 864], [0, 0, 0, -973, -4, -17, -4, -864]],
+ ["microseconds", [0, 0, 0, 973, 4, 17, 4, 864, 197], [0, 0, 0, -973, -4, -17, -4, -864, -198]],
+ ["nanoseconds", [0, 0, 0, 973, 4, 17, 4, 864, 197, 500], [0, 0, 0, -973, -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(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { 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/PlainDateTime/prototype/until/roundingmode-halfTrunc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-halfTrunc.js
new file mode 100644
index 0000000000..52011b2f13
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-halfTrunc.js
@@ -0,0 +1,45 @@
+// |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.plaindatetime.prototype.until
+description: Tests calculations with roundingMode "halfTrunc".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2019, 1, 8, 8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainDateTime(2021, 9, 7, 12, 39, 40, 987, 654, 289);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [0, 32], [0, -32]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+ ["hours", [0, 0, 0, 973, 4], [0, 0, 0, -973, -4]],
+ ["minutes", [0, 0, 0, 973, 4, 17], [0, 0, 0, -973, -4, -17]],
+ ["seconds", [0, 0, 0, 973, 4, 17, 5], [0, 0, 0, -973, -4, -17, -5]],
+ ["milliseconds", [0, 0, 0, 973, 4, 17, 4, 864], [0, 0, 0, -973, -4, -17, -4, -864]],
+ ["microseconds", [0, 0, 0, 973, 4, 17, 4, 864, 197], [0, 0, 0, -973, -4, -17, -4, -864, -197]],
+ ["nanoseconds", [0, 0, 0, 973, 4, 17, 4, 864, 197, 500], [0, 0, 0, -973, -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(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { 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/PlainDateTime/prototype/until/roundingmode-halfexpand-default-changes.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-halfexpand-default-changes.js
new file mode 100644
index 0000000000..72cde68fb8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-halfexpand-default-changes.js
@@ -0,0 +1,31 @@
+// |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.plaindatetime.prototype.until
+description: A different default for largest unit will be used if smallest unit is larger than "days"
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2019, 1, 8, 8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainDateTime(2021, 9, 7, 12, 39, 40, 987, 654, 321);
+
+TemporalHelpers.assertDuration(
+ earlier.until(later, {smallestUnit: "years", roundingMode: "halfExpand"}),
+ 3, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "assumes a different default for largestUnit if smallestUnit is larger than days (largest unit = years)"
+);
+TemporalHelpers.assertDuration(
+ earlier.until(later, {smallestUnit: "months", roundingMode: "halfExpand"}),
+ 0, 32, 0, 0, 0, 0, 0, 0, 0, 0,
+ "assumes a different default for largestUnit if smallestUnit is larger than days (largest unit = months)"
+);
+TemporalHelpers.assertDuration(
+ earlier.until(later, {smallestUnit: "weeks", roundingMode: "halfExpand"}),
+ 0, 0, 139, 0, 0, 0, 0, 0, 0, 0,
+ "assumes a different default for largestUnit if smallestUnit is larger than days (largest unit = weeks)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-invalid-string.js
new file mode 100644
index 0000000000..667e88aaa0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/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.plaindatetime.prototype.until
+description: RangeError thrown when roundingMode option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2000, 5, 3, 13, 35, 57, 123, 987, 500);
+for (const roundingMode of ["other string", "cile", "CEIL", "ce\u0131l", "auto", "halfexpand", "floor\0"]) {
+ assert.throws(RangeError, () => earlier.until(later, { smallestUnit: "microsecond", roundingMode }));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-trunc-is-default.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-trunc-is-default.js
new file mode 100644
index 0000000000..290e08cd04
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-trunc-is-default.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.plaindatetime.prototype.until
+description: Show that truncation is the default rounding mode
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2019, 1, 8, 8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainDateTime(2021, 9, 7, 12, 39, 40, 987, 654, 321);
+
+TemporalHelpers.assertDuration(
+ earlier.until(later, {smallestUnit: "minutes"}),
+ 0, 0, 0, 973, 4, 17,0, 0, 0, 0,
+ "trunc is the default (round up)"
+);
+
+TemporalHelpers.assertDuration(
+ earlier.until(later, {smallestUnit: "seconds"}),
+ 0, 0, 0, 973, 4, 17, 4, 0, 0, 0,
+ "trunc is the default (round down)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-trunc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-trunc.js
new file mode 100644
index 0000000000..6eb452ae4f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-trunc.js
@@ -0,0 +1,45 @@
+// |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.plaindatetime.prototype.until
+description: Tests calculations with roundingMode "trunc".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2019, 1, 8, 8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainDateTime(2021, 9, 7, 12, 39, 40, 987, 654, 289);
+
+const expected = [
+ ["years", [2], [-2]],
+ ["months", [0, 31], [0, -31]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+ ["hours", [0, 0, 0, 973, 4], [0, 0, 0, -973, -4]],
+ ["minutes", [0, 0, 0, 973, 4, 17], [0, 0, 0, -973, -4, -17]],
+ ["seconds", [0, 0, 0, 973, 4, 17, 4], [0, 0, 0, -973, -4, -17, -4]],
+ ["milliseconds", [0, 0, 0, 973, 4, 17, 4, 864], [0, 0, 0, -973, -4, -17, -4, -864]],
+ ["microseconds", [0, 0, 0, 973, 4, 17, 4, 864, 197], [0, 0, 0, -973, -4, -17, -4, -864, -197]],
+ ["nanoseconds", [0, 0, 0, 973, 4, 17, 4, 864, 197, 500], [0, 0, 0, -973, -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(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { 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/PlainDateTime/prototype/until/roundingmode-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-undefined.js
new file mode 100644
index 0000000000..3a25e93e3a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-undefined.js
@@ -0,0 +1,30 @@
+// |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.plaindatetime.prototype.until
+description: Fallback value for roundingMode option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2000, 5, 3, 13, 35, 57, 123, 987, 500);
+
+const explicit1 = earlier.until(later, { smallestUnit: "microsecond", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit1, 0, 0, 0, 1, 1, 1, 1, 123, 987, 0, "default roundingMode is trunc");
+const implicit1 = earlier.until(later, { smallestUnit: "microsecond" });
+TemporalHelpers.assertDuration(implicit1, 0, 0, 0, 1, 1, 1, 1, 123, 987, 0, "default roundingMode is trunc");
+
+const explicit2 = earlier.until(later, { smallestUnit: "millisecond", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit2, 0, 0, 0, 1, 1, 1, 1, 123, 0, 0, "default roundingMode is trunc");
+const implicit2 = earlier.until(later, { smallestUnit: "millisecond" });
+TemporalHelpers.assertDuration(implicit2, 0, 0, 0, 1, 1, 1, 1, 123, 0, 0, "default roundingMode is trunc");
+
+const explicit3 = earlier.until(later, { smallestUnit: "second", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit3, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, "default roundingMode is trunc");
+const implicit3 = earlier.until(later, { smallestUnit: "second" });
+TemporalHelpers.assertDuration(implicit3, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, "default roundingMode is trunc");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-wrong-type.js
new file mode 100644
index 0000000000..f8f12c6573
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/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.plaindatetime.prototype.until
+description: Type conversions for roundingMode option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2000, 5, 3, 13, 35, 57, 123, 987, 500);
+TemporalHelpers.checkStringOptionWrongType("roundingMode", "trunc",
+ (roundingMode) => earlier.until(later, { smallestUnit: "microsecond", roundingMode }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 1, 1, 1, 1, 123, 987, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/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/PlainDateTime/prototype/until/smallestunit-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/smallestunit-invalid-string.js
new file mode 100644
index 0000000000..93de6cef01
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/smallestunit-invalid-string.js
@@ -0,0 +1,31 @@
+// |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.plaindatetime.prototype.until
+description: RangeError thrown when smallestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2000, 5, 3, 13, 35, 57, 987, 654, 321);
+const badValues = [
+ "era",
+ "eraYear",
+ "millisecond\0",
+ "mill\u0131second",
+ "SECOND",
+ "eras",
+ "eraYears",
+ "milliseconds\0",
+ "mill\u0131seconds",
+ "SECONDS",
+ "other string",
+];
+for (const smallestUnit of badValues) {
+ assert.throws(RangeError, () => earlier.until(later, { smallestUnit }),
+ `"${smallestUnit}" is not a valid value for smallest unit`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/smallestunit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/smallestunit-plurals-accepted.js
new file mode 100644
index 0000000000..493b52af8b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/smallestunit-plurals-accepted.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.plaindatetime.prototype.until
+description: Plural units are accepted as well for the smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const later = new Temporal.PlainDateTime(2001, 6, 12, 13, 35, 57, 988, 655, 322);
+const validUnits = [
+ "year",
+ "month",
+ "week",
+ "day",
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+];
+TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => earlier.until(later, { smallestUnit }), validUnits);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/smallestunit-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/smallestunit-undefined.js
new file mode 100644
index 0000000000..884259ff1c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/smallestunit-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.plaindatetime.prototype.until
+description: Fallback value for smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2000, 5, 3, 13, 35, 57, 987, 654, 321);
+
+const explicit = earlier.until(later, { smallestUnit: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 1, 1, 1, 1, 987, 654, 321, "default smallestUnit is nanosecond");
+const implicit = earlier.until(later, {});
+TemporalHelpers.assertDuration(implicit, 0, 0, 0, 1, 1, 1, 1, 987, 654, 321, "default smallestUnit is nanosecond");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/smallestunit-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/smallestunit-wrong-type.js
new file mode 100644
index 0000000000..b646bd90bd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/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.plaindatetime.prototype.until
+description: Type conversions for smallestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2000, 5, 3, 13, 35, 57, 987, 654, 321);
+TemporalHelpers.checkStringOptionWrongType("smallestUnit", "microsecond",
+ (smallestUnit) => earlier.until(later, { smallestUnit }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 1, 1, 1, 1, 987, 654, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/subseconds.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/subseconds.js
new file mode 100644
index 0000000000..1bf72b6a69
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/subseconds.js
@@ -0,0 +1,33 @@
+// |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.plaindatetime.prototype.until
+description: Returned granularity may be finer than seconds
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const feb20 = new Temporal.PlainDateTime(2020, 2, 1, 0, 0);
+const feb21 = new Temporal.PlainDateTime(2020, 2, 2, 0, 0, 0, 250, 250, 250);
+
+TemporalHelpers.assertDuration(
+ feb20.until(feb21, { largestUnit: "milliseconds" }),
+ 0, 0, 0, 0, 0, 0, 0, 86400250, 250, 250,
+ "can return subseconds (millisecond precision)"
+);
+
+TemporalHelpers.assertDuration(
+ feb20.until(feb21, { largestUnit: "microseconds" }),
+ 0, 0, 0, 0, 0, 0, 0, 0, 86400250250, 250,
+ "can return subseconds (microsecond precision)"
+);
+
+TemporalHelpers.assertDuration(
+ feb20.until(feb21, { largestUnit: "nanoseconds" }),
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 86400250250250,
+ "can return subseconds (nanosecond precision)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/units-changed.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/units-changed.js
new file mode 100644
index 0000000000..3da503c495
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/units-changed.js
@@ -0,0 +1,69 @@
+// |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.plaindatetime.prototype.until
+description: Largest unit is respected
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const feb20 = new Temporal.PlainDateTime(2020, 2, 1, 0, 0);
+const feb21 = new Temporal.PlainDateTime(2021, 2, 1, 0, 0);
+
+TemporalHelpers.assertDuration(
+ feb20.until(feb21, { largestUnit: "years" }),
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "can return lower or higher units (years)"
+);
+
+TemporalHelpers.assertDuration(
+ feb20.until(feb21, { largestUnit: "months" }),
+ 0, 12, 0, 0, 0, 0, 0, 0, 0, 0,
+ "can return lower or higher units (months)"
+);
+
+TemporalHelpers.assertDuration(
+ feb20.until(feb21, { largestUnit: "weeks" }),
+ 0, 0, 52, 2, 0, 0, 0, 0, 0, 0,
+ "can return lower or higher units (weeks)"
+);
+
+TemporalHelpers.assertDuration(
+ feb20.until(feb21, { largestUnit: "hours" }),
+ 0, 0, 0, 0, 8784, 0, 0, 0, 0, 0,
+ "can return lower or higher units (hours)"
+);
+
+TemporalHelpers.assertDuration(
+ feb20.until(feb21, { largestUnit: "minutes" }),
+ 0, 0, 0, 0, 0, 527040, 0, 0, 0, 0,
+ "can return lower or higher units (minutes)"
+);
+
+TemporalHelpers.assertDuration(
+ feb20.until(feb21, { largestUnit: "seconds" }),
+ 0, 0, 0, 0, 0, 0, 31622400, 0, 0, 0,
+ "can return lower or higher units (seconds)"
+);
+
+TemporalHelpers.assertDuration(
+ feb20.until(feb21, { largestUnit: "milliseconds" }),
+ 0, 0, 0, 0, 0, 0, 0, 31622400000, 0, 0,
+ "can return lower or higher units (milliseconds)"
+);
+
+TemporalHelpers.assertDuration(
+ feb20.until(feb21, { largestUnit: "microseconds" }),
+ 0, 0, 0, 0, 0, 0, 0, 0, 31622400000000, 0,
+ "can return lower or higher units (microseconds)"
+);
+
+TemporalHelpers.assertDuration(
+ feb20.until(feb21, { largestUnit: "nanoseconds" }),
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 31622400000000000,
+ "can return lower or higher units (nanoseconds)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/weeks-months-mutually-exclusive.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/weeks-months-mutually-exclusive.js
new file mode 100644
index 0000000000..36acc146d4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/weeks-months-mutually-exclusive.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.plaindatetime.prototype.until
+description: Weeks and months are mutually exclusive
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dt = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789);
+const laterDateTime = dt.add({ days: 42, hours: 3 });
+
+TemporalHelpers.assertDuration(
+ dt.until(laterDateTime, { largestUnit: "weeks" }),
+ 0, 0, 6, 0, 3, 0, 0, 0, 0, 0,
+ "weeks and months mutually exclusive (prefer weeks)"
+);
+
+TemporalHelpers.assertDuration(
+ dt.until(laterDateTime, { largestUnit: "months" }),
+ 0, 1, 0, 12, 3, 0, 0, 0, 0, 0,
+ "weeks and months mutually exclusive (prefer months)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/year-zero.js
new file mode 100644
index 0000000000..cd6f5f364a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/year-zero.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.plaindatetime.prototype.until
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-12-07",
+ "-000000-12-07T03:24:30",
+ "-000000-12-07T03:24:30+01:00",
+ "-000000-12-07T03:24:30+00:00[UTC]",
+];
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.until(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/basic.js
new file mode 100644
index 0000000000..8cc67cdb3b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/basic.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.plaindatetime.prototype.valueof
+description: Comparison operators (except !== and ===) do not work
+features: [Temporal]
+---*/
+
+const dt1 = new Temporal.PlainDateTime(1963, 2, 13, 9, 36, 29, 123, 456, 789);
+const dt1again = new Temporal.PlainDateTime(1963, 2, 13, 9, 36, 29, 123, 456, 789);
+const dt2 = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789);
+
+assert.sameValue(dt1 === dt1, true, "object equality implies ===");
+assert.sameValue(dt1 !== dt1again, true, "object non-equality, even if all data is the same, implies !==");
+assert.throws(TypeError, () => dt1 < dt1, "< throws (same objects)");
+assert.throws(TypeError, () => dt1 < dt2, "< throws (different objects)");
+assert.throws(TypeError, () => dt1 > dt1, "> throws (same objects)");
+assert.throws(TypeError, () => dt1 > dt2, "> throws (different objects)");
+assert.throws(TypeError, () => dt1 <= dt1, "<= does not throw (same objects)");
+assert.throws(TypeError, () => dt1 <= dt2, "<= throws (different objects)");
+assert.throws(TypeError, () => dt1 >= dt1, ">= throws (same objects)");
+assert.throws(TypeError, () => dt1 >= dt2, ">= throws (different objects)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/branding.js
new file mode 100644
index 0000000000..7325efb87e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/branding.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.plaindatetime.prototype.valueof
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const valueOf = Temporal.PlainDateTime.prototype.valueOf;
+
+assert.sameValue(typeof valueOf, "function");
+
+assert.throws(TypeError, () => valueOf.call(undefined), "undefined");
+assert.throws(TypeError, () => valueOf.call(null), "null");
+assert.throws(TypeError, () => valueOf.call(true), "true");
+assert.throws(TypeError, () => valueOf.call(""), "empty string");
+assert.throws(TypeError, () => valueOf.call(Symbol()), "symbol");
+assert.throws(TypeError, () => valueOf.call(1), "1");
+assert.throws(TypeError, () => valueOf.call({}), "plain object");
+assert.throws(TypeError, () => valueOf.call(Temporal.PlainDateTime), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => valueOf.call(Temporal.PlainDateTime.prototype), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/builtin.js
new file mode 100644
index 0000000000..ebb2963ac2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/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.plaindatetime.prototype.valueof
+description: >
+ Tests that Temporal.PlainDateTime.prototype.valueOf
+ 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.PlainDateTime.prototype.valueOf),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.prototype.valueOf),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.prototype.valueOf),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.prototype.valueOf.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/length.js
new file mode 100644
index 0000000000..7c64b114ea
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/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.plaindatetime.prototype.valueof
+description: Temporal.PlainDateTime.prototype.valueOf.length is 0
+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.PlainDateTime.prototype.valueOf, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/name.js
new file mode 100644
index 0000000000..205c983cf8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/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.plaindatetime.prototype.valueof
+description: Temporal.PlainDateTime.prototype.valueOf.name is "valueOf".
+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.PlainDateTime.prototype.valueOf, "name", {
+ value: "valueOf",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/not-a-constructor.js
new file mode 100644
index 0000000000..36a384a2c0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/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.plaindatetime.prototype.valueof
+description: >
+ Temporal.PlainDateTime.prototype.valueOf 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.PlainDateTime.prototype.valueOf();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.prototype.valueOf), false,
+ "isConstructor(Temporal.PlainDateTime.prototype.valueOf)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/prop-desc.js
new file mode 100644
index 0000000000..60ad86a0d2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/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.plaindatetime.prototype.valueof
+description: The "valueOf" property of Temporal.PlainDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.prototype.valueOf,
+ "function",
+ "`typeof PlainDateTime.prototype.valueOf` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime.prototype, "valueOf", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/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/PlainDateTime/prototype/weekOfYear/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/basic.js
new file mode 100644
index 0000000000..9b1f195177
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/basic.js
@@ -0,0 +1,15 @@
+// |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-get-temporal.plaindatetime.prototype.weekofyear
+description: Checking week of year for a "normal" case (non-undefined, non-boundary case, etc.)
+features: [Temporal]
+---*/
+
+const calendar = Temporal.Calendar.from("iso8601");
+const datetime = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789, calendar);
+assert.sameValue(datetime.weekOfYear, 47, "check week of year information");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/branding.js
new file mode 100644
index 0000000000..826226663e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/branding.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-get-temporal.plaindatetime.prototype.weekofyear
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const weekOfYear = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "weekOfYear").get;
+
+assert.sameValue(typeof weekOfYear, "function");
+
+assert.throws(TypeError, () => weekOfYear.call(undefined), "undefined");
+assert.throws(TypeError, () => weekOfYear.call(null), "null");
+assert.throws(TypeError, () => weekOfYear.call(true), "true");
+assert.throws(TypeError, () => weekOfYear.call(""), "empty string");
+assert.throws(TypeError, () => weekOfYear.call(Symbol()), "symbol");
+assert.throws(TypeError, () => weekOfYear.call(1), "1");
+assert.throws(TypeError, () => weekOfYear.call({}), "plain object");
+assert.throws(TypeError, () => weekOfYear.call(Temporal.PlainDateTime), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => weekOfYear.call(Temporal.PlainDateTime.prototype), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..53c69b52e1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plaindatetime.prototype.weekofyear
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const weekOfYearOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "weekOfYear");
+Object.defineProperty(Temporal.Calendar.prototype, "weekOfYear", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("weekOfYear should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, "iso8601");
+instance.weekOfYear;
+
+Object.defineProperty(Temporal.Calendar.prototype, "weekOfYear", weekOfYearOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/custom.js
new file mode 100644
index 0000000000..f2f8584457
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/custom.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-get-temporal.plaindatetime.prototype.weekofyear
+description: Custom calendar tests for weekOfYear().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ weekOfYear(...args) {
+ ++calls;
+ assert.compareArray(args, [pdt], "weekOfYear arguments");
+ return 7;
+ }
+}
+
+const calendar = new CustomCalendar();
+const pdt = new Temporal.PlainDateTime(1830, 8, 25, 20, 0, 0, 0, 0, 0, calendar);
+const result = pdt.weekOfYear;
+assert.sameValue(result, 7, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/prop-desc.js
new file mode 100644
index 0000000000..269a04acb9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/prop-desc.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-get-temporal.plaindatetime.prototype.weekofyear
+description: The "weekOfYear" property of Temporal.PlainDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "weekOfYear");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/validate-calendar-value.js
new file mode 100644
index 0000000000..d1624e1617
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/validate-calendar-value.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-get-temporal.plaindatetime.prototype.weekofyear
+description: Validate result returned from calendar weekOfYear() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [null, TypeError],
+ [false, TypeError],
+ [Infinity, RangeError],
+ [-Infinity, RangeError],
+ [NaN, RangeError],
+ [-7, RangeError],
+ [-0.1, RangeError],
+ ["string", TypeError],
+ [Symbol("foo"), TypeError],
+ [7n, TypeError],
+ [{}, TypeError],
+ [true, TypeError],
+ [7.1, RangeError],
+ ["7", TypeError],
+ ["7.5", TypeError],
+ [{valueOf() { return 7; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ weekOfYear() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainDateTime(1981, 12, 15, 14, 15, 45, 987, 654, 321, calendar);
+ assert.throws(error, () => instance.weekOfYear, `${typeof result} ${String(result)} not converted to positive integer`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/argument-not-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/argument-not-object.js
new file mode 100644
index 0000000000..f0076b021f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/argument-not-object.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.plaindatetime.prototype.with
+description: Non-object arguments throw.
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const args = [
+ undefined,
+ null,
+ true,
+ "2020-01-12T10:20:30",
+ Symbol(),
+ 2020,
+ 2020n,
+];
+for (const argument of args) {
+ assert.throws(TypeError, () => instance.with(argument), `Does not support ${typeof argument}`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/argument-object-insufficient-data.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/argument-object-insufficient-data.js
new file mode 100644
index 0000000000..c63204f4a5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/argument-object-insufficient-data.js
@@ -0,0 +1,45 @@
+// |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.
+
+/*---
+description: Unrecognized properties (incl. plurals of recognized units) are ignored
+esid: sec-temporal.plaindatetime.prototype.with
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+assert.throws(
+ TypeError,
+ () => instance.with({}),
+ "empty object not acceptable"
+);
+
+const units = ["year", "month", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"];
+
+units.forEach((unit) => {
+ let plural = `${unit}s`;
+ let options = {};
+ options[plural] = 1;
+ assert.throws(
+ TypeError,
+ () => instance.with(options),
+ `plural unit ("${plural}" vs "${unit}") is not acceptable`
+ );
+});
+
+assert.throws(
+ TypeError,
+ () => instance.with({nonsense: true}),
+ "throw if no recognized properties present"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ instance.with({year: 1965, nonsense: true}),
+ 1965, 5, "M05", 2, 12, 34, 56, 987, 654, 321,
+ "unrecognized properties ignored & does not throw if recognized properties present)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/basic.js
new file mode 100644
index 0000000000..4d8893ceb1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/basic.js
@@ -0,0 +1,80 @@
+// |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.
+
+/*---
+description: A variety of "normal" (non-throwing, non-boundary case, non-null, etc.) arguments
+esid: sec-temporal.plaindatetime.prototype.with
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const datetime = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789);
+
+TemporalHelpers.assertPlainDateTime(
+ datetime.with({ year: 2019 }),
+ 2019, 11, "M11", 18, 15, 23, 30, 123, 456, 789,
+ "with year works"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ datetime.with({ month: 5 }),
+ 1976, 5, "M05", 18, 15, 23, 30, 123, 456, 789,
+ "with month works"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ datetime.with({ monthCode: "M05" }),
+ 1976, 5, "M05", 18, 15, 23, 30, 123, 456, 789,
+ "with month code works"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ datetime.with({ day: 5 }),
+ 1976, 11, "M11", 5, 15, 23, 30, 123, 456, 789,
+ "with day works"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ datetime.with({ hour: 5 }),
+ 1976, 11, "M11", 18, 5, 23, 30, 123, 456, 789,
+ "with hour works"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ datetime.with({ minute: 5 }),
+ 1976, 11, "M11", 18, 15, 5, 30, 123, 456, 789,
+ "with minute works"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ datetime.with({ second: 5 }),
+ 1976, 11, "M11", 18, 15, 23, 5, 123, 456, 789,
+ "with second works"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ datetime.with({ millisecond: 5 }),
+ 1976, 11, "M11", 18, 15, 23, 30, 5, 456, 789,
+ "with millisecond works"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ datetime.with({ microsecond: 5 }),
+ 1976, 11, "M11", 18, 15, 23, 30, 123, 5, 789,
+ "with microsecond works"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ datetime.with({ nanosecond: 5 }),
+ 1976, 11, "M11", 18, 15, 23, 30, 123, 456, 5,
+ "with nanosecond works"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ datetime.with({ month: 5, second: 15 }),
+ 1976, 5, "M05", 18, 15, 23, 15, 123, 456, 789,
+ "with month and second works"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/branding.js
new file mode 100644
index 0000000000..e658a4109b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/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.plaindatetime.prototype.with
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const with_ = Temporal.PlainDateTime.prototype.with;
+
+assert.sameValue(typeof with_, "function");
+
+const args = [{ year: 2022 }];
+
+assert.throws(TypeError, () => with_.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => with_.apply(null, args), "null");
+assert.throws(TypeError, () => with_.apply(true, args), "true");
+assert.throws(TypeError, () => with_.apply("", args), "empty string");
+assert.throws(TypeError, () => with_.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => with_.apply(1, args), "1");
+assert.throws(TypeError, () => with_.apply({}, args), "plain object");
+assert.throws(TypeError, () => with_.apply(Temporal.PlainDateTime, args), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => with_.apply(Temporal.PlainDateTime.prototype, args), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..63191733a5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/builtin-calendar-no-array-iteration.js
@@ -0,0 +1,23 @@
+// |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.plaindatetime.prototype.with
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.PlainDateTime(2023, 5, 2, 12, 34, 56, 987, 654, 321, "iso8601");
+instance.with({ day: 5 });
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..5b28018cf0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,46 @@
+// |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.plaindatetime.prototype.with
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const fieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "fields");
+Object.defineProperty(Temporal.Calendar.prototype, "fields", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("fields should not be looked up");
+ },
+});
+const mergeFieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "mergeFields");
+Object.defineProperty(Temporal.Calendar.prototype, "mergeFields", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("mergeFields should not be looked up");
+ },
+});
+const dateFromFieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dateFromFields");
+Object.defineProperty(Temporal.Calendar.prototype, "dateFromFields", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("dateFromFields should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, "iso8601");
+instance.with({ year: 2001 });
+
+Object.defineProperty(Temporal.Calendar.prototype, "fields", fieldsOriginal);
+Object.defineProperty(Temporal.Calendar.prototype, "mergeFields", mergeFieldsOriginal);
+Object.defineProperty(Temporal.Calendar.prototype, "dateFromFields", dateFromFieldsOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/builtin.js
new file mode 100644
index 0000000000..4f4737af99
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/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.plaindatetime.prototype.with
+description: >
+ Tests that Temporal.PlainDateTime.prototype.with
+ 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.PlainDateTime.prototype.with),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.prototype.with),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.prototype.with),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.prototype.with.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/calendar-fields-iterable.js
new file mode 100644
index 0000000000..26c0c8cc9d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/calendar-fields-iterable.js
@@ -0,0 +1,32 @@
+// |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.plaindatetime.prototype.with
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plaindatetime.prototype.with step 9:
+ 9. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+datetime.with({ year: 2005 });
+
+assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/calendar-fromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/calendar-fromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..53b61f8a84
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/calendar-fromfields-called-with-null-prototype-fields.js
@@ -0,0 +1,18 @@
+// |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.plaindatetime.prototype.with
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+instance.with({ day: 24 });
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should have been called on the calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/calendar-merge-fields-returns-primitive.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/calendar-merge-fields-returns-primitive.js
new file mode 100644
index 0000000000..805ed6e5ff
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/calendar-merge-fields-returns-primitive.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.plaindatetime.prototype.with
+description: >
+ with() should throw a TypeError if mergeFields() returns a primitive,
+ without passing the value on to any other calendar methods
+includes: [compareArray.js, temporalHelpers.js]
+features: [BigInt, Symbol, Temporal]
+---*/
+
+[undefined, null, true, 3.14159, "bad value", Symbol("no"), 7n].forEach((primitive) => {
+ const calendar = TemporalHelpers.calendarMergeFieldsReturnsPrimitive(primitive);
+ const instance = new Temporal.PlainDateTime(2000, 5, 2, 15, 30, 45, 987, 654, 321, calendar);
+ assert.throws(TypeError, () => instance.with({ year: 2005 }), "bad return from mergeFields() throws");
+ assert.sameValue(calendar.dateFromFieldsCallCount, 0, "dateFromFields() never called");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/calendar-mergefields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/calendar-mergefields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..295a391ef4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/calendar-mergefields-called-with-null-prototype-fields.js
@@ -0,0 +1,18 @@
+// |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.plaindatetime.prototype.with
+description: >
+ Calendar.mergeFields method is called with null-prototype fields objects
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckMergeFieldsPrototypePollution();
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+instance.with({ day: 24 });
+assert.sameValue(calendar.mergeFieldsCallCount, 1, "mergeFields should have been called on the calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/calendar-options.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/calendar-options.js
new file mode 100644
index 0000000000..55f92aa702
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/calendar-options.js
@@ -0,0 +1,36 @@
+// |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.plaindatetime.prototype.with
+description: >
+ The options argument is copied and the copy is passed to
+ Calendar#dateFromFields.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const options = {
+ extra: "property",
+};
+let calledDateFromFields = 0;
+class Calendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ dateFromFields(fields, optionsArg) {
+ ++calledDateFromFields;
+ assert.notSameValue(optionsArg, options, "should pass copied options object");
+ assert.sameValue(optionsArg.extra, "property", "should copy all properties from options object");
+ assert.sameValue(Object.getPrototypeOf(optionsArg), null, "Copy has null prototype");
+ return super.dateFromFields(fields, optionsArg);
+ }
+};
+const calendar = new Calendar();
+const plaindatetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+const result = plaindatetime.with({ year: 2005 }, options);
+TemporalHelpers.assertPlainDateTime(result, 2005, 5, "M05", 2, 12, 34, 56, 987, 654, 321);
+assert.sameValue(calledDateFromFields, 1, "should have called overridden dateFromFields once");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/calendar-temporal-object-throws.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/calendar-temporal-object-throws.js
new file mode 100644
index 0000000000..9e5eab90fb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/calendar-temporal-object-throws.js
@@ -0,0 +1,36 @@
+// |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.
+
+/*---
+description: Throws if a Temporal object with a calendar is supplied
+esid: sec-temporal.plaindatetime.prototype.with
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789);
+
+const values = [
+ Temporal.PlainDate.from("2022-04-12"),
+ Temporal.PlainDateTime.from("2022-04-12T15:19:45"),
+ Temporal.PlainMonthDay.from("04-12"),
+ Temporal.PlainTime.from("15:19:45"),
+ Temporal.PlainYearMonth.from("2022-04"),
+ Temporal.ZonedDateTime.from("2022-04-12T15:19:45[UTC]"),
+];
+
+for (const value of values) {
+ Object.defineProperty(value, "calendar", {
+ get() { throw new Test262Error("should not get calendar property") }
+ });
+ Object.defineProperty(value, "timeZone", {
+ get() { throw new Test262Error("should not get timeZone property") }
+ });
+ assert.throws(
+ TypeError,
+ () => datetime.with(value),
+ "throws with temporal object"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/calendar-throws.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/calendar-throws.js
new file mode 100644
index 0000000000..9565cb370a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/calendar-throws.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.
+
+/*---
+description: Throws if a calendar is supplied
+esid: sec-temporal.plaindatetime.prototype.with
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789);
+
+assert.throws(
+ TypeError,
+ () => datetime.with({ year: 2021, calendar: "iso8601" }),
+ "throws with calendar property"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..35f27a64bf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/constructor-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |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.plaindatetime.prototype.with
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const datetime = new Temporal.PlainDateTime(2023, 5, 1, 0, 0, 0, 0, 0, 0, calendar);
+
+assert.throws(RangeError, () => datetime.with({hour: 12}));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/copies-merge-fields-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/copies-merge-fields-object.js
new file mode 100644
index 0000000000..a60773fc91
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/copies-merge-fields-object.js
@@ -0,0 +1,58 @@
+// |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.plaindatetime.prototype.with
+description: The object returned from mergeFields() is copied before being passed to dateFromFields().
+info: |
+ sec-temporal.plaindatetime.prototype.with steps 13–15:
+ 13. Set _fields_ to ? CalendarMergeFields(_calendar_, _fields_, _partialDate_).
+ 14. Set _fields_ to ? PrepareTemporalFields(_fields_, _fieldNames_, «»).
+ 15. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+ sec-temporal-interprettemporaldatetimefields step 2:
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "get day",
+ "get day.valueOf",
+ "call day.valueOf",
+ "get hour",
+ "get hour.valueOf",
+ "call hour.valueOf",
+ "get microsecond",
+ "get microsecond.valueOf",
+ "call microsecond.valueOf",
+ "get millisecond",
+ "get millisecond.valueOf",
+ "call millisecond.valueOf",
+ "get minute",
+ "get minute.valueOf",
+ "call minute.valueOf",
+ "get month",
+ "get month.valueOf",
+ "call month.valueOf",
+ "get monthCode",
+ "get monthCode.toString",
+ "call monthCode.toString",
+ "get nanosecond",
+ "get nanosecond.valueOf",
+ "call nanosecond.valueOf",
+ "get second",
+ "get second.valueOf",
+ "call second.valueOf",
+ "get year",
+ "get year.valueOf",
+ "call year.valueOf",
+];
+
+const calendar = TemporalHelpers.calendarMergeFieldsGetters();
+const datetime = new Temporal.PlainDateTime(2021, 3, 31, 12, 34, 56, 987, 654, 321, calendar);
+datetime.with({ year: 2022 });
+
+assert.compareArray(calendar.mergeFieldsReturnOperations, expected, "getters called on mergeFields return");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/copy-properties-not-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/copy-properties-not-undefined.js
new file mode 100644
index 0000000000..60f627767f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/copy-properties-not-undefined.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.plaindatetime.prototype.with
+description: PreparePartialTemporalFields copies only defined properties of source object
+info: |
+ 4. For each value _property_ of _fieldNames_, do
+ a. Let _value_ be ? Get(_fields_, _property_).
+ b. If _value_ is not *undefined*, then
+ ...
+ iii. Perform ! CreateDataPropertyOrThrow(_result_, _property_, _value_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainDateTime = new Temporal.PlainDateTime(2006, 1, 24, 11, 42, 58);
+
+TemporalHelpers.assertPlainDateTime(plainDateTime.with({ day: 8, hour: 10, year: undefined }),
+ 2006, 1, "M01", 8, 10, 42, 58, 0, 0, 0,
+ "only the properties that are present and defined in the plain object are copied"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/duplicate-calendar-fields.js
new file mode 100644
index 0000000000..5be05fd288
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/duplicate-calendar-fields.js
@@ -0,0 +1,18 @@
+// |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.plaindatetime.prototype.with
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['monthCode'], ['day'], ['hour'], ['microsecond'], ['millisecond'], ['minute'], ['month'], ['nanosecond'], ['second'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const datetime = new Temporal.PlainDateTime(2023, 5, 1, 0, 0, 0, 0, 0, 0, calendar);
+
+ assert.throws(RangeError, () => datetime.with({hour: 12}));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..ae7ad4a2b4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/infinity-throws-rangeerror.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.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plaindatetime.prototype.with
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((prop) => {
+ ["constrain", "reject"].forEach((overflow) => {
+ assert.throws(RangeError, () => instance.with({ [prop]: inf }, { overflow }), `${prop} property cannot be ${inf} (overflow ${overflow}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.with({ [prop]: obj }, { overflow }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/length.js
new file mode 100644
index 0000000000..539f00c49e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/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.plaindatetime.prototype.with
+description: Temporal.PlainDateTime.prototype.with.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.PlainDateTime.prototype.with, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/month-and-monthcode-must-agree.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/month-and-monthcode-must-agree.js
new file mode 100644
index 0000000000..f936006c14
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/month-and-monthcode-must-agree.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.plaindatetime.prototype.with
+description: The month and month code should agree
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789);
+
+assert.throws(
+ RangeError,
+ () => datetime.with({ month: 5, monthCode: "M06" }),
+ "month and monthCode must agree"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/multiple-unrecognized-properties-ignored.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/multiple-unrecognized-properties-ignored.js
new file mode 100644
index 0000000000..9c9eb5c6dd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/multiple-unrecognized-properties-ignored.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.
+
+/*---
+description: Unrecognized units are ignored
+esid: sec-temporal.plaindatetime.prototype.with
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const datetime = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789);
+const units = ["year", "month", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"];
+
+units.forEach((unit) => {
+ let plural = `${unit}s`;
+ let arg = { month: 12 };
+ arg[plural] = 1;
+ TemporalHelpers.assertPlainDateTime(
+ datetime.with(arg),
+ 1976, 12, "M12", 18, 15, 23, 30, 123, 456, 789,
+ `unrecognized property (${plural}) gets ignored`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/name.js
new file mode 100644
index 0000000000..a30c3f28e2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/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.plaindatetime.prototype.with
+description: Temporal.PlainDateTime.prototype.with.name is "with".
+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.PlainDateTime.prototype.with, "name", {
+ value: "with",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/not-a-constructor.js
new file mode 100644
index 0000000000..3ab26bd234
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/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.plaindatetime.prototype.with
+description: >
+ Temporal.PlainDateTime.prototype.with 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.PlainDateTime.prototype.with();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.prototype.with), false,
+ "isConstructor(Temporal.PlainDateTime.prototype.with)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/options-empty.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/options-empty.js
new file mode 100644
index 0000000000..9ac3c21267
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/options-empty.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.plaindatetime.prototype.with
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const datetime = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789);
+
+TemporalHelpers.assertPlainDateTime(
+ datetime.with({ day: 40 }, {}),
+ 1976, 11, "M11", 30, 15, 23, 30, 123, 456, 789,
+ "options may be empty object"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ datetime.with({ day: 40 }, () => {}),
+ 1976, 11, "M11", 30, 15, 23, 30, 123, 456, 789,
+ "read empty options from function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/options-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/options-invalid.js
new file mode 100644
index 0000000000..2f945aeb50
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/options-invalid.js
@@ -0,0 +1,23 @@
+// |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.plaindatetime.prototype.with
+description: Verify that undefined options are handled correctly.
+features: [Temporal, Symbol]
+---*/
+
+const datetime = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789);
+
+const badOptions = [null, 1, 'hello', true, Symbol('foo'), 1n];
+
+badOptions.forEach((bad) => {
+ assert.throws(
+ TypeError,
+ () => datetime.with({ day: 5 }, bad),
+ `bad options (${typeof bad})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/options-undefined.js
new file mode 100644
index 0000000000..0b37d9c62a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/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.plaindatetime.prototype.with
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 2, 2, 12, 34, 56, 987, 654, 321);
+const fields = { day: 31 };
+
+const explicit = datetime.with(fields, undefined);
+assert.sameValue(explicit.month, 2, "default overflow is constrain");
+assert.sameValue(explicit.day, 29, "default overflow is constrain");
+
+const implicit = datetime.with(fields);
+assert.sameValue(implicit.month, 2, "default overflow is constrain");
+assert.sameValue(implicit.day, 29, "default overflow is constrain");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/options-wrong-type.js
new file mode 100644
index 0000000000..0831b45561
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/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.plaindatetime.prototype.with
+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.PlainDateTime(2000, 5, 2);
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.with({ day: 5 }, value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/order-of-operations.js
new file mode 100644
index 0000000000..a408ffd916
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/order-of-operations.js
@@ -0,0 +1,110 @@
+// |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.plaindatetime.prototype.with
+description: Properties on an object passed to with() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ // RejectObjectWithCalendarOrTimeZone
+ "get fields.calendar",
+ "get fields.timeZone",
+ // CopyDataProperties
+ "ownKeys options",
+ "getOwnPropertyDescriptor options.overflow",
+ "get options.overflow",
+ "getOwnPropertyDescriptor options.extra",
+ "get options.extra",
+ // lookup
+ "get this.calendar.dateFromFields",
+ "get this.calendar.fields",
+ "get this.calendar.mergeFields",
+ // CalendarFields
+ "call this.calendar.fields",
+ // PrepareTemporalFields on receiver
+ "get this.calendar.day",
+ "call this.calendar.day",
+ "get this.calendar.month",
+ "call this.calendar.month",
+ "get this.calendar.monthCode",
+ "call this.calendar.monthCode",
+ "get this.calendar.year",
+ "call this.calendar.year",
+ // PrepareTemporalFields on argument
+ "get fields.day",
+ "get fields.day.valueOf",
+ "call fields.day.valueOf",
+ "get fields.hour",
+ "get fields.hour.valueOf",
+ "call fields.hour.valueOf",
+ "get fields.microsecond",
+ "get fields.microsecond.valueOf",
+ "call fields.microsecond.valueOf",
+ "get fields.millisecond",
+ "get fields.millisecond.valueOf",
+ "call fields.millisecond.valueOf",
+ "get fields.minute",
+ "get fields.minute.valueOf",
+ "call fields.minute.valueOf",
+ "get fields.month",
+ "get fields.month.valueOf",
+ "call fields.month.valueOf",
+ "get fields.monthCode",
+ "get fields.monthCode.toString",
+ "call fields.monthCode.toString",
+ "get fields.nanosecond",
+ "get fields.nanosecond.valueOf",
+ "call fields.nanosecond.valueOf",
+ "get fields.second",
+ "get fields.second.valueOf",
+ "call fields.second.valueOf",
+ "get fields.year",
+ "get fields.year.valueOf",
+ "call fields.year.valueOf",
+ // CalendarMergeFields
+ "call this.calendar.mergeFields",
+ // InterpretTemporalDateTimeFields
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+ "call this.calendar.dateFromFields",
+];
+const actual = [];
+
+const calendar = TemporalHelpers.calendarObserver(actual, "this.calendar");
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+// clear observable operations that occurred during the constructor call
+actual.splice(0);
+
+TemporalHelpers.observeProperty(actual, instance, "hour", 12, "this");
+TemporalHelpers.observeProperty(actual, instance, "minute", 34, "this");
+TemporalHelpers.observeProperty(actual, instance, "second", 56, "this");
+TemporalHelpers.observeProperty(actual, instance, "millisecond", 987, "this");
+TemporalHelpers.observeProperty(actual, instance, "microsecond", 654, "this");
+TemporalHelpers.observeProperty(actual, instance, "nanosecond", 321, "this");
+
+const fields = TemporalHelpers.propertyBagObserver(actual, {
+ year: 1.7,
+ month: 1.7,
+ monthCode: "M01",
+ day: 1.7,
+ hour: 1.7,
+ minute: 1.7,
+ second: 1.7,
+ millisecond: 1.7,
+ microsecond: 1.7,
+ nanosecond: 1.7,
+}, "fields");
+
+const options = TemporalHelpers.propertyBagObserver(actual, {
+ overflow: "constrain",
+ extra: "property",
+}, "options");
+
+instance.with(fields, options);
+assert.compareArray(actual, expected, "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/overflow-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/overflow-invalid-string.js
new file mode 100644
index 0000000000..750d7be7e0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/overflow-invalid-string.js
@@ -0,0 +1,32 @@
+// |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.plaindatetime.prototype.with
+description: RangeError thrown when overflow option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-interprettemporaldatetimefields steps 2–3:
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+ 3. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plaindatetime.prototype.with step 16:
+ 16. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12);
+
+const badOverflows = ["", "CONSTRAIN", "balance", "other string", "constra\u0131n", "reject\0"];
+for (const overflow of badOverflows) {
+ assert.throws(
+ RangeError,
+ () => datetime.with({ minute: 45 }, { overflow }),
+ `invalid overflow ("${overflow}")`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/overflow-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/overflow-undefined.js
new file mode 100644
index 0000000000..32cf45f248
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/overflow-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.plaindatetime.prototype.with
+description: Fallback value for overflow option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-interprettemporaldatetimefields steps 2–3:
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+ 3. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plaindatetime.prototype.with step 16:
+ 16. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12);
+const explicit = datetime.with({ minute: 67 }, { overflow: undefined });
+TemporalHelpers.assertPlainDateTime(explicit, 2000, 5, "M05", 2, 12, 59, 0, 0, 0, 0, "default overflow is constrain");
+const implicit = datetime.with({ minute: 67 }, {});
+TemporalHelpers.assertPlainDateTime(implicit, 2000, 5, "M05", 2, 12, 59, 0, 0, 0, 0, "default overflow is constrain");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/overflow-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/overflow-wrong-type.js
new file mode 100644
index 0000000000..6af0e9768a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/overflow-wrong-type.js
@@ -0,0 +1,48 @@
+// |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.plaindatetime.prototype.with
+description: Type conversions for overflow option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-interprettemporaldatetimefields steps 2–3:
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+ 3. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plaindatetime.prototype.with step 16:
+ 16. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12);
+
+// See TemporalHelpers.checkStringOptionWrongType(); this code path has
+// different expectations for observable calls
+
+assert.throws(RangeError, () => datetime.with({ minute: 45 }, { overflow: null }), "null");
+assert.throws(RangeError, () => datetime.with({ minute: 45 }, { overflow: true }), "true");
+assert.throws(RangeError, () => datetime.with({ minute: 45 }, { overflow: false }), "false");
+assert.throws(TypeError, () => datetime.with({ minute: 45 }, { overflow: Symbol() }), "symbol");
+assert.throws(RangeError, () => datetime.with({ minute: 45 }, { overflow: 2 }), "number");
+assert.throws(RangeError, () => datetime.with({ minute: 45 }, { overflow: 2n }), "bigint");
+assert.throws(RangeError, () => datetime.with({ minute: 45 }, { overflow: {} }), "plain object");
+
+// toString property should only be read and converted to a string once, because
+// a copied object with the resulting string on it is passed to
+// Calendar.dateFromFields().
+const expected = [
+ "get overflow.toString",
+ "call overflow.toString",
+];
+const actual = [];
+const observer = TemporalHelpers.toPrimitiveObserver(actual, "constrain", "overflow");
+const result = datetime.with({ minute: 45 }, { overflow: observer });
+TemporalHelpers.assertPlainDateTime(result, 2000, 5, "M05", 2, 12, 45, 0, 0, 0, 0, "object with toString");
+assert.compareArray(actual, expected, "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/prop-desc.js
new file mode 100644
index 0000000000..bbbe9d6d15
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/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.plaindatetime.prototype.with
+description: The "with" property of Temporal.PlainDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.prototype.with,
+ "function",
+ "`typeof PlainDateTime.prototype.with` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime.prototype, "with", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/proto-in-calendar-fields.js
new file mode 100644
index 0000000000..6bfd62f345
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/proto-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |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.plaindatetime.prototype.with
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const datetime = new Temporal.PlainDateTime(2023, 5, 1, 0, 0, 0, 0, 0, 0, calendar);
+
+assert.throws(RangeError, () => datetime.with({hour: 12}));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/read-time-fields-before-datefromfields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/read-time-fields-before-datefromfields.js
new file mode 100644
index 0000000000..9fc273cdfb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/read-time-fields-before-datefromfields.js
@@ -0,0 +1,29 @@
+// |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.plaindatetime.prototype.with
+description: The time fields are read from the object before being passed to dateFromFields().
+info: |
+ sec-temporal.plaindatetime.prototype.with step 15:
+ 15. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+ sec-temporal-interprettemporaldatetimefields steps 1–2:
+ 1. Let _timeResult_ be ? ToTemporalTimeRecord(_fields_).
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarMakeInfinityTime();
+const datetime = new Temporal.PlainDateTime(2021, 3, 31, 12, 34, 56, 987, 654, 321, calendar);
+const newDatetime = datetime.with({ year: 2022 });
+
+assert.sameValue(newDatetime.hour, 12, "hour value");
+assert.sameValue(newDatetime.minute, 34, "minute value");
+assert.sameValue(newDatetime.second, 56, "second value");
+assert.sameValue(newDatetime.millisecond, 987, "millisecond value");
+assert.sameValue(newDatetime.microsecond, 654, "microsecond value");
+assert.sameValue(newDatetime.nanosecond, 321, "nanosecond value");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/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/PlainDateTime/prototype/with/string-throws.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/string-throws.js
new file mode 100644
index 0000000000..f6908a4cca
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/string-throws.js
@@ -0,0 +1,23 @@
+// |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.
+
+/*---
+description: Throws if a string argument is supplied
+esid: sec-temporal.plaindatetime.prototype.with
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+const baddies = ["12:00", "1995-04-07", "2019-05-17T12:34:56.007007007", "2019-05-17T12:34:56.007007007Z", "42"];
+
+baddies.forEach((bad) => {
+ assert.throws(
+ TypeError,
+ () => instance.with(bad),
+ `bad argument (${bad})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/subclassing-ignored.js
new file mode 100644
index 0000000000..b06990e6bd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/subclassing-ignored.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.plaindatetime.prototype.with
+description: Objects of a subclass are never created as return values for with()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.PlainDateTime,
+ [2000, 5, 2, 12, 34, 56, 987, 654, 321],
+ "with",
+ [{ nanosecond: 1 }],
+ (result) => TemporalHelpers.assertPlainDateTime(result, 2000, 5, "M05", 2, 12, 34, 56, 987, 654, 1),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/timezone-throws.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/timezone-throws.js
new file mode 100644
index 0000000000..3460a16758
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/timezone-throws.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.
+
+/*---
+description: Throws if a timezone is supplied
+esid: sec-temporal.plaindatetime.prototype.with
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789);
+
+assert.throws(
+ TypeError,
+ () => datetime.with({ year: 2021, timeZone: "UTC" }),
+ "throws with timezone property"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/argument-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/argument-string.js
new file mode 100644
index 0000000000..e9e1586477
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/argument-string.js
@@ -0,0 +1,57 @@
+// |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.plaindatetime.prototype.withcalendar
+description: String argument, if it names a recognizable calendar, gets cast
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const calendar = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "something special",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ toString() { return "something special"; },
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+const dt = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789, calendar);
+const result = dt.withCalendar("iso8601");
+
+TemporalHelpers.assertPlainDateTime(
+ result,
+ 1976, 11, "M11", 18, 15, 23, 30, 123, 456, 789,
+ "'iso8601' is a recognizable calendar"
+);
+
+assert.sameValue(
+ result.getISOFields().calendar,
+ "iso8601",
+ "underlying calendar has changed and calendar slot stores a string"
+);
+
+assert.throws(
+ RangeError,
+ () => dt.withCalendar("this will fail"),
+ "unknown calendar throws"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/basic.js
new file mode 100644
index 0000000000..1509be6e07
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/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.plaindatetime.prototype.withcalendar
+description: Non-throwing non-edge case
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const dt = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789);
+const calendar = new Temporal.Calendar("iso8601");
+
+const result = dt.withCalendar(calendar);
+
+TemporalHelpers.assertPlainDateTime(
+ result,
+ 1976, 11, "M11", 18, 15, 23, 30, 123, 456, 789,
+ "works"
+);
+
+assert.sameValue(result.getCalendar(), calendar, "underlying calendar is unchanged");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/branding.js
new file mode 100644
index 0000000000..a888613ab4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/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.plaindatetime.prototype.withcalendar
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const withCalendar = Temporal.PlainDateTime.prototype.withCalendar;
+
+assert.sameValue(typeof withCalendar, "function");
+
+const args = [new Temporal.Calendar("iso8601")];
+
+assert.throws(TypeError, () => withCalendar.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => withCalendar.apply(null, args), "null");
+assert.throws(TypeError, () => withCalendar.apply(true, args), "true");
+assert.throws(TypeError, () => withCalendar.apply("", args), "empty string");
+assert.throws(TypeError, () => withCalendar.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => withCalendar.apply(1, args), "1");
+assert.throws(TypeError, () => withCalendar.apply({}, args), "plain object");
+assert.throws(TypeError, () => withCalendar.apply(Temporal.PlainDateTime, args), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => withCalendar.apply(Temporal.PlainDateTime.prototype, args), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..004403b09a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plaindatetime.prototype.withcalendar
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const idOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "id");
+Object.defineProperty(Temporal.Calendar.prototype, "id", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("id should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, "iso8601");
+instance.withCalendar("iso8601");
+
+Object.defineProperty(Temporal.Calendar.prototype, "id", idOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/builtin.js
new file mode 100644
index 0000000000..49f64fd2fc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/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.plaindatetime.prototype.withcalendar
+description: >
+ Tests that Temporal.PlainDateTime.prototype.withCalendar
+ 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.PlainDateTime.prototype.withCalendar),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.prototype.withCalendar),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.prototype.withCalendar),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.prototype.withCalendar.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/calendar-case-insensitive.js
new file mode 100644
index 0000000000..90f3905fff
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/calendar-case-insensitive.js
@@ -0,0 +1,39 @@
+// |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.plaindatetime.prototype.withcalendar
+description: Calendar names are case-insensitive
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789, {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "replace-me",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+});
+
+const arg = "iSo8601";
+const result = instance.withCalendar(arg);
+assert.sameValue(result.calendarId, "iso8601", "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/calendar-number.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/calendar-number.js
new file mode 100644
index 0000000000..34afdcd997
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/calendar-number.js
@@ -0,0 +1,50 @@
+// |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.plaindatetime.prototype.withcalendar
+description: A number is not allowed to be a calendar
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789, {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "replace-me",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+});
+
+const numbers = [
+ 1,
+ -19761118,
+ 19761118,
+ 1234567890,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => instance.withCalendar(arg),
+ "A number is not a valid ISO string for Calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/calendar-string-leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/calendar-string-leap-second.js
new file mode 100644
index 0000000000..bd62a3a13c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/calendar-string-leap-second.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.plaindatetime.prototype.withcalendar
+description: Leap second is a valid ISO string for Calendar
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789, {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "replace-me",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+});
+
+const arg = "2016-12-31T23:59:60";
+const result = instance.withCalendar(arg);
+assert.sameValue(
+ result.calendarId,
+ "iso8601",
+ "leap second is a valid ISO string for Calendar"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/calendar-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/calendar-string.js
new file mode 100644
index 0000000000..c8018bde0d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/calendar-string.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.plaindatetime.prototype.withcalendar
+description: A calendar ID is valid input for Calendar
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789, {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "replace-me",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+});
+
+const arg = "iso8601";
+
+const result = instance.withCalendar(arg);
+assert.sameValue(result.getISOFields().calendar, "iso8601", `Calendar created from string "${arg}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/calendar-temporal-object.js
new file mode 100644
index 0000000000..fdabe3e22c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/calendar-temporal-object.js
@@ -0,0 +1,64 @@
+// |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.plaindatetime.prototype.withcalendar
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal-totemporalcalendar step 1.b:
+ b. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const plainDate = new Temporal.PlainDate(2000, 5, 2);
+const plainDateTime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const plainMonthDay = new Temporal.PlainMonthDay(5, 2);
+const plainYearMonth = new Temporal.PlainYearMonth(2000, 5);
+const zonedDateTime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+
+[plainDate, plainDateTime, plainMonthDay, plainYearMonth, zonedDateTime].forEach((arg) => {
+ const actual = [];
+ const expected = [];
+
+ const calendar = arg.getISOFields().calendar;
+
+ Object.defineProperty(arg, "calendar", {
+ get() {
+ actual.push("get calendar");
+ return calendar;
+ },
+ });
+
+ const instance = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789, {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "replace-me",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+});
+ const result = instance.withCalendar(arg);
+ assert.sameValue(result.getISOFields().calendar, calendar, "Temporal object coerced to calendar");
+
+ assert.compareArray(actual, expected, "calendar getter not called");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/calendar-wrong-type.js
new file mode 100644
index 0000000000..ff7d515fbf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/calendar-wrong-type.js
@@ -0,0 +1,64 @@
+// |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.plaindatetime.prototype.withcalendar
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or object for Calendar
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789, {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "replace-me",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+});
+
+const primitiveTests = [
+ [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.withCalendar(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.withCalendar(arg), `${description} is not a valid object and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/length.js
new file mode 100644
index 0000000000..73d506d949
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/length.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.plaindatetime.prototype.withcalendar
+description: Temporal.PlainDateTime.prototype.withCalendar.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.PlainDateTime.prototype.withCalendar, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/missing-argument.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/missing-argument.js
new file mode 100644
index 0000000000..502f7a9f48
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/missing-argument.js
@@ -0,0 +1,15 @@
+// |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.plaindatetime.prototype.withcalendar
+description: TypeError thrown when calendar argument not given
+features: [Temporal]
+---*/
+
+const plainDateTime = Temporal.PlainDateTime.from("1976-11-18T14:00:00");
+assert.throws(TypeError, () => plainDateTime.withCalendar(), "missing argument");
+assert.throws(TypeError, () => plainDateTime.withCalendar(undefined), "undefined argument");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/name.js
new file mode 100644
index 0000000000..1d9d820482
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/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.plaindatetime.prototype.withcalendar
+description: Temporal.PlainDateTime.prototype.withCalendar.name is "withCalendar".
+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.PlainDateTime.prototype.withCalendar, "name", {
+ value: "withCalendar",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/not-a-constructor.js
new file mode 100644
index 0000000000..fd1c779874
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/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.plaindatetime.prototype.withcalendar
+description: >
+ Temporal.PlainDateTime.prototype.withCalendar 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.PlainDateTime.prototype.withCalendar();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.prototype.withCalendar), false,
+ "isConstructor(Temporal.PlainDateTime.prototype.withCalendar)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/prop-desc.js
new file mode 100644
index 0000000000..9059757976
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/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.plaindatetime.prototype.withcalendar
+description: The "withCalendar" property of Temporal.PlainDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.prototype.withCalendar,
+ "function",
+ "`typeof PlainDateTime.prototype.withCalendar` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime.prototype, "withCalendar", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/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/PlainDateTime/prototype/withCalendar/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/subclassing-ignored.js
new file mode 100644
index 0000000000..f8670228b6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/subclassing-ignored.js
@@ -0,0 +1,50 @@
+// |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.plaindatetime.prototype.withcalendar
+description: Objects of a subclass are never created as return values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const customCalendar = {
+ era() { return undefined; },
+ eraYear() { return undefined; },
+ year() { return 1900; },
+ month() { return 2; },
+ monthCode() { return "M02"; },
+ day() { return 5; },
+ id: "custom-calendar",
+ toString() { return "custom-calendar"; },
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.PlainDateTime,
+ [2000, 5, 2, 12, 34, 56, 987, 654, 321],
+ "withCalendar",
+ [customCalendar],
+ (result) => {
+ TemporalHelpers.assertPlainDateTime(result, 1900, 2, "M02", 5, 12, 34, 56, 987, 654, 321);
+ assert.sameValue(result.getCalendar(), customCalendar, "calendar result");
+ },
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..aff156fa39
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-builtin-calendar-no-array-iteration.js
@@ -0,0 +1,24 @@
+// |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.plaindatetime.prototype.withplaindate
+description: >
+ Calling the method with a property bag argument with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const arg = { year: 2000, month: 5, day: 2, calendar: "iso8601" };
+instance.withPlainDate(arg);
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-calendar-datefromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..f070f616c2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-calendar-datefromfields-called-with-null-prototype-fields.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.plaindatetime.prototype.withplaindate
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const arg = { year: 2000, month: 5, day: 2, calendar };
+instance.withPlainDate(arg);
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..7db65ba3b4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-constructor-in-calendar-fields.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.plaindatetime.prototype.withplaindate
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+assert.throws(RangeError, () => instance.withPlainDate(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-duplicate-calendar-fields.js
new file mode 100644
index 0000000000..094d3b1764
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-duplicate-calendar-fields.js
@@ -0,0 +1,19 @@
+// |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.plaindatetime.prototype.withplaindate
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['day'], ['month'], ['monthCode'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar };
+ const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+ assert.throws(RangeError, () => instance.withPlainDate(arg));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-leap-second.js
new file mode 100644
index 0000000000..cd45b2dbdf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-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.plaindatetime.prototype.withplaindate
+description: Leap second is a valid ISO string for PlainDate
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+let arg = "2016-12-31T23:59:60";
+const result1 = instance.withPlainDate(arg);
+TemporalHelpers.assertPlainDateTime(
+ result1,
+ 2016, 12, "M12", 31, 12, 34, 56, 987, 654, 321,
+ "leap second is a valid ISO string for PlainDate"
+);
+
+arg = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+const result2 = instance.withPlainDate(arg);
+TemporalHelpers.assertPlainDateTime(
+ result2,
+ 2016, 12, "M12", 31, 12, 34, 56, 987, 654, 321,
+ "second: 60 is ignored in property bag for PlainDate"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-number.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-number.js
new file mode 100644
index 0000000000..48bbc1cf50
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/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.plaindatetime.prototype.withplaindate
+description: A number cannot be used in place of a Temporal.PlainDate
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+const numbers = [
+ 1,
+ 19761118,
+ -19761118,
+ 1234567890,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => instance.withPlainDate(arg),
+ 'Numbers cannot be used in place of an ISO string for PlainDate'
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-object-insufficient-data.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-object-insufficient-data.js
new file mode 100644
index 0000000000..e7d7859da5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-object-insufficient-data.js
@@ -0,0 +1,38 @@
+// |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.plaindatetime.prototype.withplaindate
+description: Unrecognized properties of plain object ignored
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dt = new Temporal.PlainDateTime(1995, 12, 7, 3, 24, 30);
+
+assert.throws(
+ TypeError,
+ () => dt.withPlainDate({}),
+ "empty object not acceptable"
+);
+
+assert.throws(
+ TypeError,
+ () => dt.withPlainDate({ months: 12 }), // should be "month"
+ "no recognized properties (look like it might work)"
+);
+
+assert.throws(
+ TypeError,
+ () => dt.with({nonsense: true}),
+ "no recognized properties (clearly won't work)"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ dt.withPlainDate({ year: 2000, month: 6, day: 1, months: 123 }), // 'months' unrecognized; see above
+ 2000, 6, "M06", 1, 3, 24, 30, 0, 0, 0,
+ "unrecognized properties ignored & does not throw if recognized properties present)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-object.js
new file mode 100644
index 0000000000..7e7bbddf1c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-object.js
@@ -0,0 +1,20 @@
+// |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.plaindatetime.prototype.withplaindate
+description: Plain object may be acceptable
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dt = new Temporal.PlainDateTime(1995, 12, 7, 3, 24, 30);
+
+TemporalHelpers.assertPlainDateTime(
+ dt.withPlainDate({ year: 2000, month: 6, day: 1 }),
+ 2000, 6, "M06", 1, 3, 24, 30, 0, 0, 0,
+ "plain object works"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-plaindate-calendar-noniso.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-plaindate-calendar-noniso.js
new file mode 100644
index 0000000000..1255bf51c1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-plaindate-calendar-noniso.js
@@ -0,0 +1,56 @@
+// |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.plaindatetime.prototype.withplaindate
+description: PlainDate calendar is preserved with ISO PDT
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const cal = {
+ id: 'thisisnotiso',
+ era() { return undefined; },
+ eraYear() { return undefined; },
+ toString() { return "this is a string"; },
+ year() { return 2008; },
+ month() { return 9; },
+ monthCode() { return "M09"; },
+ day() { return 6; },
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+const pdt = new Temporal.PlainDateTime(1995, 12, 7, 3, 24, 30, 0, 0, 0);
+assert.sameValue(pdt.calendarId, "iso8601", "PlainDateTime with ISO calendar");
+const pd = new Temporal.PlainDate(2010, 11, 12, cal);
+const shifted = pdt.withPlainDate(pd);
+
+TemporalHelpers.assertPlainDateTime(
+ shifted,
+ 2008, 9, "M09", 6, 3, 24, 30, 0, 0, 0,
+ "calendar is changed if receiver has ISO calendar (1)"
+ // Testing of era and eraYear should only be coded under intl402
+);
+
+assert.sameValue(
+ shifted.getCalendar(),
+ cal,
+ "calendar is changed if receiver has ISO calendar (2)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-plaindate-calendar-same-id.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-plaindate-calendar-same-id.js
new file mode 100644
index 0000000000..3ef31c8d45
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-plaindate-calendar-same-id.js
@@ -0,0 +1,79 @@
+// |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.plaindatetime.prototype.withplaindate
+description: PlainDate calendar is preserved when both calendars have the same id
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const cal1 = {
+ id: "this is a string",
+ toString() { return "this is another string"; },
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+const cal2 = {
+ id: "this is a string",
+ era() { return undefined; },
+ eraYear() { return undefined; },
+ toString() { return "thisisnotiso"; },
+ year() { return 2008; },
+ month() { return 9; },
+ monthCode() { return "M09"; },
+ day() { return 6; },
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+const pdt = new Temporal.PlainDateTime(1995, 12, 7, 3, 24, 30, 0, 0, 0, cal1);
+const pd = new Temporal.PlainDate(2010, 11, 12, cal2);
+const shifted = pdt.withPlainDate(pd);
+
+TemporalHelpers.assertPlainDateTime(
+ shifted,
+ 2008, 9, "M09", 6, 3, 24, 30, 0, 0, 0,
+ "calendar is changed with same id (1)"
+ // Testing of era and eraYear should only be coded under intl402
+);
+
+assert.sameValue(
+ shifted.getCalendar(),
+ cal2,
+ "calendar is changed with same id (2)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-plaindate-calendar-same-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-plaindate-calendar-same-object.js
new file mode 100644
index 0000000000..7c36ca99e2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-plaindate-calendar-same-object.js
@@ -0,0 +1,60 @@
+// |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.plaindatetime.prototype.withplaindate
+description: PlainDate calendar is preserved when both calendars are the same object
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+let calls = 0;
+const cal = {
+ id: 'thisisnotiso',
+ era() { return undefined; },
+ eraYear() { return undefined; },
+ toString() {
+ ++calls;
+ return "this is a string";
+ },
+ year() { return 2008; },
+ month() { return 9; },
+ monthCode() { return "M09"; },
+ day() { return 6; },
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+const pdt = new Temporal.PlainDateTime(1995, 12, 7, 3, 24, 30, 0, 0, 0, cal);
+const pd = new Temporal.PlainDate(2010, 11, 12, cal);
+const shifted = pdt.withPlainDate(pd);
+
+TemporalHelpers.assertPlainDateTime(
+ shifted,
+ 2008, 9, "M09", 6, 3, 24, 30, 0, 0, 0,
+ "calendar is unchanged with same calendars (1)"
+ // Testing of era and eraYear should only be coded under intl402
+);
+
+assert.sameValue(
+ shifted.getCalendar(),
+ cal,
+ "calendar is unchanged with same calendars (2)"
+);
+assert.sameValue(calls, 0, "should not have called cal.toString()");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-plaindate-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-plaindate-calendar.js
new file mode 100644
index 0000000000..d85612f889
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-plaindate-calendar.js
@@ -0,0 +1,56 @@
+// |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.plaindatetime.prototype.withplaindate
+description: Original PDT calendar is preserved with ISO PlainDate
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const cal = {
+ id: 'thisisnotiso',
+ era() { return undefined; },
+ eraYear() { return undefined; },
+ toString() { return "this is a string"; },
+ year() { return 2008; },
+ month() { return 9; },
+ monthCode() { return "M09"; },
+ day() { return 6; },
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+const pdt = new Temporal.PlainDateTime(1995, 12, 7, 3, 24, 30, 0, 0, 0, cal);
+const pd = new Temporal.PlainDate(2010, 11, 12);
+assert.sameValue(pd.calendarId, "iso8601", "PlainDate with ISO calendar");
+const shifted = pdt.withPlainDate(pd);
+
+TemporalHelpers.assertPlainDateTime(
+ shifted,
+ 2008, 9, "M09", 6, 3, 24, 30, 0, 0, 0,
+ "calendar is unchanged if input has ISO calendar (1)"
+ // Testing of era and eraYear should only be coded under intl402
+);
+
+assert.sameValue(
+ shifted.getCalendar(),
+ cal,
+ "calendar is unchanged if input has ISO calendar (2)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-plaindate.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-plaindate.js
new file mode 100644
index 0000000000..fb713898c1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-plaindate.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.plaindatetime.prototype.withplaindate
+description: PlainDate object is acceptable
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dt = new Temporal.PlainDateTime(1995, 12, 7, 3, 24, 30);
+const date = new Temporal.PlainDate(2020, 1, 23);
+
+TemporalHelpers.assertPlainDateTime(
+ dt.withPlainDate(date),
+ 2020, 1, "M01", 23, 3, 24, 30, 0, 0, 0,
+ "PlainDate argument works"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-plaindatetime.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-plaindatetime.js
new file mode 100644
index 0000000000..377c7724a0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-plaindatetime.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.plaindatetime.withplaindate
+description: Fast path for converting Temporal.PlainDateTime to Temporal.PlainDate by reading internal slots
+info: |
+ sec-temporal.plaindatetime.prototype.withplaindate step 3:
+ 3. Let _plainDate_ be ? ToTemporalDate(_plainDateLike_).
+ sec-temporal-totemporaldate step 2.b:
+ b. If _item_ has an [[InitializedTemporalDateTime]] internal slot, then
+ i. Return ! CreateTemporalDate(_item_.[[ISOYear]], _item_.[[ISOMonth]], _item_.[[ISODay]], _item_.[[Calendar]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkPlainDateTimeConversionFastPath((datetime) => {
+ const receiver = new Temporal.PlainDateTime(2001, 9, 9, 6, 54, 32, 123, 456, 789);
+ const result = receiver.withPlainDate(datetime);
+ TemporalHelpers.assertPlainDateTime(result, 2000, 5, "M05", 2, 6, 54, 32, 123, 456, 789);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-propertybag-calendar-case-insensitive.js
new file mode 100644
index 0000000000..a5d1fd08e0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-propertybag-calendar-case-insensitive.js
@@ -0,0 +1,20 @@
+// |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.plaindatetime.prototype.withplaindate
+description: The calendar name is case-insensitive
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+const calendar = "IsO8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.withPlainDate(arg);
+TemporalHelpers.assertPlainDateTime(result, 1976, 11, "M11", 18, 12, 34, 56, 987, 654, 321, "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-propertybag-calendar-leap-second.js
new file mode 100644
index 0000000000..ac91345475
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-propertybag-calendar-leap-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.plaindatetime.prototype.withplaindate
+description: Leap second is a valid ISO string for a calendar in a property bag
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+const calendar = "2016-12-31T23:59:60";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.withPlainDate(arg);
+TemporalHelpers.assertPlainDateTime(
+ result,
+ 1976, 11, "M11", 18, 12, 34, 56, 987, 654, 321,
+ "leap second is a valid ISO string for calendar"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-propertybag-calendar-number.js
new file mode 100644
index 0000000000..cbd1950207
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-propertybag-calendar-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.plaindatetime.prototype.withplaindate
+description: A number as calendar in a property bag is not accepted
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(1976, 11, 18);
+
+const numbers = [
+ 1,
+ 19970327,
+ -19970327,
+ 1234567890,
+];
+for (const calendar of numbers) {
+ const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+ assert.throws(
+ TypeError,
+ () => instance.withPlainDate(arg),
+ "Numbers cannot be used as a calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-propertybag-calendar-string.js
new file mode 100644
index 0000000000..c8a2dcbcd9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-propertybag-calendar-string.js
@@ -0,0 +1,20 @@
+// |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.plaindatetime.prototype.withplaindate
+description: A calendar ID is valid input for Calendar
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+const calendar = "iso8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.withPlainDate(arg);
+TemporalHelpers.assertPlainDateTime(result, 1976, 11, "M11", 18, 12, 34, 56, 987, 654, 321, `Calendar created from string "${calendar}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-propertybag-calendar-wrong-type.js
new file mode 100644
index 0000000000..0baf544ed1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-propertybag-calendar-wrong-type.js
@@ -0,0 +1,46 @@
+// |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.plaindatetime.prototype.withplaindate
+description: >
+ Appropriate error thrown when a calendar property from a property bag cannot
+ be converted to a calendar object or string
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [calendar, description] of primitiveTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ typeof calendar === 'string' ? RangeError : TypeError,
+ () => instance.withPlainDate(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+ [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields()
+];
+
+for (const [calendar, description] of typeErrorTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(TypeError, () => instance.withPlainDate(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/PlainDateTime/prototype/withPlainDate/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-propertybag-calendar-year-zero.js
new file mode 100644
index 0000000000..d39551d616
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-propertybag-calendar-year-zero.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.plaindatetime.prototype.withplaindate
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T17:45",
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+01:00",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.withPlainDate(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-proto-in-calendar-fields.js
new file mode 100644
index 0000000000..8753275d77
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-proto-in-calendar-fields.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.plaindatetime.prototype.withplaindate
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+assert.throws(RangeError, () => instance.withPlainDate(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..e15d9e970d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-calendar-annotation.js
@@ -0,0 +1,34 @@
+// |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.plaindatetime.prototype.withplaindate
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2000-05-02[u-ca=iso8601]", "without time or time zone"],
+ ["2000-05-02[UTC][u-ca=iso8601]", "with time zone and no time"],
+ ["2000-05-02T15:23[u-ca=iso8601]", "without time zone"],
+ ["2000-05-02T15:23[UTC][u-ca=iso8601]", "with time zone"],
+ ["2000-05-02T15:23[!u-ca=iso8601]", "with ! and no time zone"],
+ ["2000-05-02T15:23[UTC][!u-ca=iso8601]", "with ! and time zone"],
+ ["2000-05-02T15:23[u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 15, 23);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.withPlainDate(arg);
+
+ TemporalHelpers.assertPlainDateTime(
+ result,
+ 2000, 5, "M05", 2, 15, 23, 0, 0, 0, 0,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..34d98195f3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-critical-unknown-annotation.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.plaindatetime.prototype.withplaindate
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[!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.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.withPlainDate(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..53e460dac6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-date-with-utc-offset.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.plaindatetime.prototype.withplaindate
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 15, 23);
+
+const validStrings = [
+ "2000-05-02T00+00:00",
+ "2000-05-02T00+00:00[UTC]",
+ "2000-05-02T00+00:00[!UTC]",
+ "2000-05-02T00-02:30[America/St_Johns]",
+];
+
+for (const arg of validStrings) {
+ const result = instance.withPlainDate(arg);
+
+ TemporalHelpers.assertPlainDateTime(
+ result,
+ 2000, 5, "M05", 2, 15, 23, 0, 0, 0, 0,
+ `"${arg}" is a valid UTC offset with time for PlainDate`
+ );
+}
+
+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.withPlainDate(arg),
+ `"${arg}" UTC offset without time is not valid for PlainDate`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-invalid.js
new file mode 100644
index 0000000000..d7d5f1c03c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-invalid.js
@@ -0,0 +1,64 @@
+// |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.plaindatetime.prototype.withplaindate
+description: >
+ RangeError thrown if an invalid ISO string (or syntactically valid ISO string
+ that is not supported) is used as a PlainDate
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ // invalid ISO strings:
+ "",
+ "invalid iso8601",
+ "2020-01-00",
+ "2020-01-32",
+ "2020-02-30",
+ "2021-02-29",
+ "2020-00-01",
+ "2020-13-01",
+ "2020-01-01T",
+ "2020-01-01T25:00:00",
+ "2020-01-01T01:60:00",
+ "2020-01-01T01:60:61",
+ "2020-01-01junk",
+ "2020-01-01T00:00:00junk",
+ "2020-01-01T00:00:00+00:00junk",
+ "2020-01-01T00:00:00+00:00[UTC]junk",
+ "2020-01-01T00:00:00+00:00[UTC][u-ca=iso8601]junk",
+ "02020-01-01",
+ "2020-001-01",
+ "2020-01-001",
+ "2020-01-01T001",
+ "2020-01-01T01:001",
+ "2020-01-01T01:01:001",
+ // valid, but forms not supported in Temporal:
+ "2020-W01-1",
+ "2020-001",
+ "+0002020-01-01",
+ // valid, but this calendar must not exist:
+ "2020-01-01[u-ca=notexist]",
+ // may be valid in other contexts, but insufficient information for PlainDate:
+ "2020-01",
+ "+002020-01",
+ "01-01",
+ "2020-W01",
+ "P1Y",
+ "-P12Y",
+ // valid, but outside the supported range:
+ "-999999-01-01",
+ "+999999-01-01",
+];
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => instance.withPlainDate(arg),
+ `"${arg}" should not be a valid ISO string for a PlainDate`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-iso-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-iso-calendar.js
new file mode 100644
index 0000000000..4f253733bc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-iso-calendar.js
@@ -0,0 +1,54 @@
+// |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.plaindatetime.prototype.withplaindate
+description: Original PDT calendar is preserved with ISO string
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const cal = {
+ id: "thisisnotiso",
+ era() { return undefined; },
+ eraYear() { return undefined; },
+ toString() { return "this is a string"; },
+ year() { return 2008; },
+ month() { return 9; },
+ monthCode() { return "M09"; },
+ day() { return 6; },
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+const dt = new Temporal.PlainDateTime(1995, 12, 7, 3, 24, 30, 0, 0, 0, cal);
+const shifted = dt.withPlainDate("2010-11-12");
+
+TemporalHelpers.assertPlainDateTime(
+ shifted,
+ 2008, 9, "M09", 6, 3, 24, 30, 0, 0, 0,
+ "calendar is unchanged if input has ISO calendar (1)"
+ // Testing of era and eraYear should only be coded under intl402
+);
+
+assert.sameValue(
+ shifted.getCalendar(),
+ cal,
+ "calendar is unchanged if input has ISO calendar (2)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..e864fc5bea
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/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.plaindatetime.prototype.withplaindate
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[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.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.withPlainDate(arg),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..28492d2479
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-multiple-time-zone.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.plaindatetime.prototype.withplaindate
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[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.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.withPlainDate(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-time-separators.js
new file mode 100644
index 0000000000..cdfb3c1f58
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-time-separators.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.plaindatetime.prototype.withplaindate
+description: Time separator in string argument can vary
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2000-05-02T15:23", "uppercase T"],
+ ["2000-05-02t15:23", "lowercase T"],
+ ["2000-05-02 15:23", "space between date and time"],
+];
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 15, 23);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.withPlainDate(arg);
+
+ TemporalHelpers.assertPlainDateTime(
+ result,
+ 2000, 5, "M05", 2, 15, 23, 0, 0, 0, 0,
+ `variant time separators (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..6a110024d9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-time-zone-annotation.js
@@ -0,0 +1,39 @@
+// |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.plaindatetime.prototype.withplaindate
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2000-05-02[Asia/Kolkata]", "named, with no time"],
+ ["2000-05-02[!Europe/Vienna]", "named, with ! and no time"],
+ ["2000-05-02[+00:00]", "numeric, with no time"],
+ ["2000-05-02[!-02:30]", "numeric, with ! and no time"],
+ ["2000-05-02T15:23[America/Sao_Paulo]", "named, with no offset"],
+ ["2000-05-02T15:23[!Asia/Tokyo]", "named, with ! and no offset"],
+ ["2000-05-02T15:23[-02:30]", "numeric, with no offset"],
+ ["2000-05-02T15:23[!+00:00]", "numeric, with ! and no offset"],
+ ["2000-05-02T15:23+00:00[America/New_York]", "named, with offset"],
+ ["2000-05-02T15:23+00:00[!UTC]", "named, with offset and !"],
+ ["2000-05-02T15:23+00:00[+01:00]", "numeric, with offset"],
+ ["2000-05-02T15:23+00:00[!-08:00]", "numeric, with offset and !"],
+];
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 15, 23);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.withPlainDate(arg);
+
+ TemporalHelpers.assertPlainDateTime(
+ result,
+ 2000, 5, "M05", 2, 15, 23, 0, 0, 0, 0,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..642a911fec
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-unknown-annotation.js
@@ -0,0 +1,33 @@
+// |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.plaindatetime.prototype.withplaindate
+description: Various forms of unknown annotation
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2000-05-02[foo=bar]", "without time"],
+ ["2000-05-02T15:23[foo=bar]", "alone"],
+ ["2000-05-02T15:23[UTC][foo=bar]", "with time zone"],
+ ["2000-05-02T15:23[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["2000-05-02T15:23[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["2000-05-02T15:23[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 15, 23);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.withPlainDate(arg);
+
+ TemporalHelpers.assertPlainDateTime(
+ result,
+ 2000, 5, "M05", 2, 15, 23, 0, 0, 0, 0,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..c0a04dcdde
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-with-utc-designator.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.plaindatetime.prototype.withplaindate
+description: RangeError thrown if a string with UTC designator is used as a PlainDate
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "2019-10-01T09:00:00Z",
+ "2019-10-01T09:00:00Z[UTC]",
+];
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.withPlainDate(arg),
+ "String with UTC designator should not be valid as a PlainDate"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string.js
new file mode 100644
index 0000000000..bea0f65186
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-string.js
@@ -0,0 +1,20 @@
+// |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.plaindatetime.prototype.withplaindate
+description: PlainDate-like string argument is acceptable
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const dt = new Temporal.PlainDateTime(1995, 12, 7, 3, 24, 30);
+
+TemporalHelpers.assertPlainDateTime(
+ dt.withPlainDate("2018-09-15"),
+ 2018, 9, "M09", 15, 3, 24, 30, 0, 0, 0,
+ "PlainDate-like string argument works"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-wrong-type.js
new file mode 100644
index 0000000000..06f30c6762
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/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.plaindatetime.prototype.withplaindate
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainDate
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 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.withPlainDate(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.PlainDate, "Temporal.PlainDate, object"],
+ [Temporal.PlainDate.prototype, "Temporal.PlainDate.prototype, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.withPlainDate(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/PlainDateTime/prototype/withPlainDate/argument-zoneddatetime-convert.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-zoneddatetime-convert.js
new file mode 100644
index 0000000000..233705b50c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-zoneddatetime-convert.js
@@ -0,0 +1,22 @@
+// |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.plaindatetime.prototype.withplaindate
+description: An exception from TimeZone#getOffsetNanosecondsFor() is propagated.
+features: [Temporal]
+---*/
+
+class TZ extends Temporal.TimeZone {
+ constructor() { super("UTC") }
+ getOffsetNanosecondsFor() { throw new Test262Error() }
+}
+
+const tz = new TZ();
+const arg = new Temporal.ZonedDateTime(0n, tz);
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+assert.throws(Test262Error, () => instance.withPlainDate(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-zoneddatetime-slots.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-zoneddatetime-slots.js
new file mode 100644
index 0000000000..cab1b1db13
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-zoneddatetime-slots.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.plaindatetime.prototype.withplaindate
+description: Getters are not called when converting a ZonedDateTime to a PlainDate.
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.ZonedDateTime.prototype);
+const getters = ["year", "month", "monthCode", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond", "calendar"];
+
+for (const property of getters) {
+ Object.defineProperty(Temporal.ZonedDateTime.prototype, property, {
+ get() {
+ actual.push(`get ${property}`);
+ const value = prototypeDescrs[property].get.call(this);
+ return {
+ toString() {
+ actual.push(`toString ${property}`);
+ return value.toString();
+ },
+ valueOf() {
+ actual.push(`valueOf ${property}`);
+ return value;
+ },
+ };
+ },
+ });
+}
+
+const arg = new Temporal.ZonedDateTime(0n, "UTC");
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+instance.withPlainDate(arg);
+assert.compareArray(actual, []);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..6eeedcd1b0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/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.plaindatetime.prototype.withplaindate
+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 plain = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ const zoned = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => plain.withPlainDate(zoned));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..63fe5656ec
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/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.plaindatetime.prototype.withplaindate
+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 plain = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ const zoned = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => plain.withPlainDate(zoned),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..d18bb10db3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/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.plaindatetime.prototype.withplaindate
+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 plain = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ const zoned = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => plain.withPlainDate(zoned));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..191d49e8e4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/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.plaindatetime.prototype.withplaindate
+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 plain = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ const zoned = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => plain.withPlainDate(zoned));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/branding.js
new file mode 100644
index 0000000000..754f29a7a8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/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.plaindatetime.prototype.withplaindate
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const withPlainDate = Temporal.PlainDateTime.prototype.withPlainDate;
+
+assert.sameValue(typeof withPlainDate, "function");
+
+const args = [new Temporal.PlainDate(2022, 6, 22)];
+
+assert.throws(TypeError, () => withPlainDate.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => withPlainDate.apply(null, args), "null");
+assert.throws(TypeError, () => withPlainDate.apply(true, args), "true");
+assert.throws(TypeError, () => withPlainDate.apply("", args), "empty string");
+assert.throws(TypeError, () => withPlainDate.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => withPlainDate.apply(1, args), "1");
+assert.throws(TypeError, () => withPlainDate.apply({}, args), "plain object");
+assert.throws(TypeError, () => withPlainDate.apply(Temporal.PlainDateTime, args), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => withPlainDate.apply(Temporal.PlainDateTime.prototype, args), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..5b78381f1c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plaindatetime.prototype.withplaindate
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const idOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "id");
+Object.defineProperty(Temporal.Calendar.prototype, "id", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("id should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, "iso8601");
+instance.withPlainDate(new Temporal.PlainDate(2001, 6, 13));
+
+Object.defineProperty(Temporal.Calendar.prototype, "id", idOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/builtin.js
new file mode 100644
index 0000000000..260cb95caa
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/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.plaindatetime.prototype.withplaindate
+description: >
+ Tests that Temporal.PlainDateTime.prototype.withPlainDate
+ 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.PlainDateTime.prototype.withPlainDate),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.prototype.withPlainDate),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.prototype.withPlainDate),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.prototype.withPlainDate.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/calendar-datefromfields-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/calendar-datefromfields-called-with-options-undefined.js
new file mode 100644
index 0000000000..34b0955ac4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/calendar-datefromfields-called-with-options-undefined.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.plaindatetime.prototype.withplaindate
+description: >
+ Calendar.dateFromFields method is called with undefined as the options value
+ when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+instance.withPlainDate({ year: 2000, month: 5, day: 3, calendar });
+assert.sameValue(calendar.dateFromFieldsCallCount, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/calendar-fields-iterable.js
new file mode 100644
index 0000000000..a53cf9d14c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/calendar-fields-iterable.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.plaindatetime.prototype.withplaindate
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plaindatetime.prototype.withplaindate step 3:
+ 3. Let _plainDate_ be ? ToTemporalDate(_plainDateLike_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const datetime = new Temporal.PlainDateTime(2000, 5, 3, 13, 3, 27, 123, 456, 789, calendar1);
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+datetime.withPlainDate({ year: 2001, month: 6, day: 4, calendar: calendar2 });
+
+assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/calendar-temporal-object.js
new file mode 100644
index 0000000000..ecc32638c5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/calendar-temporal-object.js
@@ -0,0 +1,31 @@
+// |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.plaindatetime.prototype.withplaindate
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plaindatetime.prototype.withplaindate step 3:
+ 3. Let _plainDate_ be ? ToTemporalDate(_plainDateLike_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject, calendar) => {
+ const datetime = new Temporal.PlainDateTime(2000, 5, 3, 13, 3, 27, 123, 456, 789);
+ // the PlainDate's calendar will override the PlainDateTime's ISO calendar
+ const result = datetime.withPlainDate({ year: 2001, month: 6, day: 4, calendar: temporalObject });
+ assert.sameValue(result.getCalendar(), calendar, "Temporal object coerced to calendar");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..934b4207c9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/infinity-throws-rangeerror.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.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plaindatetime.prototype.withplaindate
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 15);
+const base = { year: 2000, month: 5, day: 2 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.withPlainDate({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.withPlainDate({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/length.js
new file mode 100644
index 0000000000..72a684301c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/length.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.plaindatetime.prototype.withplaindate
+description: Temporal.PlainDateTime.prototype.withPlainDate.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.PlainDateTime.prototype.withPlainDate, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/name.js
new file mode 100644
index 0000000000..4e528f6cd5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/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.plaindatetime.prototype.withplaindate
+description: Temporal.PlainDateTime.prototype.withPlainDate.name is "withPlainDate".
+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.PlainDateTime.prototype.withPlainDate, "name", {
+ value: "withPlainDate",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/non-compatible-calendars-throw.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/non-compatible-calendars-throw.js
new file mode 100644
index 0000000000..3b7f15afb4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/non-compatible-calendars-throw.js
@@ -0,0 +1,56 @@
+// |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.plaindatetime.withplaindate
+description: If two non-ISO calendars are involved, an error is raised
+features: [Temporal]
+---*/
+
+const calendarMethods = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+
+const cal = {
+ id: "foo",
+ toString() { return "this is a string"; },
+ ...calendarMethods,
+};
+
+const dt = new Temporal.PlainDateTime(1995, 12, 7, 3, 24, 30, 0, 0, 0, cal);
+
+const anotherCal = {
+ id: "bar",
+ toString() { return "this is another string"; },
+ ...calendarMethods,
+};
+
+const date = new Temporal.PlainDate(2008, 9, 6, anotherCal);
+
+assert.throws(
+ RangeError,
+ () => dt.withPlainDate(date),
+ "throws if both `this` and `other` have a non-ISO calendar"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/not-a-constructor.js
new file mode 100644
index 0000000000..35bb31258b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/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.plaindatetime.prototype.withplaindate
+description: >
+ Temporal.PlainDateTime.prototype.withPlainDate 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.PlainDateTime.prototype.withPlainDate();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.prototype.withPlainDate), false,
+ "isConstructor(Temporal.PlainDateTime.prototype.withPlainDate)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/prop-desc.js
new file mode 100644
index 0000000000..4da47c13b6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/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.plaindatetime.prototype.withplaindate
+description: The "withPlainDate" property of Temporal.PlainDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.prototype.withPlainDate,
+ "function",
+ "`typeof PlainDateTime.prototype.withPlainDate` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime.prototype, "withPlainDate", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/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/PlainDateTime/prototype/withPlainDate/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/subclassing-ignored.js
new file mode 100644
index 0000000000..686f5b7e44
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/subclassing-ignored.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.plaindatetime.prototype.withplaindate
+description: Objects of a subclass are never created as return values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.PlainDateTime,
+ [2000, 5, 2, 12, 34, 56, 987, 654, 321],
+ "withPlainDate",
+ ["1999-04-27"],
+ (result) => TemporalHelpers.assertPlainDateTime(result, 1999, 4, "M04", 27, 12, 34, 56, 987, 654, 321),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/year-zero.js
new file mode 100644
index 0000000000..7b8342376b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/year-zero.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.plaindatetime.prototype.withplaindate
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T00:45",
+ "-000000-10-31T00:45+01:00",
+ "-000000-10-31T00:45+00:00[UTC]",
+];
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.withPlainDate(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-number.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-number.js
new file mode 100644
index 0000000000..c57bc4aa1f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/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.plaindatetime.prototype.withplaintime
+description: A number is invalid in place of an ISO string for Temporal.PlainTime
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2);
+
+const numbers = [
+ 1,
+ -123456.987654321,
+ 1234567,
+ 123456.9876543219,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => instance.withPlainTime(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/PlainDateTime/prototype/withPlainTime/argument-object-insufficient-data.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-object-insufficient-data.js
new file mode 100644
index 0000000000..a0532e47d4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-object-insufficient-data.js
@@ -0,0 +1,38 @@
+// |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.plaindatetime.prototype.withplaintime
+description: A plain object can be used as an argument
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const dt = new Temporal.PlainDateTime(2015, 12, 7, 3, 24, 30, 0, 3, 500);
+
+assert.throws(
+ TypeError,
+ () => dt.withPlainTime({}),
+ "empty object not an acceptable argument"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ dt.withPlainTime({ hour: 10 }),
+ 2015, 12, "M12", 7, 10, 0, 0, 0, 0, 0,
+ "plain object (hour) works"
+);
+
+assert.throws(
+ TypeError,
+ () => dt.withPlainTime({ hours: 9 }), // should be "hour", see above
+ "plain object with a single unrecognized property fails"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ dt.withPlainTime({ hour: 10, seconds: 123 }),
+ 2015, 12, "M12", 7, 10, 0, 0, 0, 0, 0,
+ "unrecognized properties are ignored if at least one recognized property is present"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..5c4d6b028f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/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.plaindatetime.prototype.withplaintime
+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.PlainDateTime(1976, 11, 18, 15, 23);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.withPlainTime(arg);
+
+ TemporalHelpers.assertPlainDateTime(
+ result,
+ 1976, 11, "M11", 18, 12, 34, 56, 987, 654, 321,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..f0535a69b0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/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.plaindatetime.prototype.withplaintime
+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.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.withPlainTime(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..eb14fdd13b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/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.plaindatetime.prototype.withplaintime
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const instance = new Temporal.PlainDateTime(1976, 11, 18, 15, 23);
+
+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.withPlainTime(arg);
+
+ TemporalHelpers.assertPlainDateTime(
+ result,
+ 1976, 11, "M11", 18, 12, 34, 56, 987, 654, 321,
+ `"${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.withPlainTime(arg),
+ `"${arg}" UTC offset without time is not valid for PlainTime`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..c1c58dd36c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/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.plaindatetime.prototype.withplaintime
+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.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.withPlainTime(arg),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..b50a240a48
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/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.plaindatetime.prototype.withplaintime
+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.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.withPlainTime(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-no-implicit-midnight.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-no-implicit-midnight.js
new file mode 100644
index 0000000000..288d13416c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/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.plaindatetime.prototype.withplaintime
+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.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+assert.throws(
+ RangeError,
+ () => instance.withPlainTime(arg),
+ "Date-only string throws, does not implicitly convert to midnight"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-time-designator-required-for-disambiguation.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-time-designator-required-for-disambiguation.js
new file mode 100644
index 0000000000..2fbe0e86ff
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/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.plaindatetime.prototype.withplaintime
+description: ISO 8601 time designator "T" required in cases of ambiguity
+includes: [temporalHelpers.js]
+features: [Temporal, arrow-function]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+TemporalHelpers.ISO.plainTimeStringsAmbiguous().forEach((string) => {
+ let arg = string;
+ assert.throws(
+ RangeError,
+ () => instance.withPlainTime(arg),
+ `'${arg}' is ambiguous and requires T prefix`
+ );
+ // The same string with a T prefix should not throw:
+ arg = `T${string}`;
+ instance.withPlainTime(arg);
+
+ arg = ` ${string}`;
+ assert.throws(
+ RangeError,
+ () => instance.withPlainTime(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.withPlainTime(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-time-separators.js
new file mode 100644
index 0000000000..b7ff3060b6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/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.plaindatetime.prototype.withplaintime
+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.PlainDateTime(1976, 11, 18, 15, 23);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.withPlainTime(arg);
+
+ TemporalHelpers.assertPlainDateTime(
+ result,
+ 1976, 11, "M11", 18, 12, 34, 56, 987, 654, 321,
+ `variant time separators (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..ac21c47797
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/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.plaindatetime.prototype.withplaintime
+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.PlainDateTime(1976, 11, 18, 15, 23);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.withPlainTime(arg);
+
+ TemporalHelpers.assertPlainDateTime(
+ result,
+ 1976, 11, "M11", 18, 12, 34, 56, 987, 654, 321,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..837d16ed8e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/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.plaindatetime.prototype.withplaintime
+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.PlainDateTime(1976, 11, 18, 15, 23);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.withPlainTime(arg);
+
+ TemporalHelpers.assertPlainDateTime(
+ result,
+ 1976, 11, "M11", 18, 12, 34, 56, 987, 654, 321,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-with-time-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-with-time-designator.js
new file mode 100644
index 0000000000..53f6f8533c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/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.plaindatetime.prototype.withplaintime
+description: ISO 8601 time designator "T" allowed at the start of PlainTime strings
+includes: [temporalHelpers.js]
+features: [Temporal, arrow-function]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 1, 1, 12, 30, 45, 123, 456, 789);
+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.withPlainTime(arg);
+ TemporalHelpers.assertPlainDateTime(result, 2000, 1, "M01", 1, 0, 30, 0, 0, 0, 0, `T prefix is accepted: ${arg}`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..60ed3676fa
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/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.plaindatetime.prototype.withplaintime
+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.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.withPlainTime(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/PlainDateTime/prototype/withPlainTime/argument-string-without-time-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-without-time-designator.js
new file mode 100644
index 0000000000..2d0af7c731
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-without-time-designator.js
@@ -0,0 +1,20 @@
+// |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.plaindatetime.prototype.withplaintime
+description: String argument without ISO 8601 time designator "T" allowed
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const dt = new Temporal.PlainDateTime(2015, 12, 7, 3, 24, 30, 0, 3, 500);
+
+TemporalHelpers.assertPlainDateTime(
+ dt.withPlainTime("12:34"),
+ 2015, 12, "M12", 7, 12, 34, 0, 0, 0, 0,
+ "time-like string works"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-time.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-time.js
new file mode 100644
index 0000000000..51ede37ea3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-time.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.plaindatetime.prototype.withplaintime
+description: An instance of PlainTime can be used as an argument
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const dt = new Temporal.PlainDateTime(2015, 12, 7, 3, 24, 30, 0, 3, 500);
+const hour = 11;
+const minute = 22;
+const time = new Temporal.PlainTime(hour, minute);
+
+TemporalHelpers.assertPlainDateTime(
+ dt.withPlainTime(time),
+ 2015,
+ 12,
+ "M12",
+ 7,
+ hour,
+ minute,
+ 0,
+ 0,
+ 0,
+ 0,
+ "PlainTime argument works"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-wrong-type.js
new file mode 100644
index 0000000000..3ce1146118
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-wrong-type.js
@@ -0,0 +1,42 @@
+// |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.plaindatetime.prototype.withplaintime
+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.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+const primitiveTests = [
+ [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.withPlainTime(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.withPlainTime(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/PlainDateTime/prototype/withPlainTime/argument-zoneddatetime-balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-zoneddatetime-balance-negative-time-units.js
new file mode 100644
index 0000000000..3fb5f18f0a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-zoneddatetime-balance-negative-time-units.js
@@ -0,0 +1,47 @@
+// |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.plaindatetime.prototype.withplaintime
+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.plaindatetime.prototype.withplaintime step 4:
+ 4. Let _plainTime_ be ? ToTemporalTime(_plainTimeLike_).
+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 pdt = new Temporal.PlainDateTime(2000, 5, 2);
+const newpdt = pdt.withPlainTime(datetime);
+
+TemporalHelpers.assertPlainDateTime(newpdt, 2000, 5, "M05", 2, 1, 1, 1, 1, 0, 999);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-zoneddatetime-negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-zoneddatetime-negative-epochnanoseconds.js
new file mode 100644
index 0000000000..90134da1be
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/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.plaindatetime.prototype.withplaintime
+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.PlainDateTime(2000, 5, 2, 15, 30, 45, 987, 654, 321);
+const result = instance.withPlainTime(datetime);
+TemporalHelpers.assertPlainDateTime(result, 2000, 5, "M05", 2, 16, 50, 35, 0, 0, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..e96dca9640
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/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.plaindatetime.prototype.withplaintime
+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 plain = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ const zoned = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => plain.withPlainTime(zoned));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..e916235f4c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/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.plaindatetime.prototype.withplaintime
+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 plain = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ const zoned = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => plain.withPlainTime(zoned),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..60d258eda4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/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.plaindatetime.prototype.withplaintime
+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 plain = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ const zoned = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => plain.withPlainTime(zoned));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..54d4aabd9b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/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.plaindatetime.prototype.withplaintime
+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 plain = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ const zoned = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => plain.withPlainTime(zoned));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/branding.js
new file mode 100644
index 0000000000..cae7cd48bf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/branding.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.plaindatetime.prototype.withplaintime
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const withPlainTime = Temporal.PlainDateTime.prototype.withPlainTime;
+
+assert.sameValue(typeof withPlainTime, "function");
+
+assert.throws(TypeError, () => withPlainTime.call(undefined), "undefined");
+assert.throws(TypeError, () => withPlainTime.call(null), "null");
+assert.throws(TypeError, () => withPlainTime.call(true), "true");
+assert.throws(TypeError, () => withPlainTime.call(""), "empty string");
+assert.throws(TypeError, () => withPlainTime.call(Symbol()), "symbol");
+assert.throws(TypeError, () => withPlainTime.call(1), "1");
+assert.throws(TypeError, () => withPlainTime.call({}), "plain object");
+assert.throws(TypeError, () => withPlainTime.call(Temporal.PlainDateTime), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => withPlainTime.call(Temporal.PlainDateTime.prototype), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/builtin.js
new file mode 100644
index 0000000000..3b4d77779a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/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.plaindatetime.prototype.withplaintime
+description: >
+ Tests that Temporal.PlainDateTime.prototype.withPlainTime
+ 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.PlainDateTime.prototype.withPlainTime),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.prototype.withPlainTime),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.prototype.withPlainTime),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.prototype.withPlainTime.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/leap-second.js
new file mode 100644
index 0000000000..27bab2e006
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/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.plaindatetime.prototype.withplaintime
+description: Leap second is a valid ISO string for PlainTime
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+let arg = "2016-12-31T23:59:60";
+const result1 = instance.withPlainTime(arg);
+TemporalHelpers.assertPlainDateTime(
+ result1,
+ 2000, 5, "M05", 2, 23, 59, 59, 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.withPlainTime(arg);
+TemporalHelpers.assertPlainDateTime(
+ result2,
+ 2000, 5, "M05", 2, 23, 59, 59, 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/PlainDateTime/prototype/withPlainTime/length.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/length.js
new file mode 100644
index 0000000000..d090f2385c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/length.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.plaindatetime.prototype.withplaintime
+description: Temporal.PlainDateTime.prototype.withPlainTime.length is 0
+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.PlainDateTime.prototype.withPlainTime, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/name.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/name.js
new file mode 100644
index 0000000000..ed1b0a27c4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/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.plaindatetime.prototype.withplaintime
+description: Temporal.PlainDateTime.prototype.withPlainTime.name is "withPlainTime".
+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.PlainDateTime.prototype.withPlainTime, "name", {
+ value: "withPlainTime",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/no-argument-default-to-midnight.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/no-argument-default-to-midnight.js
new file mode 100644
index 0000000000..a291dc154a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/no-argument-default-to-midnight.js
@@ -0,0 +1,20 @@
+// |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.plaindatetime.prototype.withplaintime
+description: If no argument is given, default to midnight
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dt = new Temporal.PlainDateTime(2015, 12, 7, 3, 24, 30, 0, 3, 500);
+
+TemporalHelpers.assertPlainDateTime(
+ dt.withPlainTime(),
+ 2015, 12, "M12", 7, 0, 0, 0, 0, 0, 0,
+ "no argument defaults to midnight"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/not-a-constructor.js
new file mode 100644
index 0000000000..f0568aef06
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/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.plaindatetime.prototype.withplaintime
+description: >
+ Temporal.PlainDateTime.prototype.withPlainTime 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.PlainDateTime.prototype.withPlainTime();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.prototype.withPlainTime), false,
+ "isConstructor(Temporal.PlainDateTime.prototype.withPlainTime)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/plaintime-propertybag-no-time-units.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/plaintime-propertybag-no-time-units.js
new file mode 100644
index 0000000000..a074797a02
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/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.plaindatetime.prototype.withplaintime
+description: Missing time units in property bag default to 0
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 1, 1, 12, 30, 45, 123, 456, 789);
+
+const props = {};
+assert.throws(TypeError, () => instance.withPlainTime(props), "TypeError if no properties are present");
+
+props.minute = 30;
+const result = instance.withPlainTime(props);
+TemporalHelpers.assertPlainDateTime(result, 2000, 1, "M01", 1, 0, 30, 0, 0, 0, 0, "missing time units default to 0");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/prop-desc.js
new file mode 100644
index 0000000000..f874bcd013
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/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.plaindatetime.prototype.withplaintime
+description: The "withPlainTime" property of Temporal.PlainDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.prototype.withPlainTime,
+ "function",
+ "`typeof PlainDateTime.prototype.withPlainTime` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime.prototype, "withPlainTime", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/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/PlainDateTime/prototype/withPlainTime/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/subclassing-ignored.js
new file mode 100644
index 0000000000..a28b2cfc9e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/subclassing-ignored.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.plaindatetime.prototype.withplaintime
+description: Objects of a subclass are never created as return values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.PlainDateTime,
+ [2000, 5, 2, 12, 34, 56, 987, 654, 321],
+ "withPlainTime",
+ ["05:43:21.123456789"],
+ (result) => TemporalHelpers.assertPlainDateTime(result, 2000, 5, "M05", 2, 5, 43, 21, 123, 456, 789),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/time-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/time-undefined.js
new file mode 100644
index 0000000000..929ea523ea
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/time-undefined.js
@@ -0,0 +1,29 @@
+// |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.plaindatetime.prototype.withplaintime
+description: The time is assumed to be midnight if not given
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+const explicit = datetime.withPlainTime(undefined);
+assert.sameValue(explicit.hour, 0, "default time is midnight");
+assert.sameValue(explicit.minute, 0, "default time is midnight");
+assert.sameValue(explicit.second, 0, "default time is midnight");
+assert.sameValue(explicit.millisecond, 0, "default time is midnight");
+assert.sameValue(explicit.microsecond, 0, "default time is midnight");
+assert.sameValue(explicit.nanosecond, 0, "default time is midnight");
+
+const implicit = datetime.withPlainTime();
+assert.sameValue(implicit.hour, 0, "default time is midnight");
+assert.sameValue(implicit.minute, 0, "default time is midnight");
+assert.sameValue(implicit.second, 0, "default time is midnight");
+assert.sameValue(implicit.millisecond, 0, "default time is midnight");
+assert.sameValue(implicit.microsecond, 0, "default time is midnight");
+assert.sameValue(implicit.nanosecond, 0, "default time is midnight");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/year-zero.js
new file mode 100644
index 0000000000..6be051a45f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/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.plaindatetime.prototype.withplaintime
+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.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.withPlainTime(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/year/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/year/branding.js
new file mode 100644
index 0000000000..71d9222d6d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/year/branding.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-get-temporal.plaindatetime.prototype.year
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const year = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "year").get;
+
+assert.sameValue(typeof year, "function");
+
+assert.throws(TypeError, () => year.call(undefined), "undefined");
+assert.throws(TypeError, () => year.call(null), "null");
+assert.throws(TypeError, () => year.call(true), "true");
+assert.throws(TypeError, () => year.call(""), "empty string");
+assert.throws(TypeError, () => year.call(Symbol()), "symbol");
+assert.throws(TypeError, () => year.call(1), "1");
+assert.throws(TypeError, () => year.call({}), "plain object");
+assert.throws(TypeError, () => year.call(Temporal.PlainDateTime), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => year.call(Temporal.PlainDateTime.prototype), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/year/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/year/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/year/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/year/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/year/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..8fc12c64f9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/year/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plaindatetime.prototype.year
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const yearOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "year");
+Object.defineProperty(Temporal.Calendar.prototype, "year", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("year should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, "iso8601");
+instance.year;
+
+Object.defineProperty(Temporal.Calendar.prototype, "year", yearOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/year/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/year/custom.js
new file mode 100644
index 0000000000..f2dfc106bc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/year/custom.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-get-temporal.plaindatetime.prototype.year
+description: Custom calendar tests for year().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ year(...args) {
+ ++calls;
+ assert.compareArray(args, [pdt], "year arguments");
+ return 7;
+ }
+}
+
+const calendar = new CustomCalendar();
+const pdt = new Temporal.PlainDateTime(1830, 8, 25, 20, 0, 0, 0, 0, 0, calendar);
+const result = pdt.year;
+assert.sameValue(result, 7, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/year/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/year/prop-desc.js
new file mode 100644
index 0000000000..79ae1af772
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/year/prop-desc.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-get-temporal.plaindatetime.prototype.year
+description: The "year" property of Temporal.PlainDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "year");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/year/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/year/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/year/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/year/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/year/validate-calendar-value.js
new file mode 100644
index 0000000000..1d35fb8597
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/year/validate-calendar-value.js
@@ -0,0 +1,54 @@
+// |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-get-temporal.plaindatetime.prototype.year
+description: Validate result returned from calendar year() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [Infinity, RangeError],
+ [-Infinity, RangeError],
+ [Symbol("foo"), TypeError],
+ [7n, TypeError],
+ [NaN, RangeError],
+ ["string", TypeError],
+ [{}, TypeError],
+ [null, TypeError],
+ [true, TypeError],
+ [false, TypeError],
+ [7.1, RangeError],
+ [-0.1, RangeError],
+ ["7", TypeError],
+ ["7.5", TypeError],
+ [{valueOf() { return 7; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ year() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainDateTime(1981, 12, 15, 14, 15, 45, 987, 654, 321, calendar);
+ assert.throws(error, () => instance.year, `${typeof result} ${String(result)} not converted to integer`);
+});
+
+const preservedResults = [
+ -7,
+];
+
+preservedResults.forEach(result => {
+ const calendar = new class extends Temporal.Calendar {
+ year() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainDateTime(1981, 12, 15, 14, 15, 45, 987, 654, 321, calendar);
+ assert.sameValue(instance.year, result, `${typeof result} ${String(result)} preserved`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/yearOfWeek/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/yearOfWeek/basic.js
new file mode 100644
index 0000000000..7f11b118e0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/yearOfWeek/basic.js
@@ -0,0 +1,15 @@
+// |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-get-temporal.plaindatetime.prototype.yearofweek
+description: Checking yearOfWeek for a "normal" case (non-undefined, non-boundary case, etc.)
+features: [Temporal]
+---*/
+
+const calendar = Temporal.Calendar.from("iso8601");
+const datetime = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789, calendar);
+assert.sameValue(datetime.yearOfWeek, 1976, "check yearOfWeek information");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/yearOfWeek/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/yearOfWeek/branding.js
new file mode 100644
index 0000000000..01584442eb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/yearOfWeek/branding.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-get-temporal.plaindatetime.prototype.yearofweek
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const yearOfWeek = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "yearOfWeek").get;
+
+assert.sameValue(typeof yearOfWeek, "function");
+
+assert.throws(TypeError, () => yearOfWeek.call(undefined), "undefined");
+assert.throws(TypeError, () => yearOfWeek.call(null), "null");
+assert.throws(TypeError, () => yearOfWeek.call(true), "true");
+assert.throws(TypeError, () => yearOfWeek.call(""), "empty string");
+assert.throws(TypeError, () => yearOfWeek.call(Symbol()), "symbol");
+assert.throws(TypeError, () => yearOfWeek.call(1), "1");
+assert.throws(TypeError, () => yearOfWeek.call({}), "plain object");
+assert.throws(TypeError, () => yearOfWeek.call(Temporal.PlainDateTime), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => yearOfWeek.call(Temporal.PlainDateTime.prototype), "Temporal.PlainDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/yearOfWeek/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/yearOfWeek/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/yearOfWeek/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/yearOfWeek/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/yearOfWeek/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..ac90671ed2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/yearOfWeek/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plaindatetime.prototype.yearofweek
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const yearOfWeekOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "yearOfWeek");
+Object.defineProperty(Temporal.Calendar.prototype, "yearOfWeek", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("yearOfWeek should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, "iso8601");
+instance.yearOfWeek;
+
+Object.defineProperty(Temporal.Calendar.prototype, "yearOfWeek", yearOfWeekOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/yearOfWeek/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/yearOfWeek/custom.js
new file mode 100644
index 0000000000..2bd3306412
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/yearOfWeek/custom.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-get-temporal.plaindatetime.prototype.yearofweek
+description: Custom calendar tests for yearOfWeek().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ yearOfWeek(...args) {
+ ++calls;
+ assert.compareArray(args, [pdt], "yearOfWeek arguments");
+ return 7;
+ }
+}
+
+const calendar = new CustomCalendar();
+const pdt = new Temporal.PlainDateTime(1830, 8, 25, 20, 0, 0, 0, 0, 0, calendar);
+const result = pdt.yearOfWeek;
+assert.sameValue(result, 7, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/yearOfWeek/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/yearOfWeek/prop-desc.js
new file mode 100644
index 0000000000..43b57c137a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/yearOfWeek/prop-desc.js
@@ -0,0 +1,17 @@
+// |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-get-temporal.plaindatetime.prototype.yearofweek
+description: The "yearOfWeek" property of Temporal.PlainDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "yearOfWeek");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/yearOfWeek/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/yearOfWeek/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/yearOfWeek/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/yearOfWeek/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/yearOfWeek/validate-calendar-value.js
new file mode 100644
index 0000000000..cd43fd4474
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/yearOfWeek/validate-calendar-value.js
@@ -0,0 +1,54 @@
+// |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-get-temporal.plaindatetime.prototype.yearofweek
+description: Validate result returned from calendar yearOfWeek() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [Infinity, RangeError],
+ [-Infinity, RangeError],
+ [Symbol("foo"), TypeError],
+ [7n, TypeError],
+ [NaN, RangeError],
+ ["string", TypeError],
+ [{}, TypeError],
+ [null, TypeError],
+ [true, TypeError],
+ [false, TypeError],
+ [7.1, RangeError],
+ [-0.1, RangeError],
+ ["7", TypeError],
+ ["7.5", TypeError],
+ [{valueOf() { return 7; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ yearOfWeek() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainDateTime(1981, 12, 15, 14, 15, 45, 987, 654, 321, calendar);
+ assert.throws(error, () => instance.yearOfWeek, `${typeof result} ${String(result)} not converted to integer`);
+});
+
+const preservedResults = [
+ -7,
+];
+
+preservedResults.forEach(result => {
+ const calendar = new class extends Temporal.Calendar {
+ yearOfWeek() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainDateTime(1981, 12, 15, 14, 15, 45, 987, 654, 321, calendar);
+ assert.sameValue(instance.yearOfWeek, result, `${typeof result} ${String(result)} preserved`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/second-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/second-undefined.js
new file mode 100644
index 0000000000..6a827ccf6c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/second-undefined.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.plaindatetime
+description: Second argument defaults to 0 if not given
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const args = [2000, 5, 2, 12, 34];
+
+TemporalHelpers.assertPlainDateTime(
+ new Temporal.PlainDateTime(...args, undefined),
+ 2000, 5, "M05", 2, 12, 34, 0, 0, 0, 0,
+ "second default argument (argument present)"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ new Temporal.PlainDateTime(...args),
+ 2000, 5, "M05", 2, 12, 34, 0, 0, 0, 0,
+ "second default argument (argument missing)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/subclass.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/subclass.js
new file mode 100644
index 0000000000..d0d1991b31
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/subclass.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.plaindatetime
+description: Test for Temporal.PlainDateTime subclassing.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+class CustomPlainDateTime extends Temporal.PlainDateTime {
+}
+
+const instance = new CustomPlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+TemporalHelpers.assertPlainDateTime(instance, 2000, 5, "M05", 2, 12, 34, 56, 987, 654, 321);
+assert.sameValue(Object.getPrototypeOf(instance), CustomPlainDateTime.prototype, "Instance of CustomPlainDateTime");
+assert(instance instanceof CustomPlainDateTime, "Instance of CustomPlainDateTime");
+assert(instance instanceof Temporal.PlainDateTime, "Instance of Temporal.PlainDateTime");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/basic.js
new file mode 100644
index 0000000000..a0edb834b5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/basic.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.plainmonthday
+description: Basic tests for the PlainMonthDay constructor.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const leapDay = new Temporal.PlainMonthDay(2, 29);
+TemporalHelpers.assertPlainMonthDay(leapDay, "M02", 29, "leap day is supported");
+assert.sameValue(leapDay.calendarId, "iso8601", "leap day calendar");
+
+const beforeEpoch = new Temporal.PlainMonthDay(12, 2, "iso8601", 1920);
+TemporalHelpers.assertPlainMonthDay(beforeEpoch, "M12", 2, "reference year before epoch", 1920);
+assert.sameValue(beforeEpoch.calendarId, "iso8601", "reference year before epoch calendar");
+
+const afterEpoch = new Temporal.PlainMonthDay(1, 7, "iso8601", 1980);
+TemporalHelpers.assertPlainMonthDay(afterEpoch, "M01", 7, "reference year after epoch", 1980);
+assert.sameValue(afterEpoch.calendarId, "iso8601", "reference year after epoch calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/builtin.js
new file mode 100644
index 0000000000..0f9124ee4a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/builtin.js
@@ -0,0 +1,30 @@
+// |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.plainmonthday
+description: Tests that Temporal.PlainMonthDay meets the requirements for built-in objects
+info: |
+ 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.PlainMonthDay),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainMonthDay),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainMonthDay),
+ Function.prototype, "prototype");
+
+assert.sameValue(typeof Temporal.PlainMonthDay.prototype,
+ "object", "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/calendar-always.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/calendar-always.js
new file mode 100644
index 0000000000..727a72bfe5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/calendar-always.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.plainmonthday
+description: If calendar name is to be emitted, include additional reference info
+features: [Temporal]
+---*/
+
+const pmd = new Temporal.PlainMonthDay(10, 31, "iso8601", 2019);
+
+assert.sameValue(
+ pmd.toString({ calendarName: 'always' }),
+ "2019-10-31[u-ca=iso8601]",
+ "emit year-month-day if calendarName = 'always' (four-argument constructor)"
+);
+
+const anotherPMD = Temporal.PlainMonthDay.from("2019-10-31"); // 2019 will get dropped
+
+assert.sameValue(
+ anotherPMD.toString({ calendarName: 'always' }),
+ "1972-10-31[u-ca=iso8601]",
+ "emit fallback year if calendarName = 'always' (static from)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/calendar-case-insensitive.js
new file mode 100644
index 0000000000..3bf53f6ba0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/calendar-case-insensitive.js
@@ -0,0 +1,16 @@
+// |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.plainmonthday
+description: Calendar names are case-insensitive
+features: [Temporal]
+---*/
+
+const arg = "iSo8601";
+
+const result = new Temporal.PlainMonthDay(12, 15, arg, 1972);
+assert.sameValue(result.calendarId, "iso8601", "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/calendar-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/calendar-invalid.js
new file mode 100644
index 0000000000..5d26677c45
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/calendar-invalid.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.
+
+/*---
+description: Temporal.PlainMonthDay throws a RangeError if the calendar argument is invalid
+esid: sec-temporal.plainmonthday
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = ["get month.valueOf", "call month.valueOf", "get day.valueOf", "call day.valueOf"];
+const actual = [];
+const args = [
+ TemporalHelpers.toPrimitiveObserver(actual, 2, "month"),
+ TemporalHelpers.toPrimitiveObserver(actual, 1, "day"),
+ "local",
+ TemporalHelpers.toPrimitiveObserver(actual, 1, "year")
+];
+assert.throws(RangeError, () => new Temporal.PlainMonthDay(...args));
+assert.compareArray(actual, expected, "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/calendar-number.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/calendar-number.js
new file mode 100644
index 0000000000..539d444e0e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/calendar-number.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.plainmonthday
+description: A number is not allowed to be a calendar
+features: [Temporal]
+---*/
+
+const numbers = [
+ 1,
+ -19761118,
+ 19761118,
+ 1234567890,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => new Temporal.PlainMonthDay(12, 15, arg, 1972),
+ "A number is not a valid ISO string for Calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/calendar-string.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/calendar-string.js
new file mode 100644
index 0000000000..06e5c4938c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/calendar-string.js
@@ -0,0 +1,16 @@
+// |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.plainmonthday.constructor
+description: A calendar ID is valid input for Calendar
+features: [Temporal]
+---*/
+
+const arg = "iso8601";
+
+const result = new Temporal.PlainMonthDay(12, 15, arg, 1972);
+assert.sameValue(result.getISOFields().calendar, "iso8601", `Calendar created from string "${arg}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/calendar-temporal-object.js
new file mode 100644
index 0000000000..910285cd61
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/calendar-temporal-object.js
@@ -0,0 +1,41 @@
+// |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.plainmonthday
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal-totemporalcalendar step 1.b:
+ b. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const plainDate = new Temporal.PlainDate(2000, 5, 2);
+const plainDateTime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const plainMonthDay = new Temporal.PlainMonthDay(5, 2);
+const plainYearMonth = new Temporal.PlainYearMonth(2000, 5);
+const zonedDateTime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+
+[plainDate, plainDateTime, plainMonthDay, plainYearMonth, zonedDateTime].forEach((arg) => {
+ const actual = [];
+ const expected = [];
+
+ const calendar = arg.getISOFields().calendar;
+
+ Object.defineProperty(arg, "calendar", {
+ get() {
+ actual.push("get calendar");
+ return calendar;
+ },
+ });
+
+ const result = new Temporal.PlainMonthDay(12, 15, arg, 1972);
+ assert.sameValue(result.getISOFields().calendar, calendar, "Temporal object coerced to calendar");
+
+ assert.compareArray(actual, expected, "calendar getter not called");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/calendar-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/calendar-undefined.js
new file mode 100644
index 0000000000..9db2f4c1a8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/calendar-undefined.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.plainmonthday
+description: Calendar argument defaults to the built-in ISO 8601 calendar
+features: [Temporal]
+---*/
+
+const args = [5, 2];
+
+Object.defineProperty(Temporal.Calendar, "from", {
+ get() {
+ throw new Test262Error("Should not get Calendar.from");
+ },
+});
+
+const dateExplicit = new Temporal.PlainMonthDay(...args, undefined);
+assert.sameValue(dateExplicit.calendarId, "iso8601");
+
+const dateImplicit = new Temporal.PlainMonthDay(...args);
+assert.sameValue(dateImplicit.calendarId, "iso8601");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/calendar-wrong-type.js
new file mode 100644
index 0000000000..83fc7ee16b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/calendar-wrong-type.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.plainmonthday
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or object for Calendar
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const primitiveTests = [
+ [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,
+ () => new Temporal.PlainMonthDay(12, 15, arg, 1972),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => new Temporal.PlainMonthDay(12, 15, arg, 1972), `${description} is not a valid object and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/constructor.js
new file mode 100644
index 0000000000..d5bdac8122
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/constructor.js
@@ -0,0 +1,15 @@
+// |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.plainmonthday
+description: Temporal.PlainMonthDay constructor cannot be called as a function
+info: |
+ 1. If NewTarget is undefined, throw a TypeError exception.
+features: [Temporal]
+---*/
+
+assert.throws(TypeError, () => Temporal.PlainMonthDay(1, 2));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..b94b0658d4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-builtin-calendar-no-array-iteration.js
@@ -0,0 +1,23 @@
+// |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.plainmonthday.from
+description: >
+ Calling the method with a property bag argument with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const arg = { monthCode: "M11", day: 23, calendar: "iso8601" };
+Temporal.PlainMonthDay.from(arg);
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-number.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-number.js
new file mode 100644
index 0000000000..1296a24842
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-number.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.plainmonthday.from
+description: A number is invalid in place of an ISO string for Temporal.PlainMonthDay
+features: [Temporal]
+---*/
+
+const numbers = [
+ 1,
+ 1118,
+ -1118,
+ 12345,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => Temporal.PlainMonthDay.from(arg),
+ `A number (${arg}) is not a valid ISO string for PlainMonthDay`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-plainmonthday.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-plainmonthday.js
new file mode 100644
index 0000000000..720ab148ac
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-plainmonthday.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.plainmonthday.from
+description: A PlainMonthDay object is copied, not returned directly
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const orig = new Temporal.PlainMonthDay(5, 2, undefined, 2000);
+const result = Temporal.PlainMonthDay.from(orig);
+
+TemporalHelpers.assertPlainMonthDay(
+ result,
+ "M05", 2,
+ "PlainMonthDay is copied",
+ /* isoYear = */ 2000
+);
+
+assert.sameValue(result.getISOFields().calendar, orig.getISOFields().calendar, "Calendar is copied");
+
+assert.notSameValue(
+ result,
+ orig,
+ "When a PlainMonthDay is given, the returned value is not the original PlainMonthDay"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-propertybag-calendar-case-insensitive.js
new file mode 100644
index 0000000000..a0aeefedf6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-propertybag-calendar-case-insensitive.js
@@ -0,0 +1,18 @@
+// |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.plainmonthday.from
+description: The calendar name is case-insensitive
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = "IsO8601";
+
+const arg = { monthCode: "M11", day: 18, calendar };
+const result = Temporal.PlainMonthDay.from(arg);
+TemporalHelpers.assertPlainMonthDay(result, "M11", 18, "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-propertybag-calendar-leap-second.js
new file mode 100644
index 0000000000..63643396dc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-propertybag-calendar-leap-second.js
@@ -0,0 +1,22 @@
+// |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.plainmonthday.from
+description: Leap second is a valid ISO string for a calendar in a property bag
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = "2016-12-31T23:59:60";
+
+const arg = { monthCode: "M11", day: 18, calendar };
+const result = Temporal.PlainMonthDay.from(arg);
+TemporalHelpers.assertPlainMonthDay(
+ result,
+ "M11", 18,
+ "leap second is a valid ISO string for calendar"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-propertybag-calendar-number.js
new file mode 100644
index 0000000000..864b362345
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-propertybag-calendar-number.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.plainmonthday.from
+description: A number as calendar in a property bag is not accepted
+features: [Temporal]
+---*/
+
+const numbers = [
+ 1,
+ 19970327,
+ -19970327,
+ 1234567890,
+];
+
+for (const calendar of numbers) {
+ const arg = { monthCode: "M11", day: 18, calendar };
+ assert.throws(
+ TypeError,
+ () => Temporal.PlainMonthDay.from(arg),
+ "Numbers cannot be used as a calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-propertybag-calendar-string.js
new file mode 100644
index 0000000000..2e791bc72b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-propertybag-calendar-string.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.plainmonthday.from
+description: A calendar ID is valid input for Calendar
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = "iso8601";
+
+const arg = { monthCode: "M11", day: 18, calendar };
+const result = Temporal.PlainMonthDay.from(arg);
+TemporalHelpers.assertPlainMonthDay(result, "M11", 18, `Calendar created from string "${calendar}"`);
+assert.sameValue(result.getISOFields().calendar, "iso8601", "calendar slot stores a string");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-propertybag-calendar-wrong-type.js
new file mode 100644
index 0000000000..870b265522
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-propertybag-calendar-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.plainmonthday.from
+description: >
+ Appropriate error thrown when a calendar property from a property bag cannot
+ be converted to a calendar object or string
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [calendar, description] of primitiveTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ typeof calendar === 'string' ? RangeError : TypeError,
+ () => Temporal.PlainMonthDay.from(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+ [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields()
+];
+
+for (const [calendar, description] of typeErrorTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(TypeError, () => Temporal.PlainMonthDay.from(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/PlainMonthDay/from/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-propertybag-calendar-year-zero.js
new file mode 100644
index 0000000000..19fb53023f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-propertybag-calendar-year-zero.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.plainmonthday.from
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T17:45",
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+01:00",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainMonthDay.from(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..bccd2d2e2f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-string-calendar-annotation.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.plainmonthday.from
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["1976-05-02T15:23[u-ca=iso8601]", "without time zone"],
+ ["1976-05-02T15:23[UTC][u-ca=iso8601]", "with time zone"],
+ ["1976-05-02T15:23[!u-ca=iso8601]", "with ! and no time zone"],
+ ["1976-05-02T15:23[UTC][!u-ca=iso8601]", "with ! and time zone"],
+ ["1976-05-02T15:23[u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.PlainMonthDay.from(arg);
+
+ TemporalHelpers.assertPlainMonthDay(
+ result,
+ "M05", 2,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..bc64a74f38
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-string-critical-unknown-annotation.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.plainmonthday.from
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "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]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainMonthDay.from(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..6babb740bb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-string-date-with-utc-offset.js
@@ -0,0 +1,63 @@
+// |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.plainmonthday.from
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const validStrings = [
+ "05-02[Asia/Katmandu]",
+ "05-02[!Asia/Katmandu]",
+ "05-02[u-ca=iso8601]",
+ "05-02[Asia/Tokyo][u-ca=iso8601]",
+ "--05-02[Asia/Katmandu]",
+ "--05-02[!Asia/Katmandu]",
+ "--05-02[u-ca=iso8601]",
+ "--05-02[Asia/Tokyo][u-ca=iso8601]",
+ "2000-05-02T00+00:00",
+ "2000-05-02T00+00:00[UTC]",
+ "2000-05-02T00-02:30[America/St_Johns]",
+];
+
+for (const arg of validStrings) {
+ const result = Temporal.PlainMonthDay.from(arg);
+
+ TemporalHelpers.assertPlainMonthDay(
+ result,
+ "M05", 2,
+ `"${arg}" is a valid UTC offset with time for PlainMonthDay`
+ );
+}
+
+const invalidStrings = [
+ "09-15Z",
+ "09-15Z[UTC]",
+ "09-15+01:00",
+ "09-15+01:00[Europe/Vienna]",
+ "--09-15Z",
+ "--09-15Z[UTC]",
+ "--09-15+01:00",
+ "--09-15+01:00[Europe/Vienna]",
+ "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]",
+ "09-15[u-ca=chinese]",
+];
+
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainMonthDay.from(arg),
+ `"${arg}" UTC offset without time is not valid for PlainMonthDay`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..0ea75ba1d0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/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.plainmonthday.from
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[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]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainMonthDay.from(arg),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..7801034dc8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-string-multiple-time-zone.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.plainmonthday.from
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "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]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainMonthDay.from(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-string-time-separators.js
new file mode 100644
index 0000000000..14812c97f5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-string-time-separators.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.plainmonthday.from
+description: Time separator in string argument can vary
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["1976-05-02T15:23", "uppercase T"],
+ ["1976-05-02t15:23", "lowercase T"],
+ ["1976-05-02 15:23", "space between date and time"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.PlainMonthDay.from(arg);
+
+ TemporalHelpers.assertPlainMonthDay(
+ result,
+ "M05", 2,
+ `variant time separators (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..f4b348d0bb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-string-time-zone-annotation.js
@@ -0,0 +1,33 @@
+// |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.plainmonthday.from
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["1976-05-02T15:23[Asia/Kolkata]", "named, with no offset"],
+ ["1976-05-02T15:23[!Europe/Vienna]", "named, with ! and no offset"],
+ ["1976-05-02T15:23[+00:00]", "numeric, with no offset"],
+ ["1976-05-02T15:23[!-02:30]", "numeric, with ! and no offset"],
+ ["1976-05-02T15:23+00:00[UTC]", "named, with offset"],
+ ["1976-05-02T15:23+00:00[!Africa/Abidjan]", "named, with offset and !"],
+ ["1976-05-02T15:23+00:00[+01:00]", "numeric, with offset"],
+ ["1976-05-02T15:23+00:00[!-08:00]", "numeric, with offset and !"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.PlainMonthDay.from(arg);
+
+ TemporalHelpers.assertPlainMonthDay(
+ result,
+ "M05", 2,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..ec7d10884f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-string-unknown-annotation.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.plainmonthday.from
+description: Various forms of unknown annotation
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["1976-05-02T15:23[foo=bar]", "alone"],
+ ["1976-05-02T15:23[UTC][foo=bar]", "with time zone"],
+ ["1976-05-02T15:23[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["1976-05-02T15:23[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["1976-05-02T15:23[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.PlainMonthDay.from(arg);
+
+ TemporalHelpers.assertPlainMonthDay(
+ result,
+ "M05", 2,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..c2591c4eec
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-string-with-utc-designator.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.plainmonthday.from
+description: RangeError thrown if a string with UTC designator is used as a PlainMonthDay
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "2019-10-01T09:00:00Z",
+ "2019-10-01T09:00:00Z[UTC]",
+];
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainMonthDay.from(arg),
+ "String with UTC designator should not be valid as a PlainMonthDay"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-wrong-type.js
new file mode 100644
index 0000000000..7ee63cf148
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-wrong-type.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.plainmonthday.from
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainMonthDay
+features: [BigInt, Symbol, Temporal]
+---*/
+
+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,
+ () => Temporal.PlainMonthDay.from(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.PlainMonthDay, "Temporal.PlainMonthDay, object"],
+ [Temporal.PlainMonthDay.prototype, "Temporal.PlainMonthDay.prototype, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => Temporal.PlainMonthDay.from(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/PlainMonthDay/from/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/builtin.js
new file mode 100644
index 0000000000..7863e22457
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/builtin.js
@@ -0,0 +1,33 @@
+// |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.plainmonthday.from
+description: Tests that Temporal.PlainMonthDay.from meets the requirements for built-in objects
+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.PlainMonthDay.from),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainMonthDay.from),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainMonthDay.from),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainMonthDay.from.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/calendar-datefromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..3fba4ca9b7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/calendar-datefromfields-called-with-null-prototype-fields.js
@@ -0,0 +1,18 @@
+// |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.plainmonthday.from
+description: >
+ Calendar.monthDayFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const arg = { monthCode: "M05", day: 2, calendar };
+Temporal.PlainMonthDay.from(arg);
+assert.sameValue(calendar.monthDayFromFieldsCallCount, 1, "monthDayFromFields should be called on the property bag's calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/calendar-fields-iterable.js
new file mode 100644
index 0000000000..507085975d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/calendar-fields-iterable.js
@@ -0,0 +1,33 @@
+// |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.plainmonthday.from
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plainmonthday.from step 3:
+ 3. Return ? ToTemporalMonthDay(_item_, _options_).
+ sec-temporal-totemporalmonthday step 2.f:
+ f. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+Temporal.PlainMonthDay.from({ monthCode: "M05", day: 2, calendar });
+
+assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/calendar-monthdayfromfields-validates-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/calendar-monthdayfromfields-validates-fields.js
new file mode 100644
index 0000000000..6f966f881f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/calendar-monthdayfromfields-validates-fields.js
@@ -0,0 +1,34 @@
+// |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.plainmonthday.from
+description: >
+ Calendar.monthDayFromFields method validates which fields must be present
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = new class extends Temporal.Calendar {
+ fields(fields) {
+ return fields.slice().concat("fnord");
+ }
+ monthDayFromFields(fields) {
+ // accept everything except fnord
+ assert.sameValue(fields.fnord, undefined);
+ return new Temporal.PlainMonthDay(1, 1, this, 1972);
+ }
+}("iso8601");
+
+// This would throw on any non-ISO builtin calendar
+const result = Temporal.PlainMonthDay.from({ month: 8, day: 16, calendar });
+TemporalHelpers.assertPlainMonthDay(result, "M01", 1, "monthDayFromFields determines what fields are necessary")
+
+assert.throws(
+ Test262Error,
+ () => Temporal.PlainMonthDay.from({ monthCode: "M09", day: 19, fnord: "fnord", calendar }),
+ "monthDayFromFields determines what fields are disallowed"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/calendar-temporal-object.js
new file mode 100644
index 0000000000..7820bbb8bd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/calendar-temporal-object.js
@@ -0,0 +1,29 @@
+// |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.plainmonthday.from
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plainmonthday.from step 3:
+ 3. Return ? ToTemporalMonthDay(_item_, _options_).
+ sec-temporal-totemporalmonthday step 3.e:
+ e. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject, calendar) => {
+ const result = Temporal.PlainMonthDay.from({ monthCode: "M05", day: 2, calendar: temporalObject });
+ assert.sameValue(result.getCalendar(), calendar, "Temporal object coerced to calendar");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..5f47f9111f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/constructor-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |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.plainmonthday.prototype.from
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+
+assert.throws(RangeError, () => Temporal.PlainMonthDay.from(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/duplicate-calendar-fields.js
new file mode 100644
index 0000000000..d12829d43c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/duplicate-calendar-fields.js
@@ -0,0 +1,19 @@
+// |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.plainmonthday.prototype.from
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+
+for (const extra_fields of [['foo', 'foo'], ['day'], ['month'], ['monthCode'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar };
+
+ assert.throws(RangeError, () => Temporal.PlainMonthDay.from(arg));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/fields-leap-day.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/fields-leap-day.js
new file mode 100644
index 0000000000..fd1591dd70
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/fields-leap-day.js
@@ -0,0 +1,42 @@
+// |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.plainmonthday.from
+description: Basic tests for PlainMonthDay.from(string).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+// Leap years
+["reject", "constrain"].forEach((overflow) => {
+ const string = Temporal.PlainMonthDay.from("02-29", { overflow });
+ TemporalHelpers.assertPlainMonthDay(string, "M02", 29, `from ${overflow} string`);
+
+ const monthCode = Temporal.PlainMonthDay.from({ monthCode: "M02", day: 29 }, { overflow });
+ TemporalHelpers.assertPlainMonthDay(monthCode, "M02", 29, `from ${overflow} with monthCode`);
+
+ const implicit = Temporal.PlainMonthDay.from({ month: 2, day: 29 }, { overflow });
+ TemporalHelpers.assertPlainMonthDay(implicit, "M02", 29, `from ${overflow} without year`);
+
+ const explicit = Temporal.PlainMonthDay.from({ month: 2, day: 29, year: 1996 }, { overflow });
+ TemporalHelpers.assertPlainMonthDay(explicit, "M02", 29, `from ${overflow} with leap year`);
+});
+
+// Non-leap years
+assert.throws(RangeError,
+ () => Temporal.PlainMonthDay.from({ month: 2, day: 29, year: 2001 }, { overflow: "reject" }),
+ "from reject with non-leap year");
+
+const nonLeap = Temporal.PlainMonthDay.from({ month: 2, day: 29, year: 2001 }, { overflow: "constrain" });
+TemporalHelpers.assertPlainMonthDay(nonLeap, "M02", 28, "from constrain with non-leap year");
+
+assert.throws(RangeError,
+ () => Temporal.PlainMonthDay.from({ month: 2, day: 29, year: 2001, calendar: "iso8601" }, { overflow: "reject" }),
+ "from reject with non-leap year and explicit calendar");
+
+const nonLeapCalendar = Temporal.PlainMonthDay.from({ month: 2, day: 29, year: 2001, calendar: "iso8601" }, { overflow: "constrain" });
+TemporalHelpers.assertPlainMonthDay(nonLeapCalendar, "M02", 28, "from constrain with non-leap year and explicit calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/fields-missing-properties.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/fields-missing-properties.js
new file mode 100644
index 0000000000..9b706bf645
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/fields-missing-properties.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.plainmonthday.from
+description: Basic tests for PlainMonthDay.from(object) with missing properties.
+features: [Temporal]
+---*/
+
+assert.throws(TypeError, () => Temporal.PlainMonthDay.from({}), "No properties");
+assert.throws(TypeError, () => Temporal.PlainMonthDay.from({ day: 15 }), "Only day");
+assert.throws(TypeError, () => Temporal.PlainMonthDay.from({ monthCode: 'M12' }), "Only monthCode");
+assert.throws(TypeError, () => Temporal.PlainMonthDay.from({ monthCode: undefined, day: 15 }), "monthCode undefined");
+assert.throws(TypeError, () => Temporal.PlainMonthDay.from({ months: 12, day: 31 }), "months plural");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/fields-object.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/fields-object.js
new file mode 100644
index 0000000000..c41950ba28
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/fields-object.js
@@ -0,0 +1,33 @@
+// |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.plainmonthday.from
+description: Basic tests for PlainMonthDay.from(object).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const tests = [
+ [{ monthCode: "M10", day: 1 }, "option bag with monthCode"],
+ [{ monthCode: "M10", day: 1, year: 2015 }, "option bag with year, monthCode"],
+ [{ month: 10, day: 1 }, "option bag with year, month"],
+ [{ month: 10, day: 1, year: 2015 }, "option bag with year, month"],
+ [{ month: 10, day: 1, days: 31 }, "option bag with plural 'days'"],
+ [new Temporal.PlainMonthDay(10, 1), "PlainMonthDay object"],
+ [Temporal.PlainDate.from("2019-10-01"), "PlainDate object"],
+ [{ monthCode: "M10", day: 1, calendar: "iso8601" }, "option bag with monthCode and explicit ISO calendar"],
+ [{ month: 10, day: 1, calendar: "iso8601" }, "option bag with month and explicit ISO calendar"],
+ [{ monthCode: "M10", day: 1, calendar: Temporal.Calendar.from("iso8601") }, "option bag with monthCode and object ISO calendar"],
+ [{ month: 10, day: 1, calendar: Temporal.Calendar.from("iso8601") }, "option bag with month and object ISO calendar"],
+];
+
+for (const [argument, description = argument] of tests) {
+ const plainMonthDay = Temporal.PlainMonthDay.from(argument);
+ assert.notSameValue(plainMonthDay, argument, `from ${description} converts`);
+ TemporalHelpers.assertPlainMonthDay(plainMonthDay, "M10", 1, `from ${description}`);
+ assert.sameValue(plainMonthDay.calendarId, "iso8601", `from ${description} calendar`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/fields-plainmonthday.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/fields-plainmonthday.js
new file mode 100644
index 0000000000..7950b7979d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/fields-plainmonthday.js
@@ -0,0 +1,30 @@
+// |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.plainmonthday.from
+description: Basic tests for PlainMonthDay.from(PlainMonthDay).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "get overflow",
+ "get overflow.toString",
+ "call overflow.toString",
+];
+const actual = [];
+const options = {
+ get overflow() {
+ actual.push("get overflow");
+ return TemporalHelpers.toPrimitiveObserver(actual, "reject", "overflow");
+ }
+};
+
+const fields = new Temporal.PlainMonthDay(11, 16, undefined, 1960);
+const result = Temporal.PlainMonthDay.from(fields, options);
+TemporalHelpers.assertPlainMonthDay(result, "M11", 16, "should copy reference year", 1960);
+assert.compareArray(actual, expected, "Should get overflow");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/fields-string.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/fields-string.js
new file mode 100644
index 0000000000..4b2abea439
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/fields-string.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.plainmonthday.from
+description: Basic tests for PlainMonthDay.from(string).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const argument of TemporalHelpers.ISO.plainMonthDayStringsValid()) {
+ const plainMonthDay = Temporal.PlainMonthDay.from(argument);
+ assert.notSameValue(plainMonthDay, argument, `from ${argument} converts`);
+ TemporalHelpers.assertPlainMonthDay(plainMonthDay, "M10", 1, `from ${argument}`);
+ assert.sameValue(plainMonthDay.calendarId, "iso8601", `from ${argument} calendar`);
+}
+
+for (const arg of TemporalHelpers.ISO.plainMonthDayStringsInvalid()) {
+ assert.throws(RangeError, () => Temporal.PlainMonthDay.from(arg), `"${arg}" not a valid PlainMonthDay string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..25835f1fc5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/infinity-throws-rangeerror.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.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plainmonthday.from
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const base = { year: 2000, month: 5, day: 2 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day"].forEach((prop) => {
+ ["constrain", "reject"].forEach((overflow) => {
+ assert.throws(RangeError, () => Temporal.PlainMonthDay.from({ ...base, [prop]: inf }, { overflow }), `${prop} property cannot be ${inf} (overflow ${overflow}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => Temporal.PlainMonthDay.from({ ...base, [prop]: obj }, { overflow }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/leap-second.js
new file mode 100644
index 0000000000..eb6ec3ad07
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/leap-second.js
@@ -0,0 +1,44 @@
+// |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.plainmonthday.from
+description: Leap second is a valid ISO string for PlainMonthDay
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+let arg = "2016-12-31T23:59:60";
+
+const result1 = Temporal.PlainMonthDay.from(arg);
+TemporalHelpers.assertPlainMonthDay(
+ result1,
+ "M12", 31,
+ "leap second is a valid ISO string for PlainMonthDay"
+);
+
+const result2 = Temporal.PlainMonthDay.from(arg, { overflow: "reject" });
+TemporalHelpers.assertPlainMonthDay(
+ result2,
+ "M12", 31,
+ "leap second is a valid ISO string for PlainMonthDay"
+);
+
+arg = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+
+const result3 = Temporal.PlainMonthDay.from(arg);
+TemporalHelpers.assertPlainMonthDay(
+ result3,
+ "M12", 31,
+ "second: 60 is ignored in property bag for PlainMonthDay"
+);
+
+const result4 = Temporal.PlainMonthDay.from(arg, { overflow: "reject" });
+TemporalHelpers.assertPlainMonthDay(
+ result4,
+ "M12", 31,
+ "second: 60 is ignored in property bag for PlainMonthDay even with overflow: reject"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/length.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/length.js
new file mode 100644
index 0000000000..2d96ee9b53
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/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.plainmonthday.from
+description: Temporal.PlainMonthDay.from.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.PlainMonthDay.from, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/name.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/name.js
new file mode 100644
index 0000000000..b6f2ea00b4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/name.js
@@ -0,0 +1,26 @@
+// |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.plainmonthday.from
+description: Temporal.PlainMonthDay.from.name is "from"
+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.PlainMonthDay.from, "name", {
+ value: "from",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/not-a-constructor.js
new file mode 100644
index 0000000000..593b6635ea
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/not-a-constructor.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.plainmonthday.from
+description: Temporal.PlainMonthDay.from 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.PlainMonthDay.from();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainMonthDay.from), false,
+ "isConstructor(Temporal.PlainMonthDay.from)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/observable-get-overflow-argument-primitive.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/observable-get-overflow-argument-primitive.js
new file mode 100644
index 0000000000..7544949b8e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/observable-get-overflow-argument-primitive.js
@@ -0,0 +1,39 @@
+// |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.plainmonthday.from
+description: overflow property is extracted with string argument.
+info: |
+ 1. Perform ? ToTemporalOverflow(_options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "ownKeys options",
+ "getOwnPropertyDescriptor options.overflow",
+ "get options.overflow",
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+];
+
+let actual = [];
+const options = TemporalHelpers.propertyBagObserver(actual, { overflow: "constrain" }, "options");
+
+const result = Temporal.PlainMonthDay.from("05-17", options);
+assert.compareArray(actual, expected, "Successful call");
+TemporalHelpers.assertPlainMonthDay(result, "M05", 17);
+
+actual.splice(0); // empty it for the next check
+const failureExpected = [
+ "ownKeys options",
+ "getOwnPropertyDescriptor options.overflow",
+ "get options.overflow",
+];
+
+assert.throws(TypeError, () => Temporal.PlainMonthDay.from(7, options));
+assert.compareArray(actual, failureExpected, "Failing call");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/observable-get-overflow-argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/observable-get-overflow-argument-string-invalid.js
new file mode 100644
index 0000000000..d3bd9683cb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/observable-get-overflow-argument-string-invalid.js
@@ -0,0 +1,24 @@
+// |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.plainmonthday.from
+description: overflow property is extracted with ISO-invalid string argument.
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "ownKeys options",
+ "getOwnPropertyDescriptor options.overflow",
+ "get options.overflow",
+];
+
+let actual = [];
+const options = TemporalHelpers.propertyBagObserver(actual, { overflow: "constrain" }, "options");
+
+assert.throws(RangeError, () => Temporal.PlainMonthDay.from("13-34", options));
+assert.compareArray(actual, expected);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/options-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/options-invalid.js
new file mode 100644
index 0000000000..66a571d18c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/options-invalid.js
@@ -0,0 +1,18 @@
+// |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.plainmonthday.from
+description: TypeError thrown when options argument is a primitive
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const fields = { month: 2, day: 31 };
+
+const values = [null, true, "hello", Symbol("foo"), 1, 1n];
+for (const badOptions of values) {
+ assert.throws(TypeError, () => Temporal.PlainMonthDay.from(fields, badOptions));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/options-object.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/options-object.js
new file mode 100644
index 0000000000..e72fb46bdb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/options-object.js
@@ -0,0 +1,22 @@
+// |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.plainmonthday.prototype.from
+description: Empty object may be used as options
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.assertPlainMonthDay(
+ Temporal.PlainMonthDay.from({ monthCode: "M12", day: 15 }, {}), "M12", 15,
+ "options may be an empty plain object"
+);
+
+TemporalHelpers.assertPlainMonthDay(
+ Temporal.PlainMonthDay.from({ monthCode: "M12", day: 15 }, () => {}), "M12", 15,
+ "options may be an empty function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/options-undefined.js
new file mode 100644
index 0000000000..72925d679d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/options-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.plainmonthday.from
+includes: [temporalHelpers.js]
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const fields = { month: 2, day: 31 };
+
+const explicit = Temporal.PlainMonthDay.from(fields, undefined);
+TemporalHelpers.assertPlainMonthDay(explicit, "M02", 29, "default overflow is constrain");
+
+const implicit = Temporal.PlainMonthDay.from(fields);
+TemporalHelpers.assertPlainMonthDay(implicit, "M02", 29, "default overflow is constrain");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/options-wrong-type.js
new file mode 100644
index 0000000000..420269cff3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/options-wrong-type.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.plainmonthday.from
+description: TypeError thrown when options argument is a primitive
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const badOptions = [
+ null,
+ true,
+ "some string",
+ Symbol(),
+ 1,
+ 2n,
+];
+
+for (const value of badOptions) {
+ assert.throws(TypeError, () => Temporal.PlainMonthDay.from({ monthCode: "M12", day: 15 }, value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/order-of-operations.js
new file mode 100644
index 0000000000..fc28c00f21
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/order-of-operations.js
@@ -0,0 +1,83 @@
+// |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.plainmonthday.from
+description: Properties on an object passed to from() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ // CopyDataProperties
+ "ownKeys options",
+ "getOwnPropertyDescriptor options.overflow",
+ "get options.overflow",
+ "getOwnPropertyDescriptor options.extra",
+ "get options.extra",
+ "get fields.calendar",
+ "has fields.calendar.dateAdd",
+ "has fields.calendar.dateFromFields",
+ "has fields.calendar.dateUntil",
+ "has fields.calendar.day",
+ "has fields.calendar.dayOfWeek",
+ "has fields.calendar.dayOfYear",
+ "has fields.calendar.daysInMonth",
+ "has fields.calendar.daysInWeek",
+ "has fields.calendar.daysInYear",
+ "has fields.calendar.fields",
+ "has fields.calendar.id",
+ "has fields.calendar.inLeapYear",
+ "has fields.calendar.mergeFields",
+ "has fields.calendar.month",
+ "has fields.calendar.monthCode",
+ "has fields.calendar.monthDayFromFields",
+ "has fields.calendar.monthsInYear",
+ "has fields.calendar.weekOfYear",
+ "has fields.calendar.year",
+ "has fields.calendar.yearMonthFromFields",
+ "has fields.calendar.yearOfWeek",
+ // lookup
+ "get fields.calendar.fields",
+ "get fields.calendar.monthDayFromFields",
+ // CalendarFields
+ "call fields.calendar.fields",
+ // PrepareTemporalFields
+ "get fields.day",
+ "get fields.day.valueOf",
+ "call fields.day.valueOf",
+ "get fields.month",
+ "get fields.month.valueOf",
+ "call fields.month.valueOf",
+ "get fields.monthCode",
+ "get fields.monthCode.toString",
+ "call fields.monthCode.toString",
+ "get fields.year",
+ "get fields.year.valueOf",
+ "call fields.year.valueOf",
+ // CalendarMonthDayFromFields
+ "call fields.calendar.monthDayFromFields",
+ // inside Calendar.p.monthDayFromFields
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+];
+const actual = [];
+
+const fields = TemporalHelpers.propertyBagObserver(actual, {
+ year: 1.7,
+ month: 1.7,
+ monthCode: "M01",
+ day: 1.7,
+ calendar: TemporalHelpers.calendarObserver(actual, "fields.calendar"),
+}, "fields");
+
+const options = TemporalHelpers.propertyBagObserver(actual, {
+ overflow: "constrain",
+ extra: "property",
+}, "options");
+
+Temporal.PlainMonthDay.from(fields, options);
+assert.compareArray(actual, expected, "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/overflow-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/overflow-invalid-string.js
new file mode 100644
index 0000000000..ab37726490
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/overflow-invalid-string.js
@@ -0,0 +1,43 @@
+// |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.plainmonthday.from
+description: RangeError thrown when overflow option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-totemporalmonthday steps 3–4:
+ 3. If Type(_item_) is Object, then
+ ...
+ j. Return ? MonthDayFromFields(_calendar_, _fields_, _options_).
+ 4. Perform ? ToTemporalOverflow(_options_).
+ sec-temporal.plainmonthday.from steps 2–3:
+ 2. If Type(_item_) is Object and _item_ has an [[InitializedTemporalMonthDay]] internal slot, then
+ a. Perform ? ToTemporalOverflow(_options_).
+ b. Return ...
+ 3. Return ? ToTemporalMonthDay(_item_, _options_).
+features: [Temporal]
+---*/
+
+const validValues = [
+ new Temporal.PlainMonthDay(5, 2),
+ { monthCode: "M05", day: 2 },
+ "05-02",
+];
+
+const badOverflows = ["", "CONSTRAIN", "balance", "other string", "constra\u0131n", "reject\0"];
+for (const value of validValues) {
+ for (const overflow of badOverflows) {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainMonthDay.from(value, { overflow }),
+ `invalid overflow ("${overflow}")`
+ );
+ }
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/overflow-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/overflow-undefined.js
new file mode 100644
index 0000000000..5b47d26db5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/overflow-undefined.js
@@ -0,0 +1,48 @@
+// |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.plainmonthday.from
+description: Fallback value for overflow option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-totemporalmonthday steps 3–4:
+ 3. If Type(_item_) is Object, then
+ ...
+ j. Return ? MonthDayFromFields(_calendar_, _fields_, _options_).
+ 4. Perform ? ToTemporalOverflow(_options_).
+ sec-temporal.plainmonthday.from steps 2–3:
+ 2. If Type(_item_) is Object and _item_ has an [[InitializedTemporalMonthDay]] internal slot, then
+ a. Perform ? ToTemporalOverflow(_options_).
+ b. Return ...
+ 3. Return ? ToTemporalMonthDay(_item_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const validValues = [
+ new Temporal.PlainMonthDay(5, 2),
+ "05-02",
+];
+validValues.forEach((value) => {
+ const explicit = Temporal.PlainMonthDay.from(value, { overflow: undefined });
+ TemporalHelpers.assertPlainMonthDay(explicit, "M05", 2, "overflow is ignored");
+ const implicit = Temporal.PlainMonthDay.from(value, {});
+ TemporalHelpers.assertPlainMonthDay(implicit, "M05", 2, "overflow is ignored");
+ const lambda = Temporal.PlainMonthDay.from(value, () => {});
+ TemporalHelpers.assertPlainMonthDay(lambda, "M05", 2, "overflow is ignored");
+});
+
+const propertyBag = { year: 2000, month: 13, day: 34 };
+const explicit = Temporal.PlainMonthDay.from(propertyBag, { overflow: undefined });
+TemporalHelpers.assertPlainMonthDay(explicit, "M12", 31, "default overflow is constrain");
+const implicit = Temporal.PlainMonthDay.from(propertyBag, {});
+TemporalHelpers.assertPlainMonthDay(implicit, "M12", 31, "default overflow is constrain");
+const lambda = Temporal.PlainMonthDay.from(propertyBag, () => {});
+TemporalHelpers.assertPlainMonthDay(lambda, "M12", 31, "default overflow is constrain");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/overflow-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/overflow-wrong-type.js
new file mode 100644
index 0000000000..3c022d6f6e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/overflow-wrong-type.js
@@ -0,0 +1,37 @@
+// |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.plainmonthday.from
+description: Type conversions for overflow option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-totemporalmonthday steps 3–4:
+ 3. If Type(_item_) is Object, then
+ ...
+ j. Return ? MonthDayFromFields(_calendar_, _fields_, _options_).
+ 4. Perform ? ToTemporalOverflow(_options_).
+ sec-temporal.plainmonthday.from steps 2–3:
+ 2. If Type(_item_) is Object and _item_ has an [[InitializedTemporalMonthDay]] internal slot, then
+ a. Perform ? ToTemporalOverflow(_options_).
+ b. Return ...
+ 3. Return ? ToTemporalMonthDay(_item_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const validValues = [
+ new Temporal.PlainMonthDay(5, 2),
+ { monthCode: "M05", day: 2 },
+ "05-02",
+];
+validValues.forEach((value) => TemporalHelpers.checkStringOptionWrongType("overflow", "constrain",
+ (overflow) => Temporal.PlainMonthDay.from(value, { overflow }),
+ (result, descr) => TemporalHelpers.assertPlainMonthDay(result, "M05", 2, descr),
+));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/overflow.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/overflow.js
new file mode 100644
index 0000000000..11168dad82
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/overflow.js
@@ -0,0 +1,41 @@
+// |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.plainmonthday.from
+description: Handling for overflow option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const validValues = [
+ new Temporal.PlainMonthDay(5, 2),
+ "05-02",
+];
+validValues.forEach((value) => {
+ const constrain = Temporal.PlainMonthDay.from(value, { overflow: "constrain" });
+ TemporalHelpers.assertPlainMonthDay(constrain, "M05", 2, "overflow is ignored: constrain");
+
+ const reject = Temporal.PlainMonthDay.from(value, { overflow: "reject" });
+ TemporalHelpers.assertPlainMonthDay(reject, "M05", 2, "overflow is ignored: reject");
+});
+
+const propertyBag1 = { year: 2000, month: 13, day: 34 };
+const result1 = Temporal.PlainMonthDay.from(propertyBag1, { overflow: "constrain" });
+TemporalHelpers.assertPlainMonthDay(result1, "M12", 31, "default overflow is constrain");
+assert.throws(RangeError, () => Temporal.PlainMonthDay.from(propertyBag1, { overflow: "reject" }),
+ "invalid property bag: reject");
+
+const propertyBag2 = { month: 1, day: 32 };
+const result2 = Temporal.PlainMonthDay.from(propertyBag2, { overflow: "constrain" });
+TemporalHelpers.assertPlainMonthDay(result2, "M01", 31, "default overflow is constrain");
+assert.throws(RangeError, () => Temporal.PlainMonthDay.from(propertyBag2, { overflow: "reject" }),
+ "invalid property bag: reject");
+
+assert.throws(RangeError, () => Temporal.PlainMonthDay.from("13-34", { overflow: "constrain" }),
+ "invalid ISO string: constrain");
+assert.throws(RangeError, () => Temporal.PlainMonthDay.from("13-34", { overflow: "reject" }),
+ "invalid ISO string: reject");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/prop-desc.js
new file mode 100644
index 0000000000..015eb6a725
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/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.plainmonthday.from
+description: The "from" property of Temporal.PlainMonthDay
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainMonthDay.from,
+ "function",
+ "`typeof PlainMonthDay.from` is `function`"
+);
+
+verifyProperty(Temporal.PlainMonthDay, "from", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/proto-in-calendar-fields.js
new file mode 100644
index 0000000000..31281e1772
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/proto-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |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.plainmonthday.prototype.from
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+
+assert.throws(RangeError, () => Temporal.PlainMonthDay.from(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/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/PlainMonthDay/from/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/subclassing-ignored.js
new file mode 100644
index 0000000000..1491ef7375
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/subclassing-ignored.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.plainmonthday.from
+description: The receiver is never called when calling from()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnoredStatic(
+ Temporal.PlainMonthDay,
+ "from",
+ ["05-02"],
+ (result) => TemporalHelpers.assertPlainMonthDay(result, "M05", 2),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/year-zero.js
new file mode 100644
index 0000000000..e9befb86e6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/year-zero.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.plainmonthday.from
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-08-24",
+ "-000000-08-24T15:43:27",
+ "-000000-08-24T15:43:27+01:00",
+ "-000000-08-24T15:43:27+00:00[UTC]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainMonthDay.from(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..9a06c133e5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/infinity-throws-rangeerror.js
@@ -0,0 +1,44 @@
+// |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.
+
+/*---
+description: Temporal.PlainMonthDay throws a RangeError if any numerical value is Infinity
+esid: sec-temporal.plainmonthday
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const isoCalendar = Temporal.Calendar.from('iso8601');
+
+assert.throws(RangeError, () => new Temporal.PlainMonthDay(Infinity, 1));
+assert.throws(RangeError, () => new Temporal.PlainMonthDay(1, Infinity));
+assert.throws(RangeError, () => new Temporal.PlainMonthDay(1, 1, isoCalendar, Infinity));
+
+const O = (primitiveValue, propertyName) => (calls) => TemporalHelpers.toPrimitiveObserver(calls, primitiveValue, propertyName);
+const tests = [
+ [
+ "infinite month",
+ [O(Infinity, "month"), O(1, "day"), () => "iso8601", O(1, "year")],
+ ["get month.valueOf", "call month.valueOf"]
+ ],
+ [
+ "infinite day",
+ [O(2, "month"), O(Infinity, "day"), () => "iso8601", O(1, "year")],
+ ["get month.valueOf", "call month.valueOf", "get day.valueOf", "call day.valueOf"]
+ ],
+ [
+ "infinite year",
+ [O(2, "month"), O(1, "day"), () => "iso8601", O(Infinity, "year")],
+ ["get month.valueOf", "call month.valueOf", "get day.valueOf", "call day.valueOf", "get year.valueOf", "call year.valueOf"]
+ ],
+];
+
+for (const [description, args, expected] of tests) {
+ const actual = [];
+ const args_ = args.map((o) => o(actual));
+ assert.throws(RangeError, () => new Temporal.PlainMonthDay(...args_), description);
+ assert.compareArray(actual, expected, `${description} order of operations`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/length.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/length.js
new file mode 100644
index 0000000000..6e0ad87b85
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/length.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.plainmonthday
+description: Temporal.PlainMonthDay.length is 2
+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.PlainMonthDay, "length", {
+ value: 2,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/missing-arguments.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/missing-arguments.js
new file mode 100644
index 0000000000..31432de575
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/missing-arguments.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.plainmonthday
+description: RangeError thrown after processing given args when invoked without all required args
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "get month.valueOf",
+ "call month.valueOf",
+];
+const actual = [];
+const args = [
+ TemporalHelpers.toPrimitiveObserver(actual, 1, "month"),
+];
+
+assert.throws(RangeError, () => new Temporal.PlainMonthDay(...args));
+assert.compareArray(actual, expected, "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/name.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/name.js
new file mode 100644
index 0000000000..97b592bc6c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/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.plainmonthday
+description: Temporal.PlainMonthDay.name is "PlainMonthDay"
+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.PlainMonthDay, "name", {
+ value: "PlainMonthDay",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/negative-infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/negative-infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..e58818719d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/negative-infinity-throws-rangeerror.js
@@ -0,0 +1,44 @@
+// |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.
+
+/*---
+description: Temporal.PlainMonthDay throws a RangeError if any numerical value is -Infinity
+esid: sec-temporal.plainmonthday
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const isoCalendar = Temporal.Calendar.from('iso8601');
+
+assert.throws(RangeError, () => new Temporal.PlainMonthDay(-Infinity, 1));
+assert.throws(RangeError, () => new Temporal.PlainMonthDay(1, -Infinity));
+assert.throws(RangeError, () => new Temporal.PlainMonthDay(1, 1, isoCalendar, -Infinity));
+
+const O = (primitiveValue, propertyName) => (calls) => TemporalHelpers.toPrimitiveObserver(calls, primitiveValue, propertyName);
+const tests = [
+ [
+ "infinite month",
+ [O(-Infinity, "month"), O(1, "day"), () => "iso8601", O(1, "year")],
+ ["get month.valueOf", "call month.valueOf"]
+ ],
+ [
+ "infinite day",
+ [O(2, "month"), O(-Infinity, "day"), () => "iso8601", O(1, "year")],
+ ["get month.valueOf", "call month.valueOf", "get day.valueOf", "call day.valueOf"]
+ ],
+ [
+ "infinite year",
+ [O(2, "month"), O(1, "day"), () => "iso8601", O(-Infinity, "year")],
+ ["get month.valueOf", "call month.valueOf", "get day.valueOf", "call day.valueOf", "get year.valueOf", "call year.valueOf"]
+ ],
+];
+
+for (const [description, args, expected] of tests) {
+ const actual = [];
+ const args_ = args.map((o) => o(actual));
+ assert.throws(RangeError, () => new Temporal.PlainMonthDay(...args_), description);
+ assert.compareArray(actual, expected, `${description} order of operations`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prop-desc.js
new file mode 100644
index 0000000000..188bbdd371
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/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.plainmonthday
+description: The "PlainMonthDay" property of Temporal
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainMonthDay,
+ "function",
+ "`typeof PlainMonthDay` is `function`"
+);
+
+verifyProperty(Temporal, "PlainMonthDay", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/calendarId/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/calendarId/branding.js
new file mode 100644
index 0000000000..74a0c2a4e9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/calendarId/branding.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-get-temporal.plainmonthday.prototype.calendarid
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const calendarId = Object.getOwnPropertyDescriptor(Temporal.PlainMonthDay.prototype, "calendarId").get;
+
+assert.sameValue(typeof calendarId, "function");
+
+assert.throws(TypeError, () => calendarId.call(undefined), "undefined");
+assert.throws(TypeError, () => calendarId.call(null), "null");
+assert.throws(TypeError, () => calendarId.call(true), "true");
+assert.throws(TypeError, () => calendarId.call(""), "empty string");
+assert.throws(TypeError, () => calendarId.call(Symbol()), "symbol");
+assert.throws(TypeError, () => calendarId.call(1), "1");
+assert.throws(TypeError, () => calendarId.call({}), "plain object");
+assert.throws(TypeError, () => calendarId.call(Temporal.PlainMonthDay), "Temporal.PlainMonthDay");
+assert.throws(TypeError, () => calendarId.call(Temporal.PlainMonthDay.prototype), "Temporal.PlainMonthDay.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/calendarId/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/calendarId/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/calendarId/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/calendarId/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/calendarId/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..1fe0649322
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/calendarId/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plainmonthday.prototype.calendarid
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const idOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "id");
+Object.defineProperty(Temporal.Calendar.prototype, "id", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("id should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainMonthDay(5, 2, "iso8601", 1972);
+instance.calendarId;
+
+Object.defineProperty(Temporal.Calendar.prototype, "id", idOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/calendarId/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/calendarId/prop-desc.js
new file mode 100644
index 0000000000..fb309473a0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/calendarId/prop-desc.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-get-temporal.plainmonthday.prototype.calendarid
+description: The "calendarId" property of Temporal.PlainMonthDay.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainMonthDay.prototype, "calendarId");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/calendarId/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/calendarId/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/calendarId/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/constructor.js
new file mode 100644
index 0000000000..e6dd5b96f0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/constructor.js
@@ -0,0 +1,20 @@
+// |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.plainmonthday.prototype.constructor
+description: Test for Temporal.PlainMonthDay.prototype.constructor.
+info: The initial value of Temporal.PlainMonthDay.prototype.constructor is %Temporal.PlainMonthDay%.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainMonthDay.prototype, "constructor", {
+ value: Temporal.PlainMonthDay,
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/day/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/day/basic.js
new file mode 100644
index 0000000000..14c591421e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/day/basic.js
@@ -0,0 +1,14 @@
+// |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-get-temporal.plainmonthday.prototype.day
+description: Basic tests for day().
+features: [Temporal]
+---*/
+
+const md = new Temporal.PlainMonthDay(1, 15);
+assert.sameValue(md.day, 15);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/day/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/day/branding.js
new file mode 100644
index 0000000000..b768a31458
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/day/branding.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-get-temporal.plainmonthday.prototype.day
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const day = Object.getOwnPropertyDescriptor(Temporal.PlainMonthDay.prototype, "day").get;
+
+assert.sameValue(typeof day, "function");
+
+assert.throws(TypeError, () => day.call(undefined), "undefined");
+assert.throws(TypeError, () => day.call(null), "null");
+assert.throws(TypeError, () => day.call(true), "true");
+assert.throws(TypeError, () => day.call(""), "empty string");
+assert.throws(TypeError, () => day.call(Symbol()), "symbol");
+assert.throws(TypeError, () => day.call(1), "1");
+assert.throws(TypeError, () => day.call({}), "plain object");
+assert.throws(TypeError, () => day.call(Temporal.PlainMonthDay), "Temporal.PlainMonthDay");
+assert.throws(TypeError, () => day.call(Temporal.PlainMonthDay.prototype), "Temporal.PlainMonthDay.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/day/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/day/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/day/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/day/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/day/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..d678a6ccb0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/day/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plainmonthday.prototype.day
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dayOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "day");
+Object.defineProperty(Temporal.Calendar.prototype, "day", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("day should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainMonthDay(5, 2, "iso8601", 1972);
+instance.day;
+
+Object.defineProperty(Temporal.Calendar.prototype, "day", dayOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/day/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/day/custom.js
new file mode 100644
index 0000000000..bf7cc6885f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/day/custom.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-get-temporal.plainmonthday.prototype.day
+description: Custom calendar tests for day().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ day(...args) {
+ ++calls;
+ assert.compareArray(args, [instance], "day arguments");
+ return 7;
+ }
+}
+
+const calendar = new CustomCalendar();
+const instance = new Temporal.PlainMonthDay(8, 25, calendar);
+const result = instance.day;
+assert.sameValue(result, 7, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/day/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/day/prop-desc.js
new file mode 100644
index 0000000000..ab9c5cda48
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/day/prop-desc.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-get-temporal.plainmonthday.prototype.day
+description: The "day" property of Temporal.PlainMonthDay.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainMonthDay.prototype, "day");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/day/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/day/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/day/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/day/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/day/validate-calendar-value.js
new file mode 100644
index 0000000000..ff8cf239f9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/day/validate-calendar-value.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-get-temporal.plainmonthday.prototype.day
+description: Validate result returned from calendar day() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [null, TypeError],
+ [false, TypeError],
+ [Infinity, RangeError],
+ [-Infinity, RangeError],
+ [NaN, RangeError],
+ [-7, RangeError],
+ [-0.1, RangeError],
+ ["string", TypeError],
+ [Symbol("foo"), TypeError],
+ [7n, TypeError],
+ [{}, TypeError],
+ [true, TypeError],
+ [7.1, RangeError],
+ ["7", TypeError],
+ ["7.5", TypeError],
+ [{valueOf() { return 7; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ day() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainMonthDay(12, 15, calendar);
+ assert.throws(error, () => instance.day, `${typeof result} ${String(result)} not converted to positive integer`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..67167d5a8f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-builtin-calendar-no-array-iteration.js
@@ -0,0 +1,24 @@
+// |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.plainmonthday.prototype.equals
+description: >
+ Calling the method with a property bag argument with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.PlainMonthDay(5, 2);
+const arg = { monthCode: "M11", day: 23, calendar: "iso8601" };
+instance.equals(arg);
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-number.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-number.js
new file mode 100644
index 0000000000..0edd0b0d6b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/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.plainmonthday.prototype.equals
+description: A number is invalid in place of an ISO string for Temporal.PlainMonthDay
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainMonthDay(11, 18);
+
+const numbers = [
+ 1,
+ 1118,
+ -1118,
+ 12345,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => instance.equals(arg),
+ `A number (${arg}) is not a valid ISO string for PlainMonthDay`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-propertybag-calendar-case-insensitive.js
new file mode 100644
index 0000000000..b5f54d1a21
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-propertybag-calendar-case-insensitive.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.plainmonthday.prototype.equals
+description: The calendar name is case-insensitive
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainMonthDay(11, 18);
+
+const calendar = "IsO8601";
+
+const arg = { monthCode: "M11", day: 18, calendar };
+const result = instance.equals(arg);
+assert.sameValue(result, true, "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-propertybag-calendar-leap-second.js
new file mode 100644
index 0000000000..5d016eff84
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-propertybag-calendar-leap-second.js
@@ -0,0 +1,23 @@
+// |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.plainmonthday.prototype.equals
+description: Leap second is a valid ISO string for a calendar in a property bag
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainMonthDay(11, 18);
+
+const calendar = "2016-12-31T23:59:60";
+
+const arg = { monthCode: "M11", day: 18, calendar };
+const result = instance.equals(arg);
+assert.sameValue(
+ result,
+ true,
+ "leap second is a valid ISO string for calendar"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-propertybag-calendar-number.js
new file mode 100644
index 0000000000..961d01e135
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-propertybag-calendar-number.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.plainmonthday.prototype.equals
+description: A number as calendar in a property bag is not accepted
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainMonthDay(11, 18);
+
+const numbers = [
+ 1,
+ 19970327,
+ -19970327,
+ 1234567890,
+];
+
+for (const calendar of numbers) {
+ const arg = { monthCode: "M11", day: 18, calendar };
+ assert.throws(
+ TypeError,
+ () => instance.equals(arg),
+ "Numbers cannot be used as a calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-propertybag-calendar-string.js
new file mode 100644
index 0000000000..e179d0c16a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-propertybag-calendar-string.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.plainmonthday.prototype.equals
+description: A calendar ID is valid input for Calendar
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainMonthDay(11, 18);
+
+const calendar = "iso8601";
+
+const arg = { monthCode: "M11", day: 18, calendar };
+const result = instance.equals(arg);
+assert.sameValue(result, true, `Calendar created from string "${calendar}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-propertybag-calendar-wrong-type.js
new file mode 100644
index 0000000000..d35d114766
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-propertybag-calendar-wrong-type.js
@@ -0,0 +1,46 @@
+// |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.plainmonthday.prototype.equals
+description: >
+ Appropriate error thrown when a calendar property from a property bag cannot
+ be converted to a calendar object or string
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.PlainMonthDay(5, 2);
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [calendar, description] of primitiveTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ typeof calendar === 'string' ? RangeError : TypeError,
+ () => instance.equals(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+ [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields()
+];
+
+for (const [calendar, description] of typeErrorTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(TypeError, () => instance.equals(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/PlainMonthDay/prototype/equals/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-propertybag-calendar-year-zero.js
new file mode 100644
index 0000000000..e3303ba1f5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-propertybag-calendar-year-zero.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.plainmonthday.prototype.equals
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T17:45",
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+01:00",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+const instance = new Temporal.PlainMonthDay(5, 2);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..5378f5e7d5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-string-calendar-annotation.js
@@ -0,0 +1,31 @@
+// |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.plainmonthday.prototype.equals
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["1976-05-02T15:23[u-ca=iso8601]", "without time zone"],
+ ["1976-05-02T15:23[UTC][u-ca=iso8601]", "with time zone"],
+ ["1976-05-02T15:23[!u-ca=iso8601]", "with ! and no time zone"],
+ ["1976-05-02T15:23[UTC][!u-ca=iso8601]", "with ! and time zone"],
+ ["1976-05-02T15:23[u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+const instance = new Temporal.PlainMonthDay(5, 2);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.equals(arg);
+
+ assert.sameValue(
+ result,
+ true,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..9759457bfe
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-string-critical-unknown-annotation.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.plainmonthday.prototype.equals
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "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.PlainMonthDay(5, 2);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..da63cbceda
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-string-date-with-utc-offset.js
@@ -0,0 +1,64 @@
+// |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.plainmonthday.prototype.equals
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainMonthDay(5, 2);
+
+const validStrings = [
+ "05-02[Asia/Katmandu]",
+ "05-02[!Asia/Katmandu]",
+ "05-02[u-ca=iso8601]",
+ "05-02[Asia/Tokyo][u-ca=iso8601]",
+ "--05-02[Asia/Katmandu]",
+ "--05-02[!Asia/Katmandu]",
+ "--05-02[u-ca=iso8601]",
+ "--05-02[Asia/Tokyo][u-ca=iso8601]",
+ "2000-05-02T00+00:00",
+ "2000-05-02T00+00:00[UTC]",
+ "2000-05-02T00-02:30[America/St_Johns]",
+];
+
+for (const arg of validStrings) {
+ const result = instance.equals(arg);
+
+ assert.sameValue(
+ result,
+ true,
+ `"${arg}" is a valid UTC offset with time for PlainMonthDay`
+ );
+}
+
+const invalidStrings = [
+ "09-15Z",
+ "09-15Z[UTC]",
+ "09-15+01:00",
+ "09-15+01:00[Europe/Vienna]",
+ "--09-15Z",
+ "--09-15Z[UTC]",
+ "--09-15+01:00",
+ "--09-15+01:00[Europe/Vienna]",
+ "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]",
+ "09-15[u-ca=chinese]",
+];
+
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ `"${arg}" UTC offset without time is not valid for PlainMonthDay`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-string-invalid.js
new file mode 100644
index 0000000000..43a0d89fe6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-string-invalid.js
@@ -0,0 +1,18 @@
+// |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.plainmonthday.prototype.equals
+description: An invalid ISO string is never supported
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainMonthDay(11, 18);
+
+for (const arg of TemporalHelpers.ISO.plainMonthDayStringsInvalid()) {
+ assert.throws(RangeError, () => instance.equals(arg), `"${arg}" is not a valid PlainMonthDay string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..d32bc2f4c2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/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.plainmonthday.prototype.equals
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[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.PlainMonthDay(5, 2);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..23be236348
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-string-multiple-time-zone.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.plainmonthday.prototype.equals
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "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.PlainMonthDay(5, 2);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-string-time-separators.js
new file mode 100644
index 0000000000..098c82328e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-string-time-separators.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.plainmonthday.prototype.equals
+description: Time separator in string argument can vary
+features: [Temporal]
+---*/
+
+const tests = [
+ ["1976-05-02T15:23", "uppercase T"],
+ ["1976-05-02t15:23", "lowercase T"],
+ ["1976-05-02 15:23", "space between date and time"],
+];
+
+const instance = new Temporal.PlainMonthDay(5, 2);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.equals(arg);
+
+ assert.sameValue(
+ result,
+ true,
+ `variant time separators (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..29cfc962a0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-string-time-zone-annotation.js
@@ -0,0 +1,34 @@
+// |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.plainmonthday.prototype.equals
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["1976-05-02T15:23[Asia/Kolkata]", "named, with no offset"],
+ ["1976-05-02T15:23[!Europe/Vienna]", "named, with ! and no offset"],
+ ["1976-05-02T15:23[+00:00]", "numeric, with no offset"],
+ ["1976-05-02T15:23[!-02:30]", "numeric, with ! and no offset"],
+ ["1976-05-02T15:23+00:00[UTC]", "named, with offset"],
+ ["1976-05-02T15:23+00:00[!Africa/Abidjan]", "named, with offset and !"],
+ ["1976-05-02T15:23+00:00[+01:00]", "numeric, with offset"],
+ ["1976-05-02T15:23+00:00[!-08:00]", "numeric, with offset and !"],
+];
+
+const instance = new Temporal.PlainMonthDay(5, 2);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.equals(arg);
+
+ assert.sameValue(
+ result,
+ true,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..72b91aa884
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-string-unknown-annotation.js
@@ -0,0 +1,31 @@
+// |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.plainmonthday.prototype.equals
+description: Various forms of unknown annotation
+features: [Temporal]
+---*/
+
+const tests = [
+ ["1976-05-02T15:23[foo=bar]", "alone"],
+ ["1976-05-02T15:23[UTC][foo=bar]", "with time zone"],
+ ["1976-05-02T15:23[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["1976-05-02T15:23[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["1976-05-02T15:23[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+const instance = new Temporal.PlainMonthDay(5, 2);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.equals(arg);
+
+ assert.sameValue(
+ result,
+ true,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..87c198701d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-string-with-utc-designator.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.plainmonthday.prototype.equals
+description: RangeError thrown if a string with UTC designator is used as a PlainMonthDay
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "2019-10-01T09:00:00Z",
+ "2019-10-01T09:00:00Z[UTC]",
+];
+const instance = new Temporal.PlainMonthDay(5, 2);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ "String with UTC designator should not be valid as a PlainMonthDay"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-string.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-string.js
new file mode 100644
index 0000000000..ad0d64601f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-string.js
@@ -0,0 +1,18 @@
+// |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.plainmonthday.prototype.equals
+description: A string argument is parsed into a PlainMonthDay
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainMonthDay(10, 1);
+for (const arg of TemporalHelpers.ISO.plainMonthDayStringsValid()) {
+ const result = instance.equals(arg);
+ assert.sameValue(result, true, `"${arg}" is a valid PlainMonthDay string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-wrong-type.js
new file mode 100644
index 0000000000..90cbf16040
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/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.plainmonthday.prototype.equals
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainMonthDay
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.PlainMonthDay(5, 2);
+
+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.equals(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.PlainMonthDay, "Temporal.PlainMonthDay, object"],
+ [Temporal.PlainMonthDay.prototype, "Temporal.PlainMonthDay.prototype, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.equals(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/PlainMonthDay/prototype/equals/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/basic.js
new file mode 100644
index 0000000000..d79e487636
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/basic.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.plainmonthday.prototype.equals
+description: Basic tests for equals()
+features: [Temporal]
+---*/
+
+const md1 = Temporal.PlainMonthDay.from("01-22");
+const md2 = Temporal.PlainMonthDay.from("12-15");
+assert(md1.equals(md1), "same object");
+assert.sameValue(md1.equals(md2), false, "different object");
+
+assert(md1.equals("01-22"), "same string");
+assert.sameValue(md2.equals("01-22"), false, "different string");
+
+assert(md1.equals({ month: 1, day: 22 }), "same property bag");
+assert.sameValue(md2.equals({ month: 1, day: 22 }), false, "different property bag");
+
+assert.throws(TypeError, () => md1.equals({ month: 1 }), "missing field in property bag");
+
+const mdYear1 = new Temporal.PlainMonthDay(1, 1, undefined, 1972);
+const mdYear2 = new Temporal.PlainMonthDay(1, 1, undefined, 2000);
+assert.sameValue(mdYear1.equals(mdYear2), false, "different reference years");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/branding.js
new file mode 100644
index 0000000000..f89a12072c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/branding.js
@@ -0,0 +1,27 @@
+// |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.plainmonthday.prototype.equals
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const equals = Temporal.PlainMonthDay.prototype.equals;
+
+assert.sameValue(typeof equals, "function");
+
+const args = [new Temporal.PlainMonthDay(5, 2)];
+
+assert.throws(TypeError, () => equals.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => equals.apply(null, args), "null");
+assert.throws(TypeError, () => equals.apply(true, args), "true");
+assert.throws(TypeError, () => equals.apply("", args), "empty string");
+assert.throws(TypeError, () => equals.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => equals.apply(1, args), "1");
+assert.throws(TypeError, () => equals.apply({}, args), "plain object");
+assert.throws(TypeError, () => equals.apply(Temporal.PlainMonthDay, args), "Temporal.PlainMonthDay");
+assert.throws(TypeError, () => equals.apply(Temporal.PlainMonthDay.prototype, args), "Temporal.PlainMonthDay.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..20adcbcc08
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plainmonthday.prototype.equals
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const idOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "id");
+Object.defineProperty(Temporal.Calendar.prototype, "id", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("id should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainMonthDay(5, 2, "iso8601", 1972);
+instance.equals(new Temporal.PlainMonthDay(5, 2));
+
+Object.defineProperty(Temporal.Calendar.prototype, "id", idOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/builtin.js
new file mode 100644
index 0000000000..933acb1ba0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/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.plainmonthday.prototype.equals
+description: >
+ Tests that Temporal.PlainMonthDay.prototype.equals
+ 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.PlainMonthDay.prototype.equals),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainMonthDay.prototype.equals),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainMonthDay.prototype.equals),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainMonthDay.prototype.equals.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/calendar-datefromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..79a43e3676
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/calendar-datefromfields-called-with-null-prototype-fields.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.plainmonthday.prototype.equals
+description: >
+ Calendar.monthDayFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.PlainMonthDay(5, 2);
+const arg = { monthCode: "M05", day: 2, calendar };
+instance.equals(arg);
+assert.sameValue(calendar.monthDayFromFieldsCallCount, 1, "monthDayFromFields should be called on the property bag's calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/calendar-fields-iterable.js
new file mode 100644
index 0000000000..4d0ed1c81d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/calendar-fields-iterable.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.plainmonthday.prototype.equals
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plainmonthday.prototype.equals step 3:
+ 3. Set _other_ to ? ToTemporalMonthDay(_other_).
+ sec-temporal-totemporalmonthday step 2.f:
+ f. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const date = new Temporal.PlainMonthDay(5, 2, calendar1);
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+date.equals({ monthCode: "M06", day: 2, calendar: calendar2 });
+
+assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/calendar-monthdayfromfields-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/calendar-monthdayfromfields-called-with-options-undefined.js
new file mode 100644
index 0000000000..755b1d76a5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/calendar-monthdayfromfields-called-with-options-undefined.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.plainmonthday.prototype.equals
+description: >
+ Calendar.monthDayFromFields method is called with undefined as the options
+ value when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
+const instance = new Temporal.PlainMonthDay(5, 2, calendar);
+instance.equals({ monthCode: "M05", day: 3, calendar });
+assert.sameValue(calendar.monthDayFromFieldsCallCount, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/calendar-temporal-object.js
new file mode 100644
index 0000000000..49c57643d0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/calendar-temporal-object.js
@@ -0,0 +1,29 @@
+// |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.plainmonthday.prototype.equals
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plainmonthday.prototype.equals step 3:
+ 3. Set _other_ to ? ToTemporalMonthDay(_other_).
+ sec-temporal-totemporalmonthday step 3.e:
+ e. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const monthday = new Temporal.PlainMonthDay(5, 2, temporalObject);
+ monthday.equals({ monthCode: "M06", day: 2, calendar: temporalObject });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/calendars.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/calendars.js
new file mode 100644
index 0000000000..8061c9fd53
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/calendars.js
@@ -0,0 +1,55 @@
+// |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.plainmonthday.prototype.equals
+description: Basic tests for equals() calendar handling
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "get calendar a.id",
+ "get calendar b.id",
+];
+const actual = [];
+const calendar = (id) => {
+ const c = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+ };
+ TemporalHelpers.observeProperty(actual, c, "id", id, `calendar ${id}`);
+ return c;
+};
+
+const mdA = new Temporal.PlainMonthDay(2, 7, calendar("a"));
+const mdB = new Temporal.PlainMonthDay(2, 7, calendar("b"));
+const mdC = new Temporal.PlainMonthDay(2, 7, calendar("c"), 1974);
+actual.splice(0); // disregard the HasProperty checks done in the constructor
+
+assert.sameValue(mdA.equals(mdC), false, "different year");
+assert.compareArray(actual, [], "Should not check calendar");
+
+assert.sameValue(mdA.equals(mdB), false, "different calendar");
+assert.compareArray(actual, expected, "Should check calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..762ba80f78
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/constructor-in-calendar-fields.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.plainmonthday.prototype.equals
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.PlainMonthDay(5, 2, calendar);
+
+assert.throws(RangeError, () => instance.equals(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/duplicate-calendar-fields.js
new file mode 100644
index 0000000000..dd58ceb4c5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/duplicate-calendar-fields.js
@@ -0,0 +1,19 @@
+// |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.plainmonthday.prototype.equals
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['day'], ['month'], ['monthCode'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar };
+ const instance = new Temporal.PlainMonthDay(5, 2, calendar);
+
+ assert.throws(RangeError, () => instance.equals(arg));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..a57a03b825
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/infinity-throws-rangeerror.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.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plainmonthday.prototype.equals
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainMonthDay(5, 2);
+const base = { year: 2000, month: 5, day: 2 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.equals({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.equals({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/leap-second.js
new file mode 100644
index 0000000000..6fbc596406
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/leap-second.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.plainmonthday.prototype.equals
+description: Leap second is a valid ISO string for PlainMonthDay
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainMonthDay(12, 31);
+
+let arg = "2016-12-31T23:59:60";
+const result1 = instance.equals(arg);
+assert.sameValue(
+ result1,
+ true,
+ "leap second is a valid ISO string for PlainMonthDay"
+);
+
+arg = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+const result2 = instance.equals(arg);
+assert.sameValue(
+ result2,
+ true,
+ "second: 60 is ignored in property bag for PlainMonthDay"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/length.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/length.js
new file mode 100644
index 0000000000..04d789a98a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/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.plainmonthday.prototype.equals
+description: Temporal.PlainMonthDay.prototype.equals.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.PlainMonthDay.prototype.equals, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/name.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/name.js
new file mode 100644
index 0000000000..57f153ee91
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/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.plainmonthday.prototype.equals
+description: Temporal.PlainMonthDay.prototype.equals.name is "equals".
+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.PlainMonthDay.prototype.equals, "name", {
+ value: "equals",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/not-a-constructor.js
new file mode 100644
index 0000000000..6ca09f5c77
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/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.plainmonthday.prototype.equals
+description: >
+ Temporal.PlainMonthDay.prototype.equals 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.PlainMonthDay.prototype.equals();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainMonthDay.prototype.equals), false,
+ "isConstructor(Temporal.PlainMonthDay.prototype.equals)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/prop-desc.js
new file mode 100644
index 0000000000..52ba3afee2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/prop-desc.js
@@ -0,0 +1,24 @@
+// |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.plainmonthday.prototype.equals
+description: The "equals" property of Temporal.PlainMonthDay.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainMonthDay.prototype.equals,
+ "function",
+ "`typeof PlainMonthDay.prototype.equals` is `function`"
+);
+
+verifyProperty(Temporal.PlainMonthDay.prototype, "equals", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/proto-in-calendar-fields.js
new file mode 100644
index 0000000000..6530f40ac1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/proto-in-calendar-fields.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.plainmonthday.prototype.equals
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.PlainMonthDay(5, 2, calendar);
+
+assert.throws(RangeError, () => instance.equals(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/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/PlainMonthDay/prototype/equals/year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/year-zero.js
new file mode 100644
index 0000000000..bfe6d361df
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/year-zero.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.plainmonthday.prototype.equals
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-08-24",
+ "-000000-08-24T15:43:27",
+ "-000000-08-24T15:43:27+01:00",
+ "-000000-08-24T15:43:27+00:00[UTC]",
+];
+const instance = new Temporal.PlainMonthDay(5, 2);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getCalendar/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getCalendar/branding.js
new file mode 100644
index 0000000000..fa1039773b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getCalendar/branding.js
@@ -0,0 +1,25 @@
+// |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.plainmonthday.prototype.getcalendar
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const getCalendar = Temporal.PlainMonthDay.prototype.getCalendar;
+
+assert.sameValue(typeof getCalendar, "function");
+
+assert.throws(TypeError, () => getCalendar.call(undefined), "undefined");
+assert.throws(TypeError, () => getCalendar.call(null), "null");
+assert.throws(TypeError, () => getCalendar.call(true), "true");
+assert.throws(TypeError, () => getCalendar.call(""), "empty string");
+assert.throws(TypeError, () => getCalendar.call(Symbol()), "symbol");
+assert.throws(TypeError, () => getCalendar.call(1), "1");
+assert.throws(TypeError, () => getCalendar.call({}), "plain object");
+assert.throws(TypeError, () => getCalendar.call(Temporal.PlainMonthDay), "Temporal.PlainMonthDay");
+assert.throws(TypeError, () => getCalendar.call(Temporal.PlainMonthDay.prototype), "Temporal.PlainMonthDay.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getCalendar/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getCalendar/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getCalendar/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getCalendar/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getCalendar/builtin.js
new file mode 100644
index 0000000000..eaf4f5272a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getCalendar/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.plainmonthday.prototype.getcalendar
+description: >
+ Tests that Temporal.PlainMonthDay.prototype.getCalendar
+ 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.PlainMonthDay.prototype.getCalendar),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainMonthDay.prototype.getCalendar),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainMonthDay.prototype.getCalendar),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainMonthDay.prototype.getCalendar.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getCalendar/length.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getCalendar/length.js
new file mode 100644
index 0000000000..089c7aef96
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getCalendar/length.js
@@ -0,0 +1,28 @@
+// |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.plainmonthday.prototype.getcalendar
+description: Temporal.PlainMonthDay.prototype.getCalendar.length is 0
+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.PlainMonthDay.prototype.getCalendar, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getCalendar/name.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getCalendar/name.js
new file mode 100644
index 0000000000..00adc34b54
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getCalendar/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.plainmonthday.prototype.getcalendar
+description: Temporal.PlainMonthDay.prototype.getCalendar.name is "getCalendar".
+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.PlainMonthDay.prototype.getCalendar, "name", {
+ value: "getCalendar",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getCalendar/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getCalendar/not-a-constructor.js
new file mode 100644
index 0000000000..e33dee0c53
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getCalendar/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.plainmonthday.prototype.getcalendar
+description: >
+ Temporal.PlainMonthDay.prototype.getCalendar 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.PlainMonthDay.prototype.getCalendar();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainMonthDay.prototype.getCalendar), false,
+ "isConstructor(Temporal.PlainMonthDay.prototype.getCalendar)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getCalendar/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getCalendar/prop-desc.js
new file mode 100644
index 0000000000..35510b6103
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getCalendar/prop-desc.js
@@ -0,0 +1,24 @@
+// |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.plainmonthday.prototype.getcalendar
+description: The "getCalendar" property of Temporal.PlainMonthDay.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainMonthDay.prototype.getCalendar,
+ "function",
+ "`typeof PlainMonthDay.prototype.getCalendar` is `function`"
+);
+
+verifyProperty(Temporal.PlainMonthDay.prototype, "getCalendar", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getCalendar/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getCalendar/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getCalendar/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/PlainMonthDay/prototype/getISOFields/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/branding.js
new file mode 100644
index 0000000000..ecae88abce
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/branding.js
@@ -0,0 +1,25 @@
+// |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.plainmonthday.prototype.getisofields
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const getISOFields = Temporal.PlainMonthDay.prototype.getISOFields;
+
+assert.sameValue(typeof getISOFields, "function");
+
+assert.throws(TypeError, () => getISOFields.call(undefined), "undefined");
+assert.throws(TypeError, () => getISOFields.call(null), "null");
+assert.throws(TypeError, () => getISOFields.call(true), "true");
+assert.throws(TypeError, () => getISOFields.call(""), "empty string");
+assert.throws(TypeError, () => getISOFields.call(Symbol()), "symbol");
+assert.throws(TypeError, () => getISOFields.call(1), "1");
+assert.throws(TypeError, () => getISOFields.call({}), "plain object");
+assert.throws(TypeError, () => getISOFields.call(Temporal.PlainMonthDay), "Temporal.PlainMonthDay");
+assert.throws(TypeError, () => getISOFields.call(Temporal.PlainMonthDay.prototype), "Temporal.PlainMonthDay.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/builtin.js
new file mode 100644
index 0000000000..b59a73f8fe
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/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.plainmonthday.prototype.getisofields
+description: >
+ Tests that Temporal.PlainMonthDay.prototype.getISOFields
+ 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.PlainMonthDay.prototype.getISOFields),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainMonthDay.prototype.getISOFields),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainMonthDay.prototype.getISOFields),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainMonthDay.prototype.getISOFields.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/custom.js
new file mode 100644
index 0000000000..ecba8614f1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/custom.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.plainmonthday.prototype.getisofields
+description: getISOFields does not call into user code.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarThrowEverything();
+const instance = new Temporal.PlainMonthDay(5, 2, calendar);
+const result = instance.getISOFields();
+
+assert.sameValue(result.isoYear, 1972, "isoYear result");
+assert.sameValue(result.isoMonth, 5, "isoMonth result");
+assert.sameValue(result.isoDay, 2, "isoDay result");
+assert.sameValue(result.calendar, calendar, "calendar result");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/field-names.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/field-names.js
new file mode 100644
index 0000000000..c481143d62
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/field-names.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.plainmonthday.prototype.getisofields
+description: Correct field names on the object returned from getISOFields
+features: [Temporal]
+---*/
+
+const md = new Temporal.PlainMonthDay(5, 2);
+
+const result = md.getISOFields();
+assert.sameValue(result.isoYear, 1972, "isoYear result");
+assert.sameValue(result.isoMonth, 5, "isoMonth result");
+assert.sameValue(result.isoDay, 2, "isoDay result");
+assert.sameValue(result.calendar, "iso8601", "calendar result");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/field-prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/field-prop-desc.js
new file mode 100644
index 0000000000..0661e29f4a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/field-prop-desc.js
@@ -0,0 +1,30 @@
+// |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.plainmonthday.prototype.getisofields
+description: Properties on the returned object have the correct descriptor
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "calendar",
+ "isoDay",
+ "isoMonth",
+ "isoYear",
+];
+
+const md = new Temporal.PlainMonthDay(5, 2);
+const result = md.getISOFields();
+
+for (const property of expected) {
+ verifyProperty(result, property, {
+ writable: true,
+ enumerable: true,
+ configurable: true,
+ });
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/field-traversal-order.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/field-traversal-order.js
new file mode 100644
index 0000000000..57addcf0dc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/field-traversal-order.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.plainmonthday.prototype.getisofields
+description: Properties added in correct order to object returned from getISOFields
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "calendar",
+ "isoDay",
+ "isoMonth",
+ "isoYear",
+];
+
+const md = new Temporal.PlainMonthDay(5, 2);
+const result = md.getISOFields();
+
+assert.compareArray(Object.keys(result), expected);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/length.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/length.js
new file mode 100644
index 0000000000..f354e5eba6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/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.plainmonthday.prototype.getisofields
+description: Temporal.PlainMonthDay.prototype.getISOFields.length is 0
+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.PlainMonthDay.prototype.getISOFields, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/name.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/name.js
new file mode 100644
index 0000000000..531f0e1f29
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/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.plainmonthday.prototype.getisofields
+description: Temporal.PlainMonthDay.prototype.getISOFields.name is "getISOFields".
+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.PlainMonthDay.prototype.getISOFields, "name", {
+ value: "getISOFields",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/not-a-constructor.js
new file mode 100644
index 0000000000..476aef139d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/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.plainmonthday.prototype.getisofields
+description: >
+ Temporal.PlainMonthDay.prototype.getISOFields 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.PlainMonthDay.prototype.getISOFields();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainMonthDay.prototype.getISOFields), false,
+ "isConstructor(Temporal.PlainMonthDay.prototype.getISOFields)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/prop-desc.js
new file mode 100644
index 0000000000..972010131e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/prop-desc.js
@@ -0,0 +1,24 @@
+// |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.plainmonthday.prototype.getisofields
+description: The "getISOFields" property of Temporal.PlainMonthDay.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainMonthDay.prototype.getISOFields,
+ "function",
+ "`typeof PlainMonthDay.prototype.getISOFields` is `function`"
+);
+
+verifyProperty(Temporal.PlainMonthDay.prototype, "getISOFields", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/prototype.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/prototype.js
new file mode 100644
index 0000000000..f3708b4c11
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/prototype.js
@@ -0,0 +1,15 @@
+// |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.plainmonthday.prototype.getisofields
+description: Correct prototype on the object returned from getISOFields
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainMonthDay(5, 2);
+const result = instance.getISOFields();
+assert.sameValue(Object.getPrototypeOf(result), Object.prototype, "prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/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/PlainMonthDay/prototype/month/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/month/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/month/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/month/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/month/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/month/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/month/unsupported.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/month/unsupported.js
new file mode 100644
index 0000000000..1ec10c467c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/month/unsupported.js
@@ -0,0 +1,15 @@
+// |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-get-temporal.plainmonthday.prototype.monthcode
+description: There is no 'month' property on Temporal.PlainMonthDay
+features: [Temporal]
+---*/
+
+const md = new Temporal.PlainMonthDay(1, 15);
+assert.sameValue(md.month, undefined);
+assert.sameValue("month" in md, false);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/monthCode/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/monthCode/basic.js
new file mode 100644
index 0000000000..5734f9d292
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/monthCode/basic.js
@@ -0,0 +1,14 @@
+// |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-get-temporal.plainmonthday.prototype.monthcode
+description: Basic tests for monthCode().
+features: [Temporal]
+---*/
+
+const md = new Temporal.PlainMonthDay(1, 15);
+assert.sameValue(md.monthCode, "M01");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/monthCode/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/monthCode/branding.js
new file mode 100644
index 0000000000..71d03f49a4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/monthCode/branding.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-get-temporal.plainmonthday.prototype.monthcode
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const monthCode = Object.getOwnPropertyDescriptor(Temporal.PlainMonthDay.prototype, "monthCode").get;
+
+assert.sameValue(typeof monthCode, "function");
+
+assert.throws(TypeError, () => monthCode.call(undefined), "undefined");
+assert.throws(TypeError, () => monthCode.call(null), "null");
+assert.throws(TypeError, () => monthCode.call(true), "true");
+assert.throws(TypeError, () => monthCode.call(""), "empty string");
+assert.throws(TypeError, () => monthCode.call(Symbol()), "symbol");
+assert.throws(TypeError, () => monthCode.call(1), "1");
+assert.throws(TypeError, () => monthCode.call({}), "plain object");
+assert.throws(TypeError, () => monthCode.call(Temporal.PlainMonthDay), "Temporal.PlainMonthDay");
+assert.throws(TypeError, () => monthCode.call(Temporal.PlainMonthDay.prototype), "Temporal.PlainMonthDay.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/monthCode/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/monthCode/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/monthCode/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/monthCode/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/monthCode/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..a8d85ef241
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/monthCode/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plainmonthday.prototype.monthcode
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const monthCodeOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "monthCode");
+Object.defineProperty(Temporal.Calendar.prototype, "monthCode", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("monthCode should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainMonthDay(5, 2, "iso8601", 1972);
+instance.monthCode;
+
+Object.defineProperty(Temporal.Calendar.prototype, "monthCode", monthCodeOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/monthCode/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/monthCode/custom.js
new file mode 100644
index 0000000000..ad54832ccd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/monthCode/custom.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-get-temporal.plainmonthday.prototype.monthcode
+description: Custom calendar tests for monthCode().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ monthCode(...args) {
+ ++calls;
+ assert.compareArray(args, [instance], "monthCode arguments");
+ return "M01";
+ }
+}
+
+const calendar = new CustomCalendar();
+const instance = new Temporal.PlainMonthDay(8, 25, calendar);
+const result = instance.monthCode;
+assert.sameValue(result, "M01", "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/monthCode/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/monthCode/prop-desc.js
new file mode 100644
index 0000000000..447cb731af
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/monthCode/prop-desc.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-get-temporal.plainmonthday.prototype.monthcode
+description: The "monthCode" property of Temporal.PlainMonthDay.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainMonthDay.prototype, "monthCode");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/monthCode/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/monthCode/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/monthCode/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/monthCode/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/monthCode/validate-calendar-value.js
new file mode 100644
index 0000000000..a21782ab1a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/monthCode/validate-calendar-value.js
@@ -0,0 +1,31 @@
+// |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-get-temporal.plainmonthday.prototype.monthcode
+description: Validate result returned from calendar monthCode() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [Symbol("foo"), TypeError],
+ [null, TypeError],
+ [true, TypeError],
+ [false, TypeError],
+ [7.1, TypeError],
+ [{toString() { return "M01"; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ monthCode() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainMonthDay(12, 15, calendar);
+ assert.throws(error, () => instance.monthCode, `${typeof result} ${String(result)} not converted to string`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/prop-desc.js
new file mode 100644
index 0000000000..c3502f6633
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/prop-desc.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-plainmonthday-prototype
+description: The "prototype" property of Temporal.PlainMonthDay
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(typeof Temporal.PlainMonthDay.prototype, "object");
+assert.notSameValue(Temporal.PlainMonthDay.prototype, null);
+
+verifyProperty(Temporal.PlainMonthDay, "prototype", {
+ writable: false,
+ enumerable: false,
+ configurable: false,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toJSON/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toJSON/branding.js
new file mode 100644
index 0000000000..7af9f87d07
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toJSON/branding.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.plainmonthday.prototype.tojson
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toJSON = Temporal.PlainMonthDay.prototype.toJSON;
+
+assert.sameValue(typeof toJSON, "function");
+
+assert.throws(TypeError, () => toJSON.call(undefined), "undefined");
+assert.throws(TypeError, () => toJSON.call(null), "null");
+assert.throws(TypeError, () => toJSON.call(true), "true");
+assert.throws(TypeError, () => toJSON.call(""), "empty string");
+assert.throws(TypeError, () => toJSON.call(Symbol()), "symbol");
+assert.throws(TypeError, () => toJSON.call(1), "1");
+assert.throws(TypeError, () => toJSON.call({}), "plain object");
+assert.throws(TypeError, () => toJSON.call(Temporal.PlainMonthDay), "Temporal.PlainMonthDay");
+assert.throws(TypeError, () => toJSON.call(Temporal.PlainMonthDay.prototype), "Temporal.PlainMonthDay.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toJSON/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toJSON/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toJSON/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toJSON/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toJSON/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..ad02a34dbc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toJSON/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plainmonthday.prototype.tojson
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const idOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "id");
+Object.defineProperty(Temporal.Calendar.prototype, "id", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("id should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainMonthDay(5, 2, "iso8601", 1972);
+instance.toJSON();
+
+Object.defineProperty(Temporal.Calendar.prototype, "id", idOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toJSON/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toJSON/builtin.js
new file mode 100644
index 0000000000..5a44848034
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toJSON/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.plainmonthday.prototype.tojson
+description: >
+ Tests that Temporal.PlainMonthDay.prototype.toJSON
+ 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.PlainMonthDay.prototype.toJSON),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainMonthDay.prototype.toJSON),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainMonthDay.prototype.toJSON),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainMonthDay.prototype.toJSON.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toJSON/calendarname.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toJSON/calendarname.js
new file mode 100644
index 0000000000..1993c9aeb8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toJSON/calendarname.js
@@ -0,0 +1,54 @@
+// |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.plainmonthday.protoype.tojson
+description: toJSON doesn't take calendarName into account.
+features: [Temporal]
+---*/
+
+const calendarMethods = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+
+const tests = [
+ [[], "05-02"],
+ [[{ id: "custom", ...calendarMethods }], "1972-05-02[u-ca=custom]"],
+ [[{ id: "iso8601", ...calendarMethods }], "05-02"],
+ [[{ id: "ISO8601", ...calendarMethods }], "1972-05-02[u-ca=ISO8601]"],
+ [[{ id: "\u0131so8601", ...calendarMethods }], "1972-05-02[u-ca=\u0131so8601]"], // dotless i
+];
+const options = {
+ get calendarName() {
+ TemporalHelpers.assertUnreachable("calendarName should not be accessed");
+ return "";
+ }
+};
+
+for (const [args, expected] of tests) {
+ const monthday = new Temporal.PlainMonthDay(5, 2, ...args);
+ const result = monthday.toJSON(options);
+ assert.sameValue(result, expected);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toJSON/length.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toJSON/length.js
new file mode 100644
index 0000000000..40bb95be1e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toJSON/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.plainmonthday.prototype.tojson
+description: Temporal.PlainMonthDay.prototype.toJSON.length is 0
+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.PlainMonthDay.prototype.toJSON, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toJSON/name.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toJSON/name.js
new file mode 100644
index 0000000000..99f0a89d16
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toJSON/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.plainmonthday.prototype.tojson
+description: Temporal.PlainMonthDay.prototype.toJSON.name is "toJSON".
+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.PlainMonthDay.prototype.toJSON, "name", {
+ value: "toJSON",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toJSON/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toJSON/not-a-constructor.js
new file mode 100644
index 0000000000..22a801c92c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toJSON/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.plainmonthday.prototype.tojson
+description: >
+ Temporal.PlainMonthDay.prototype.toJSON 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.PlainMonthDay.prototype.toJSON();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainMonthDay.prototype.toJSON), false,
+ "isConstructor(Temporal.PlainMonthDay.prototype.toJSON)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toJSON/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toJSON/prop-desc.js
new file mode 100644
index 0000000000..73fafdab26
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toJSON/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.plainmonthday.prototype.tojson
+description: The "toJSON" property of Temporal.PlainMonthDay.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainMonthDay.prototype.toJSON,
+ "function",
+ "`typeof PlainMonthDay.prototype.toJSON` is `function`"
+);
+
+verifyProperty(Temporal.PlainMonthDay.prototype, "toJSON", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toJSON/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toJSON/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toJSON/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/PlainMonthDay/prototype/toJSON/year-format.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toJSON/year-format.js
new file mode 100644
index 0000000000..9a9cca8e1d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toJSON/year-format.js
@@ -0,0 +1,58 @@
+// |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.plainmonthday.prototype.tojson
+description: Verify that the year is appropriately formatted as 4 or 6 digits
+features: [Temporal]
+---*/
+
+// For PlainMonthDay, the ISO reference year is only present in the string if
+// the calendar is not ISO 8601
+class NotISO extends Temporal.Calendar {
+ constructor() { super("iso8601"); }
+ get id() { return "not-iso"; }
+}
+const calendar = new NotISO();
+
+let instance = new Temporal.PlainMonthDay(12, 3, calendar, -100000);
+assert.sameValue(instance.toJSON(), "-100000-12-03[u-ca=not-iso]", "large negative year formatted as 6-digit");
+
+instance = new Temporal.PlainMonthDay(4, 5, calendar, -10000);
+assert.sameValue(instance.toJSON(), "-010000-04-05[u-ca=not-iso]", "smallest 5-digit negative year formatted as 6-digit");
+
+instance = new Temporal.PlainMonthDay(6, 7, calendar, -9999);
+assert.sameValue(instance.toJSON(), "-009999-06-07[u-ca=not-iso]", "largest 4-digit negative year formatted as 6-digit");
+
+instance = new Temporal.PlainMonthDay(8, 9, calendar, -1000);
+assert.sameValue(instance.toJSON(), "-001000-08-09[u-ca=not-iso]", "smallest 4-digit negative year formatted as 6-digit");
+
+instance = new Temporal.PlainMonthDay(10, 9, calendar, -999);
+assert.sameValue(instance.toJSON(), "-000999-10-09[u-ca=not-iso]", "largest 3-digit negative year formatted as 6-digit");
+
+instance = new Temporal.PlainMonthDay(8, 7, calendar, -1);
+assert.sameValue(instance.toJSON(), "-000001-08-07[u-ca=not-iso]", "year -1 formatted as 6-digit");
+
+instance = new Temporal.PlainMonthDay(6, 5, calendar, 0);
+assert.sameValue(instance.toJSON(), "0000-06-05[u-ca=not-iso]", "year 0 formatted as 4-digit");
+
+instance = new Temporal.PlainMonthDay(4, 3, calendar, 1);
+assert.sameValue(instance.toJSON(), "0001-04-03[u-ca=not-iso]", "year 1 formatted as 4-digit");
+
+instance = new Temporal.PlainMonthDay(2, 10, calendar, 999);
+assert.sameValue(instance.toJSON(), "0999-02-10[u-ca=not-iso]", "largest 3-digit positive year formatted as 4-digit");
+
+instance = new Temporal.PlainMonthDay(1, 23, calendar, 1000);
+assert.sameValue(instance.toJSON(), "1000-01-23[u-ca=not-iso]", "smallest 4-digit positive year formatted as 4-digit");
+
+instance = new Temporal.PlainMonthDay(4, 5, calendar, 9999);
+assert.sameValue(instance.toJSON(), "9999-04-05[u-ca=not-iso]", "largest 4-digit positive year formatted as 4-digit");
+
+instance = new Temporal.PlainMonthDay(6, 7, calendar, 10000);
+assert.sameValue(instance.toJSON(), "+010000-06-07[u-ca=not-iso]", "smallest 5-digit positive year formatted as 6-digit");
+
+instance = new Temporal.PlainMonthDay(8, 9, calendar, 100000);
+assert.sameValue(instance.toJSON(), "+100000-08-09[u-ca=not-iso]", "large positive year formatted as 6-digit");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toLocaleString/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toLocaleString/branding.js
new file mode 100644
index 0000000000..5d7b968e7c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toLocaleString/branding.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.plainmonthday.prototype.tolocalestring
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toLocaleString = Temporal.PlainMonthDay.prototype.toLocaleString;
+
+assert.sameValue(typeof toLocaleString, "function");
+
+assert.throws(TypeError, () => toLocaleString.call(undefined), "undefined");
+assert.throws(TypeError, () => toLocaleString.call(null), "null");
+assert.throws(TypeError, () => toLocaleString.call(true), "true");
+assert.throws(TypeError, () => toLocaleString.call(""), "empty string");
+assert.throws(TypeError, () => toLocaleString.call(Symbol()), "symbol");
+assert.throws(TypeError, () => toLocaleString.call(1), "1");
+assert.throws(TypeError, () => toLocaleString.call({}), "plain object");
+assert.throws(TypeError, () => toLocaleString.call(Temporal.PlainMonthDay), "Temporal.PlainMonthDay");
+assert.throws(TypeError, () => toLocaleString.call(Temporal.PlainMonthDay.prototype), "Temporal.PlainMonthDay.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toLocaleString/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toLocaleString/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toLocaleString/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toLocaleString/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toLocaleString/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..90bc4675de
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toLocaleString/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plainmonthday.prototype.tolocalestring
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const idOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "id");
+Object.defineProperty(Temporal.Calendar.prototype, "id", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("id should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainMonthDay(5, 2, "iso8601", 1972);
+instance.toLocaleString(undefined, { calendar: "iso8601" });
+
+Object.defineProperty(Temporal.Calendar.prototype, "id", idOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toLocaleString/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toLocaleString/builtin.js
new file mode 100644
index 0000000000..8ecfbca8e5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toLocaleString/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.plainmonthday.prototype.tolocalestring
+description: >
+ Tests that Temporal.PlainMonthDay.prototype.toLocaleString
+ 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.PlainMonthDay.prototype.toLocaleString),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainMonthDay.prototype.toLocaleString),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainMonthDay.prototype.toLocaleString),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainMonthDay.prototype.toLocaleString.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toLocaleString/length.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toLocaleString/length.js
new file mode 100644
index 0000000000..5e31fdccdb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toLocaleString/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.plainmonthday.prototype.tolocalestring
+description: Temporal.PlainMonthDay.prototype.toLocaleString.length is 0
+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.PlainMonthDay.prototype.toLocaleString, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toLocaleString/name.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toLocaleString/name.js
new file mode 100644
index 0000000000..5a326d9fa8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toLocaleString/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.plainmonthday.prototype.tolocalestring
+description: Temporal.PlainMonthDay.prototype.toLocaleString.name is "toLocaleString".
+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.PlainMonthDay.prototype.toLocaleString, "name", {
+ value: "toLocaleString",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toLocaleString/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toLocaleString/not-a-constructor.js
new file mode 100644
index 0000000000..a295107962
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toLocaleString/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.plainmonthday.prototype.tolocalestring
+description: >
+ Temporal.PlainMonthDay.prototype.toLocaleString 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.PlainMonthDay.prototype.toLocaleString();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainMonthDay.prototype.toLocaleString), false,
+ "isConstructor(Temporal.PlainMonthDay.prototype.toLocaleString)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toLocaleString/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toLocaleString/prop-desc.js
new file mode 100644
index 0000000000..0a03647176
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toLocaleString/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.plainmonthday.prototype.tolocalestring
+description: The "toLocaleString" property of Temporal.PlainMonthDay.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainMonthDay.prototype.toLocaleString,
+ "function",
+ "`typeof PlainMonthDay.prototype.toLocaleString` is `function`"
+);
+
+verifyProperty(Temporal.PlainMonthDay.prototype, "toLocaleString", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toLocaleString/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toLocaleString/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toLocaleString/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/PlainMonthDay/prototype/toPlainDate/argument-not-object.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/argument-not-object.js
new file mode 100644
index 0000000000..66e4537b18
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/argument-not-object.js
@@ -0,0 +1,18 @@
+// |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.plainmonthday.prototype.toplaindate
+description: Throws a TypeError if the argument is not an Object, before any other observable actions
+includes: [compareArray.js, temporalHelpers.js]
+features: [BigInt, Symbol, Temporal]
+---*/
+
+[null, undefined, true, 3.1416, "a string", Symbol("symbol"), 7n].forEach((primitive) => {
+ const calendar = TemporalHelpers.calendarThrowEverything();
+ const plainMonthDay = new Temporal.PlainMonthDay(5, 2, calendar);
+ assert.throws(TypeError, () => plainMonthDay.toPlainDate(primitive));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/basic.js
new file mode 100644
index 0000000000..66501b5d14
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/basic.js
@@ -0,0 +1,29 @@
+// |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.plainmonthday.prototype.toplaindate
+description: Basic tests for toPlainDate().
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const md = Temporal.PlainMonthDay.from("01-22");
+const d = md.toPlainDate({ year: 2002 });
+TemporalHelpers.assertPlainDate(d, 2002, 1, "M01", 22);
+
+assert.throws(TypeError, () => md.toPlainDate({ something: 'nothing' }), "missing fields");
+
+const leapDay = Temporal.PlainMonthDay.from('02-29');
+TemporalHelpers.assertPlainDate(leapDay.toPlainDate({ year: 2020 }), 2020, 2, "M02", 29);
+
+const options = {
+ get overflow() {
+ TemporalHelpers.assertUnreachable("Should not get overflow option");
+ return "";
+ }
+};
+TemporalHelpers.assertPlainDate(leapDay.toPlainDate({ year: 2020 }, options), 2020, 2, "M02", 29);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/branding.js
new file mode 100644
index 0000000000..4fde5eb378
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/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.plainmonthday.prototype.toplaindate
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toPlainDate = Temporal.PlainMonthDay.prototype.toPlainDate;
+
+assert.sameValue(typeof toPlainDate, "function");
+
+const args = [{ year: 2022 }];
+
+assert.throws(TypeError, () => toPlainDate.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => toPlainDate.apply(null, args), "null");
+assert.throws(TypeError, () => toPlainDate.apply(true, args), "true");
+assert.throws(TypeError, () => toPlainDate.apply("", args), "empty string");
+assert.throws(TypeError, () => toPlainDate.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => toPlainDate.apply(1, args), "1");
+assert.throws(TypeError, () => toPlainDate.apply({}, args), "plain object");
+assert.throws(TypeError, () => toPlainDate.apply(Temporal.PlainMonthDay, args), "Temporal.PlainMonthDay");
+assert.throws(TypeError, () => toPlainDate.apply(Temporal.PlainMonthDay.prototype, args), "Temporal.PlainMonthDay.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..e9657f74af
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/builtin-calendar-no-array-iteration.js
@@ -0,0 +1,23 @@
+// |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.plainmonthday.prototype.toplaindate
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.PlainMonthDay(5, 1, "iso8601");
+instance.toPlainDate({ year: 2005 });
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..7a2935df2c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,46 @@
+// |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.plainmonthday.prototype.toplaindate
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const fieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "fields");
+Object.defineProperty(Temporal.Calendar.prototype, "fields", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("fields should not be looked up");
+ },
+});
+const mergeFieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "mergeFields");
+Object.defineProperty(Temporal.Calendar.prototype, "mergeFields", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("mergeFields should not be looked up");
+ },
+});
+const dateFromFieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dateFromFields");
+Object.defineProperty(Temporal.Calendar.prototype, "dateFromFields", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("dateFromFields should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainMonthDay(5, 2, "iso8601", 1972);
+instance.toPlainDate({ year: 2002 });
+
+Object.defineProperty(Temporal.Calendar.prototype, "fields", fieldsOriginal);
+Object.defineProperty(Temporal.Calendar.prototype, "mergeFields", mergeFieldsOriginal);
+Object.defineProperty(Temporal.Calendar.prototype, "dateFromFields", dateFromFieldsOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/builtin.js
new file mode 100644
index 0000000000..d414e7501f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/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.plainmonthday.prototype.toplaindate
+description: >
+ Tests that Temporal.PlainMonthDay.prototype.toPlainDate
+ 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.PlainMonthDay.prototype.toPlainDate),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainMonthDay.prototype.toPlainDate),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainMonthDay.prototype.toPlainDate),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainMonthDay.prototype.toPlainDate.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/calendar-fields-iterable.js
new file mode 100644
index 0000000000..1522f05f0e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/calendar-fields-iterable.js
@@ -0,0 +1,37 @@
+// |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.plainmonthday.prototype.toplaindate
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plainmonthday.prototype.toplaindate step 4:
+ 4. Let _receiverFieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"monthCode"* »).
+ sec-temporal.plainmonthday.prototype.toplaindate step 7:
+ 7. Let _inputFieldNames_ be ? CalendarFields(_calendar_, « *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected1 = [
+ "day",
+ "monthCode",
+];
+const expected2 = [
+ "year",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+const monthday = new Temporal.PlainMonthDay(5, 2, calendar);
+monthday.toPlainDate({ year: 1997 });
+
+assert.sameValue(calendar.fieldsCallCount, 2, "fields() method called twice");
+assert.compareArray(calendar.fieldsCalledWith[0], expected1, "fields() method called first time with correct args");
+assert.compareArray(calendar.fieldsCalledWith[1], expected2, "fields() method called second time with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole first iterable");
+assert(calendar.iteratorExhausted[1], "iterated through the whole second iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/calendar-fromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/calendar-fromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..fb95a25ed6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/calendar-fromfields-called-with-null-prototype-fields.js
@@ -0,0 +1,18 @@
+// |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.plainmonthday.prototype.toplaindate
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.PlainMonthDay(5, 2, calendar);
+instance.toPlainDate({ year: 2019 });
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should have been called on the calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/calendar-merge-fields-returns-primitive.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/calendar-merge-fields-returns-primitive.js
new file mode 100644
index 0000000000..e70303d0da
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/calendar-merge-fields-returns-primitive.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.plainmonthday.prototype.toplaindate
+description: >
+ with() should throw a TypeError if mergeFields() returns a primitive,
+ without passing the value on to any other calendar methods
+includes: [compareArray.js, temporalHelpers.js]
+features: [BigInt, Symbol, Temporal]
+---*/
+
+[undefined, null, true, 3.14159, "bad value", Symbol("no"), 7n].forEach((primitive) => {
+ const calendar = TemporalHelpers.calendarMergeFieldsReturnsPrimitive(primitive);
+ const instance = new Temporal.PlainMonthDay(5, 2, calendar);
+ assert.throws(TypeError, () => instance.toPlainDate({ year: 2005 }), "bad return from mergeFields() throws");
+ assert.sameValue(calendar.dateFromFieldsCallCount, 0, "dateFromFields() never called");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/calendar-mergefields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/calendar-mergefields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..3faab87e34
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/calendar-mergefields-called-with-null-prototype-fields.js
@@ -0,0 +1,18 @@
+// |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.plainmonthday.prototype.toplaindate
+description: >
+ Calendar.mergeFields method is called with null-prototype fields objects
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckMergeFieldsPrototypePollution();
+const instance = new Temporal.PlainMonthDay(5, 2, calendar);
+instance.toPlainDate({ year: 2019 });
+assert.sameValue(calendar.mergeFieldsCallCount, 1, "mergeFields should have been called on the calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..ba5d3dc36f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/constructor-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |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.plainmonthday.prototype.toplaindate
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const md = new Temporal.PlainMonthDay(5, 1, calendar);
+
+assert.throws(RangeError, () => md.toPlainDate({year: 2023}));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/copies-merge-fields-object.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/copies-merge-fields-object.js
new file mode 100644
index 0000000000..232f3b089f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/copies-merge-fields-object.js
@@ -0,0 +1,34 @@
+// |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.plainmonthday.prototype.toplaindate
+description: The object returned from mergeFields() is copied before being passed to monthDayFromFields().
+info: |
+ sec-temporal.plainmonthday.prototype.toplaindate steps 9 and 11:
+ 9. Let _mergedFields_ be ? CalendarMergeFields(_calendar_, _fields_, _inputFields_).
+ 11. Set _mergedFields_ to ? PrepareTemporalFields(_mergedFields_, _mergedFieldNames_, «»).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "get day",
+ "get day.valueOf",
+ "call day.valueOf",
+ "get monthCode",
+ "get monthCode.toString",
+ "call monthCode.toString",
+ "get year",
+ "get year.valueOf",
+ "call year.valueOf",
+];
+
+const calendar = TemporalHelpers.calendarMergeFieldsGetters();
+const monthday = new Temporal.PlainMonthDay(3, 31, calendar);
+monthday.toPlainDate({ year: 2000 });
+
+assert.compareArray(calendar.mergeFieldsReturnOperations, expected, "getters called on mergeFields return");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/default-overflow-behaviour.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/default-overflow-behaviour.js
new file mode 100644
index 0000000000..a5457d59af
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/default-overflow-behaviour.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.plainmonthday.prototype.toplaindate
+description: A nonexistent resulting date is constrained to an existing date
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const leapDay = new Temporal.PlainMonthDay(2, 29);
+const result = leapDay.toPlainDate({ year: 2023 });
+// 2023-02-29 does not exist because 2023 is a common year
+TemporalHelpers.assertPlainDate(result, 2023, 2, "M02", 28, "2023 + 02-29 = 2023-02-28");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/duplicate-calendar-fields.js
new file mode 100644
index 0000000000..b37b1f1936
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/duplicate-calendar-fields.js
@@ -0,0 +1,18 @@
+// |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.plainmonthday.prototype.toplaindate
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['monthCode'], ['day'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const md = new Temporal.PlainMonthDay(5, 1, calendar);
+
+ assert.throws(RangeError, () => md.toPlainDate({year: 2023}));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..3078b75827
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/infinity-throws-rangeerror.js
@@ -0,0 +1,23 @@
+// |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.
+
+/*---
+description: Throws a RangeError if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plainmonthday.prototype.toplaindate
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainMonthDay(5, 2);
+
+[Infinity, -Infinity].forEach((inf) => {
+ assert.throws(RangeError, () => instance.toPlainDate({ year: inf }), `year property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, "year");
+ assert.throws(RangeError, () => instance.toPlainDate({ year: obj }));
+ assert.compareArray(calls, ["get year.valueOf", "call year.valueOf"], "it fails after fetching the primitive value");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/length.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/length.js
new file mode 100644
index 0000000000..dba010a6b6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/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.plainmonthday.prototype.toplaindate
+description: Temporal.PlainMonthDay.prototype.toPlainDate.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.PlainMonthDay.prototype.toPlainDate, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/limits.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/limits.js
new file mode 100644
index 0000000000..232c424cde
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/limits.js
@@ -0,0 +1,33 @@
+// |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.plainmonthday.prototype.toplaindate
+description: Throws a RangeError if the resulting PlainDate is out of range
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const jan1 = Temporal.PlainMonthDay.from("01-01");
+const dec31 = Temporal.PlainMonthDay.from("12-31");
+
+const minYear = -271821;
+assert.throws(RangeError, () => jan1.toPlainDate({ year: minYear }), "jan1 min");
+const apr18 = Temporal.PlainMonthDay.from("04-18");
+assert.throws(RangeError, () => apr18.toPlainDate({ year: minYear }), "apr18 min");
+TemporalHelpers.assertPlainDate(Temporal.PlainMonthDay.from("04-19").toPlainDate({ year: minYear }),
+ minYear, 4, "M04", 19, "apr19 min");
+TemporalHelpers.assertPlainDate(jan1.toPlainDate({ year: minYear + 1 }),
+ minYear + 1, 1, "M01", 1, "jan1 min");
+
+const maxYear = 275760;
+assert.throws(RangeError, () => dec31.toPlainDate({ year: maxYear }), "dec31 max");
+const sep14 = Temporal.PlainMonthDay.from("09-14");
+assert.throws(RangeError, () => sep14.toPlainDate({ year: maxYear }), "sep14 max");
+TemporalHelpers.assertPlainDate(Temporal.PlainMonthDay.from("09-13").toPlainDate({ year: maxYear }),
+ maxYear, 9, "M09", 13, "max");
+TemporalHelpers.assertPlainDate(dec31.toPlainDate({ year: maxYear - 1 }),
+ maxYear - 1, 12, "M12", 31, "dec31 max");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/name.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/name.js
new file mode 100644
index 0000000000..4c46baa3e4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/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.plainmonthday.prototype.toplaindate
+description: Temporal.PlainMonthDay.prototype.toPlainDate.name is "toPlainDate".
+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.PlainMonthDay.prototype.toPlainDate, "name", {
+ value: "toPlainDate",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/not-a-constructor.js
new file mode 100644
index 0000000000..cab2fc3623
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/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.plainmonthday.prototype.toplaindate
+description: >
+ Temporal.PlainMonthDay.prototype.toPlainDate 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.PlainMonthDay.prototype.toPlainDate();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainMonthDay.prototype.toPlainDate), false,
+ "isConstructor(Temporal.PlainMonthDay.prototype.toPlainDate)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/prop-desc.js
new file mode 100644
index 0000000000..270c6ee330
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/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.plainmonthday.prototype.toplaindate
+description: The "toPlainDate" property of Temporal.PlainMonthDay.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainMonthDay.prototype.toPlainDate,
+ "function",
+ "`typeof PlainMonthDay.prototype.toPlainDate` is `function`"
+);
+
+verifyProperty(Temporal.PlainMonthDay.prototype, "toPlainDate", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/proto-in-calendar-fields.js
new file mode 100644
index 0000000000..061303fea0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/proto-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |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.plainmonthday.prototype.toplaindate
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const md = new Temporal.PlainMonthDay(5, 1, calendar);
+
+assert.throws(RangeError, () => md.toPlainDate({year: 2023}));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/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/PlainMonthDay/prototype/toString/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/branding.js
new file mode 100644
index 0000000000..97facdb43b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/branding.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.plainmonthday.prototype.tostring
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toString = Temporal.PlainMonthDay.prototype.toString;
+
+assert.sameValue(typeof toString, "function");
+
+assert.throws(TypeError, () => toString.call(undefined), "undefined");
+assert.throws(TypeError, () => toString.call(null), "null");
+assert.throws(TypeError, () => toString.call(true), "true");
+assert.throws(TypeError, () => toString.call(""), "empty string");
+assert.throws(TypeError, () => toString.call(Symbol()), "symbol");
+assert.throws(TypeError, () => toString.call(1), "1");
+assert.throws(TypeError, () => toString.call({}), "plain object");
+assert.throws(TypeError, () => toString.call(Temporal.PlainMonthDay), "Temporal.PlainMonthDay");
+assert.throws(TypeError, () => toString.call(Temporal.PlainMonthDay.prototype), "Temporal.PlainMonthDay.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..91f2de6b15
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plainmonthday.prototype.tostring
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const idOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "id");
+Object.defineProperty(Temporal.Calendar.prototype, "id", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("id should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainMonthDay(5, 2, "iso8601", 1972);
+instance.toString();
+
+Object.defineProperty(Temporal.Calendar.prototype, "id", idOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/builtin.js
new file mode 100644
index 0000000000..d814aaf1ba
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/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.plainmonthday.prototype.tostring
+description: >
+ Tests that Temporal.PlainMonthDay.prototype.toString
+ 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.PlainMonthDay.prototype.toString),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainMonthDay.prototype.toString),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainMonthDay.prototype.toString),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainMonthDay.prototype.toString.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/calendar-tostring.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/calendar-tostring.js
new file mode 100644
index 0000000000..a7dc0e9195
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/calendar-tostring.js
@@ -0,0 +1,56 @@
+// |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.plainmonthday.protoype.tostring
+description: Number of observable 'toString' calls on the calendar for each value of calendarName
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+let calls;
+const customCalendar = {
+ get id() {
+ ++calls;
+ return "custom";
+ },
+ toString() {
+ TemporalHelpers.assertUnreachable('toString should not be called');
+ },
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+const monthday = new Temporal.PlainMonthDay(5, 2, customCalendar);
+[
+ ["always", "1972-05-02[u-ca=custom]", 1],
+ ["auto", "1972-05-02[u-ca=custom]", 1],
+ ["critical", "1972-05-02[!u-ca=custom]", 1],
+ ["never", "1972-05-02", 1],
+ [undefined, "1972-05-02[u-ca=custom]", 1],
+].forEach(([calendarName, expectedResult, expectedCalls]) => {
+ calls = 0;
+ const result = monthday.toString({ calendarName });
+ assert.sameValue(result, expectedResult, `id for calendarName = ${calendarName}`);
+ assert.sameValue(calls, expectedCalls, `calls to id getter for calendarName = ${calendarName}`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/calendarname-always.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/calendarname-always.js
new file mode 100644
index 0000000000..75598ac80e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/calendarname-always.js
@@ -0,0 +1,48 @@
+// |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.plainmonthday.prototype.tostring
+description: If calendarName is "always", the calendar ID should be included.
+features: [Temporal]
+---*/
+
+const calendarMethods = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+
+const tests = [
+ [[], "1972-05-02[u-ca=iso8601]", "built-in ISO"],
+ [[{ id: "custom", ...calendarMethods }], "1972-05-02[u-ca=custom]", "custom"],
+ [[{ id: "iso8601", ...calendarMethods }], "1972-05-02[u-ca=iso8601]", "custom with iso8601 id"],
+ [[{ id: "ISO8601", ...calendarMethods }], "1972-05-02[u-ca=ISO8601]", "custom with caps id"],
+ [[{ id: "\u0131so8601", ...calendarMethods }], "1972-05-02[u-ca=\u0131so8601]", "custom with dotless i id"],
+];
+
+for (const [args, expected, description] of tests) {
+ const monthday = new Temporal.PlainMonthDay(5, 2, ...args);
+ const result = monthday.toString({ calendarName: "always" });
+ assert.sameValue(result, expected, `${description} calendar for calendarName = always`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/calendarname-auto.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/calendarname-auto.js
new file mode 100644
index 0000000000..1ce645ffe3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/calendarname-auto.js
@@ -0,0 +1,48 @@
+// |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.plainmonthday.prototype.tostring
+description: If calendarName is "auto", "iso8601" should be omitted.
+features: [Temporal]
+---*/
+
+const calendarMethods = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+
+const tests = [
+ [[], "05-02", "built-in ISO"],
+ [[{ id: "custom", ...calendarMethods }], "1972-05-02[u-ca=custom]", "custom"],
+ [[{ id: "iso8601", ...calendarMethods }], "05-02", "custom with iso8601 id"],
+ [[{ id: "ISO8601", ...calendarMethods }], "1972-05-02[u-ca=ISO8601]", "custom with caps id"],
+ [[{ id: "\u0131so8601", ...calendarMethods }], "1972-05-02[u-ca=\u0131so8601]", "custom with dotless i id"],
+];
+
+for (const [args, expected, description] of tests) {
+ const monthday = new Temporal.PlainMonthDay(5, 2, ...args);
+ const result = monthday.toString({ calendarName: "auto" });
+ assert.sameValue(result, expected, `${description} calendar for calendarName = auto`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/calendarname-critical.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/calendarname-critical.js
new file mode 100644
index 0000000000..84832a5aae
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/calendarname-critical.js
@@ -0,0 +1,50 @@
+// |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.plainmonthday.prototype.tostring
+description: >
+ If calendarName is "calendar", the calendar ID should be included and prefixed
+ with "!".
+features: [Temporal]
+---*/
+
+const calendarMethods = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+
+const tests = [
+ [[], "1972-05-02[!u-ca=iso8601]", "built-in ISO"],
+ [[{ id: "custom", ...calendarMethods }], "1972-05-02[!u-ca=custom]", "custom"],
+ [[{ id: "iso8601", ...calendarMethods }], "1972-05-02[!u-ca=iso8601]", "custom with iso8601 id"],
+ [[{ id: "ISO8601", ...calendarMethods }], "1972-05-02[!u-ca=ISO8601]", "custom with caps id"],
+ [[{ id: "\u0131so8601", ...calendarMethods }], "1972-05-02[!u-ca=\u0131so8601]", "custom with dotless i id"],
+];
+
+for (const [args, expected, description] of tests) {
+ const monthday = new Temporal.PlainMonthDay(5, 2, ...args);
+ const result = monthday.toString({ calendarName: "critical" });
+ assert.sameValue(result, expected, `${description} calendar for calendarName = critical`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/calendarname-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/calendarname-invalid-string.js
new file mode 100644
index 0000000000..78c453b1e1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/calendarname-invalid-string.js
@@ -0,0 +1,29 @@
+// |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.plainmonthday.protoype.tostring
+description: RangeError thrown when calendarName option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-toshowcalendaroption step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"calendarName"*, « *"string"* », « *"auto"*, *"always"*, *"never"*, *"critical"* », *"auto"*).
+ sec-temporal.plainmonthday.protoype.tostring step 4:
+ 4. Let _showCalendar_ be ? ToShowCalendarOption(_options_).
+features: [Temporal]
+---*/
+
+const monthday = new Temporal.PlainMonthDay(5, 2);
+const invalidValues = ["ALWAYS", "sometimes", "other string", "auto\0"];
+
+for (const calendarName of invalidValues) {
+ assert.throws(
+ RangeError,
+ () => monthday.toString({ calendarName }),
+ `${calendarName} is an invalid value for calendarName option`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/calendarname-never.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/calendarname-never.js
new file mode 100644
index 0000000000..c87ba6f9ed
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/calendarname-never.js
@@ -0,0 +1,48 @@
+// |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.plainmonthday.prototype.tostring
+description: If calendarName is "never", the calendar ID should be omitted.
+features: [Temporal]
+---*/
+
+const calendarMethods = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+
+const tests = [
+ [[], "05-02", "built-in ISO"],
+ [[{ id: "custom", ...calendarMethods }], "1972-05-02", "custom"],
+ [[{ id: "iso8601", ...calendarMethods }], "05-02", "custom with iso8601 id"],
+ [[{ id: "ISO8601", ...calendarMethods }], "1972-05-02", "custom with caps id"],
+ [[{ id: "\u0131so8601", ...calendarMethods }], "1972-05-02", "custom with dotless i id"],
+];
+
+for (const [args, expected, description] of tests) {
+ const monthday = new Temporal.PlainMonthDay(5, 2, ...args);
+ const result = monthday.toString({ calendarName: "never" });
+ assert.sameValue(result, expected, `${description} calendar for calendarName = never`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/calendarname-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/calendarname-undefined.js
new file mode 100644
index 0000000000..87f9b23051
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/calendarname-undefined.js
@@ -0,0 +1,56 @@
+// |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.plainmonthday.protoype.tostring
+description: Fallback value for calendarName option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-toshowcalendaroption step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"calendarName"*, « *"string"* », « *"auto"*, *"always"*, *"never"*, *"critical"* », *"auto"*).
+ sec-temporal.plainmonthday.protoype.tostring step 4:
+ 4. Let _showCalendar_ be ? ToShowCalendarOption(_options_).
+features: [Temporal]
+---*/
+
+const calendarMethods = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+
+const tests = [
+ [[], "05-02", "built-in ISO"],
+ [[{ id: "custom", ...calendarMethods }], "1972-05-02[u-ca=custom]", "custom"],
+ [[{ id: "iso8601", ...calendarMethods }], "05-02", "custom with iso8601 id"],
+ [[{ id: "ISO8601", ...calendarMethods }], "1972-05-02[u-ca=ISO8601]", "custom with caps id"],
+ [[{ id: "\u0131so8601", ...calendarMethods }], "1972-05-02[u-ca=\u0131so8601]", "custom with dotless i id"],
+];
+
+for (const [args, expected, description] of tests) {
+ const monthday = new Temporal.PlainMonthDay(5, 2, ...args);
+ const result = monthday.toString({ calendarName: undefined });
+ assert.sameValue(result, expected, `default calendarName option is auto with ${description} calendar`);
+ // See options-object.js for {} and options-undefined.js for absent options arg
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/calendarname-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/calendarname-wrong-type.js
new file mode 100644
index 0000000000..6a0f7edf80
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/calendarname-wrong-type.js
@@ -0,0 +1,49 @@
+// |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.plainmonthday.protoype.tostring
+description: Type conversions for calendarName option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-toshowcalendaroption step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"calendarName"*, « *"string"* », « *"auto"*, *"always"*, *"never"*, *"critical"* », *"auto"*).
+ sec-temporal.plainmonthday.protoype.tostring step 4:
+ 4. Let _showCalendar_ be ? ToShowCalendarOption(_options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = {
+ id: "custom",
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+const monthday = new Temporal.PlainMonthDay(5, 2, calendar);
+
+TemporalHelpers.checkStringOptionWrongType("calendarName", "auto",
+ (calendarName) => monthday.toString({ calendarName }),
+ (result, descr) => assert.sameValue(result, "1972-05-02[u-ca=custom]", descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/length.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/length.js
new file mode 100644
index 0000000000..7da082f7fe
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/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.plainmonthday.prototype.tostring
+description: Temporal.PlainMonthDay.prototype.toString.length is 0
+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.PlainMonthDay.prototype.toString, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/name.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/name.js
new file mode 100644
index 0000000000..6d6088bd86
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/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.plainmonthday.prototype.tostring
+description: Temporal.PlainMonthDay.prototype.toString.name is "toString".
+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.PlainMonthDay.prototype.toString, "name", {
+ value: "toString",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/not-a-constructor.js
new file mode 100644
index 0000000000..42bf0e3785
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/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.plainmonthday.prototype.tostring
+description: >
+ Temporal.PlainMonthDay.prototype.toString 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.PlainMonthDay.prototype.toString();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainMonthDay.prototype.toString), false,
+ "isConstructor(Temporal.PlainMonthDay.prototype.toString)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/options-object.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/options-object.js
new file mode 100644
index 0000000000..7a57ecb00d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/options-object.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.plainmonthday.prototype.tostring
+description: Empty or a function object may be used as options
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainMonthDay(5, 2);
+
+const result1 = instance.toString({});
+assert.sameValue(
+ result1, "05-02",
+ "options may be an empty plain object"
+);
+
+const result2 = instance.toString(() => {});
+assert.sameValue(
+ result2, "05-02",
+ "options may be a function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/options-undefined.js
new file mode 100644
index 0000000000..c7faf80e88
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/options-undefined.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.plainmonthday.prototype.tostring
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const calendarMethods = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+
+const tests = [
+ [[], "05-02"],
+ [[{ id: "custom", ...calendarMethods }], "1972-05-02[u-ca=custom]"],
+ [[{ id: "iso8601", ...calendarMethods }], "05-02"],
+ [[{ id: "ISO8601", ...calendarMethods }], "1972-05-02[u-ca=ISO8601]"],
+ [[{ id: "\u0131so8601", ...calendarMethods }], "1972-05-02[u-ca=\u0131so8601]"], // dotless i
+];
+
+for (const [args, expected] of tests) {
+ const monthday = new Temporal.PlainMonthDay(5, 2, ...args);
+ const explicit = monthday.toString(undefined);
+ assert.sameValue(explicit, expected, "default calendarName option is auto");
+
+ const implicit = monthday.toString();
+ assert.sameValue(implicit, expected, "default calendarName option is auto");
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/options-wrong-type.js
new file mode 100644
index 0000000000..68b4fb3b51
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/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.plainmonthday.prototype.tostring
+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.PlainMonthDay(5, 2);
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.toString(value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/order-of-operations.js
new file mode 100644
index 0000000000..c56b064c02
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/order-of-operations.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.plainmonthday.prototype.tostring
+description: Properties on an object passed to toString() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "get options.calendarName",
+ "get options.calendarName.toString",
+ "call options.calendarName.toString",
+ "get this.calendar.id",
+];
+const actual = [];
+
+const calendar = TemporalHelpers.calendarObserver(actual, "this.calendar");
+const instance = new Temporal.PlainMonthDay(5, 2, calendar);
+// clear observable operations that occurred during the constructor call
+actual.splice(0);
+
+const options = TemporalHelpers.propertyBagObserver(actual, {
+ calendarName: "auto",
+}, "options");
+
+instance.toString(options);
+assert.compareArray(actual, expected, "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/prop-desc.js
new file mode 100644
index 0000000000..a7bb3e8bc6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/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.plainmonthday.prototype.tostring
+description: The "toString" property of Temporal.PlainMonthDay.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainMonthDay.prototype.toString,
+ "function",
+ "`typeof PlainMonthDay.prototype.toString` is `function`"
+);
+
+verifyProperty(Temporal.PlainMonthDay.prototype, "toString", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/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/PlainMonthDay/prototype/toString/year-format.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/year-format.js
new file mode 100644
index 0000000000..94db829612
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toString/year-format.js
@@ -0,0 +1,58 @@
+// |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.plainmonthday.prototype.tostring
+description: Verify that the year is appropriately formatted as 4 or 6 digits
+features: [Temporal]
+---*/
+
+// For PlainMonthDay, the ISO reference year is only present in the string if
+// the calendar is not ISO 8601
+class NotISO extends Temporal.Calendar {
+ constructor() { super("iso8601"); }
+ get id() { return "not-iso"; }
+}
+const calendar = new NotISO();
+
+let instance = new Temporal.PlainMonthDay(12, 3, calendar, -100000);
+assert.sameValue(instance.toString(), "-100000-12-03[u-ca=not-iso]", "large negative year formatted as 6-digit");
+
+instance = new Temporal.PlainMonthDay(4, 5, calendar, -10000);
+assert.sameValue(instance.toString(), "-010000-04-05[u-ca=not-iso]", "smallest 5-digit negative year formatted as 6-digit");
+
+instance = new Temporal.PlainMonthDay(6, 7, calendar, -9999);
+assert.sameValue(instance.toString(), "-009999-06-07[u-ca=not-iso]", "largest 4-digit negative year formatted as 6-digit");
+
+instance = new Temporal.PlainMonthDay(8, 9, calendar, -1000);
+assert.sameValue(instance.toString(), "-001000-08-09[u-ca=not-iso]", "smallest 4-digit negative year formatted as 6-digit");
+
+instance = new Temporal.PlainMonthDay(10, 9, calendar, -999);
+assert.sameValue(instance.toString(), "-000999-10-09[u-ca=not-iso]", "largest 3-digit negative year formatted as 6-digit");
+
+instance = new Temporal.PlainMonthDay(8, 7, calendar, -1);
+assert.sameValue(instance.toString(), "-000001-08-07[u-ca=not-iso]", "year -1 formatted as 6-digit");
+
+instance = new Temporal.PlainMonthDay(6, 5, calendar, 0);
+assert.sameValue(instance.toString(), "0000-06-05[u-ca=not-iso]", "year 0 formatted as 4-digit");
+
+instance = new Temporal.PlainMonthDay(4, 3, calendar, 1);
+assert.sameValue(instance.toString(), "0001-04-03[u-ca=not-iso]", "year 1 formatted as 4-digit");
+
+instance = new Temporal.PlainMonthDay(2, 10, calendar, 999);
+assert.sameValue(instance.toString(), "0999-02-10[u-ca=not-iso]", "largest 3-digit positive year formatted as 4-digit");
+
+instance = new Temporal.PlainMonthDay(1, 23, calendar, 1000);
+assert.sameValue(instance.toString(), "1000-01-23[u-ca=not-iso]", "smallest 4-digit positive year formatted as 4-digit");
+
+instance = new Temporal.PlainMonthDay(4, 5, calendar, 9999);
+assert.sameValue(instance.toString(), "9999-04-05[u-ca=not-iso]", "largest 4-digit positive year formatted as 4-digit");
+
+instance = new Temporal.PlainMonthDay(6, 7, calendar, 10000);
+assert.sameValue(instance.toString(), "+010000-06-07[u-ca=not-iso]", "smallest 5-digit positive year formatted as 6-digit");
+
+instance = new Temporal.PlainMonthDay(8, 9, calendar, 100000);
+assert.sameValue(instance.toString(), "+100000-08-09[u-ca=not-iso]", "large positive year formatted as 6-digit");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toStringTag/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toStringTag/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toStringTag/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toStringTag/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toStringTag/prop-desc.js
new file mode 100644
index 0000000000..db4be07058
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toStringTag/prop-desc.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.plainmonthday.prototype-@@tostringtag
+description: The @@toStringTag property of Temporal.PlainMonthDay
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainMonthDay.prototype, Symbol.toStringTag, {
+ value: "Temporal.PlainMonthDay",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toStringTag/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toStringTag/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/toStringTag/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/valueOf/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/valueOf/basic.js
new file mode 100644
index 0000000000..0c8ae46a4c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/valueOf/basic.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.plainmonthday.prototype.valueof
+description: Basic tests for valueOf().
+features: [Temporal]
+---*/
+
+const plainMonthDay = Temporal.PlainMonthDay.from("1963-02-13");
+const plainMonthDay2 = Temporal.PlainMonthDay.from("1963-02-13");
+
+assert.throws(TypeError, () => plainMonthDay.valueOf(), "valueOf");
+assert.throws(TypeError, () => plainMonthDay < plainMonthDay, "<");
+assert.throws(TypeError, () => plainMonthDay <= plainMonthDay, "<=");
+assert.throws(TypeError, () => plainMonthDay > plainMonthDay, ">");
+assert.throws(TypeError, () => plainMonthDay >= plainMonthDay, ">=");
+assert.sameValue(plainMonthDay === plainMonthDay, true, "===");
+assert.sameValue(plainMonthDay === plainMonthDay2, false, "===");
+assert.sameValue(plainMonthDay !== plainMonthDay, false, "!==");
+assert.sameValue(plainMonthDay !== plainMonthDay2, true, "!==");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/valueOf/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/valueOf/branding.js
new file mode 100644
index 0000000000..473c2c8055
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/valueOf/branding.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.plainmonthday.prototype.valueof
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const valueOf = Temporal.PlainMonthDay.prototype.valueOf;
+
+assert.sameValue(typeof valueOf, "function");
+
+assert.throws(TypeError, () => valueOf.call(undefined), "undefined");
+assert.throws(TypeError, () => valueOf.call(null), "null");
+assert.throws(TypeError, () => valueOf.call(true), "true");
+assert.throws(TypeError, () => valueOf.call(""), "empty string");
+assert.throws(TypeError, () => valueOf.call(Symbol()), "symbol");
+assert.throws(TypeError, () => valueOf.call(1), "1");
+assert.throws(TypeError, () => valueOf.call({}), "plain object");
+assert.throws(TypeError, () => valueOf.call(Temporal.PlainMonthDay), "Temporal.PlainMonthDay");
+assert.throws(TypeError, () => valueOf.call(Temporal.PlainMonthDay.prototype), "Temporal.PlainMonthDay.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/valueOf/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/valueOf/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/valueOf/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/valueOf/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/valueOf/builtin.js
new file mode 100644
index 0000000000..cc6856df64
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/valueOf/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.plainmonthday.prototype.valueof
+description: >
+ Tests that Temporal.PlainMonthDay.prototype.valueOf
+ 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.PlainMonthDay.prototype.valueOf),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainMonthDay.prototype.valueOf),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainMonthDay.prototype.valueOf),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainMonthDay.prototype.valueOf.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/valueOf/length.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/valueOf/length.js
new file mode 100644
index 0000000000..755c56bacd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/valueOf/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.plainmonthday.prototype.valueof
+description: Temporal.PlainMonthDay.prototype.valueOf.length is 0
+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.PlainMonthDay.prototype.valueOf, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/valueOf/name.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/valueOf/name.js
new file mode 100644
index 0000000000..49c1920de7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/valueOf/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.plainmonthday.prototype.valueof
+description: Temporal.PlainMonthDay.prototype.valueOf.name is "valueOf".
+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.PlainMonthDay.prototype.valueOf, "name", {
+ value: "valueOf",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/valueOf/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/valueOf/not-a-constructor.js
new file mode 100644
index 0000000000..61ab175d2a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/valueOf/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.plainmonthday.prototype.valueof
+description: >
+ Temporal.PlainMonthDay.prototype.valueOf 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.PlainMonthDay.prototype.valueOf();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainMonthDay.prototype.valueOf), false,
+ "isConstructor(Temporal.PlainMonthDay.prototype.valueOf)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/valueOf/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/valueOf/prop-desc.js
new file mode 100644
index 0000000000..13deb2da8d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/valueOf/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.plainmonthday.prototype.valueof
+description: The "valueOf" property of Temporal.PlainMonthDay.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainMonthDay.prototype.valueOf,
+ "function",
+ "`typeof PlainMonthDay.prototype.valueOf` is `function`"
+);
+
+verifyProperty(Temporal.PlainMonthDay.prototype, "valueOf", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/valueOf/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/valueOf/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/valueOf/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/PlainMonthDay/prototype/with/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/basic.js
new file mode 100644
index 0000000000..27f4069fc4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/basic.js
@@ -0,0 +1,44 @@
+// |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.plainmonthday.prototype.with
+description: Basic tests for with().
+includes: [compareArray.js, temporalHelpers.js]
+features: [Symbol, Temporal]
+---*/
+
+const md = Temporal.PlainMonthDay.from("01-15");
+
+TemporalHelpers.assertPlainMonthDay(md.with({ day: 22 }),
+ "M01", 22, "with({day})");
+
+TemporalHelpers.assertPlainMonthDay(md.with({ month: 12 }),
+ "M12", 15, "with({month})");
+
+TemporalHelpers.assertPlainMonthDay(md.with({ monthCode: "M12" }),
+ "M12", 15, "with({monthCode})");
+
+TemporalHelpers.assertPlainMonthDay(md.with({ month: 12, monthCode: "M12" }),
+ "M12", 15, "with({month, monthCode}) agree");
+
+assert.throws(RangeError, () => md.with({ month: 12, monthCode: "M11" }), "with({month, monthCode}) disagree");
+
+TemporalHelpers.assertPlainMonthDay(md.with({ year: 2000, month: 12 }),
+ "M12", 15, "with({year, month})");
+
+TemporalHelpers.assertPlainMonthDay(md.with({ year: 2000 }),
+ "M01", 15, "with({year})");
+
+assert.throws(TypeError, () => md.with({ day: 1, calendar: "iso8601" }), "with({calendar})");
+
+assert.throws(TypeError, () => md.with({ day: 1, timeZone: "UTC" }), "with({timeZone})");
+
+assert.throws(TypeError, () => md.with({}), "with({})");
+assert.throws(TypeError, () => md.with({ months: 12 }), "with({months})");
+
+TemporalHelpers.assertPlainMonthDay(md.with({ monthCode: "M12", days: 1 }),
+ "M12", 15, "with({monthCode, days})");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/branding.js
new file mode 100644
index 0000000000..1df04554e4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/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.plainmonthday.prototype.with
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const with_ = Temporal.PlainMonthDay.prototype.with;
+
+assert.sameValue(typeof with_, "function");
+
+const args = [{ year: 2022, month: 12 }];
+
+assert.throws(TypeError, () => with_.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => with_.apply(null, args), "null");
+assert.throws(TypeError, () => with_.apply(true, args), "true");
+assert.throws(TypeError, () => with_.apply("", args), "empty string");
+assert.throws(TypeError, () => with_.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => with_.apply(1, args), "1");
+assert.throws(TypeError, () => with_.apply({}, args), "plain object");
+assert.throws(TypeError, () => with_.apply(Temporal.PlainMonthDay, args), "Temporal.PlainMonthDay");
+assert.throws(TypeError, () => with_.apply(Temporal.PlainMonthDay.prototype, args), "Temporal.PlainMonthDay.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..10936aca0b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/builtin-calendar-no-array-iteration.js
@@ -0,0 +1,23 @@
+// |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.plainmonthday.prototype.with
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.PlainMonthDay(5, 1, "iso8601");
+instance.with({ monthCode: "M04" });
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..5b4469a1d8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,46 @@
+// |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.plainmonthday.prototype.with
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const fieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "fields");
+Object.defineProperty(Temporal.Calendar.prototype, "fields", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("fields should not be looked up");
+ },
+});
+const mergeFieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "mergeFields");
+Object.defineProperty(Temporal.Calendar.prototype, "mergeFields", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("mergeFields should not be looked up");
+ },
+});
+const monthDayFromFieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "monthDayFromFields");
+Object.defineProperty(Temporal.Calendar.prototype, "monthDayFromFields", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("monthDayFromFields should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainMonthDay(5, 2, "iso8601", 1972);
+instance.with({ monthCode: "M06" });
+
+Object.defineProperty(Temporal.Calendar.prototype, "fields", fieldsOriginal);
+Object.defineProperty(Temporal.Calendar.prototype, "mergeFields", mergeFieldsOriginal);
+Object.defineProperty(Temporal.Calendar.prototype, "monthDayFromFields", monthDayFromFieldsOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/builtin.js
new file mode 100644
index 0000000000..a94e0fd066
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/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.plainmonthday.prototype.with
+description: >
+ Tests that Temporal.PlainMonthDay.prototype.with
+ 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.PlainMonthDay.prototype.with),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainMonthDay.prototype.with),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainMonthDay.prototype.with),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainMonthDay.prototype.with.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/calendar-arguments.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/calendar-arguments.js
new file mode 100644
index 0000000000..5f98ed2e34
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/calendar-arguments.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.plainmonthday.prototype.with
+description: Correct options value is passed to calendar method
+info: |
+ MonthDayFromFields ( calendar, fields [ , options ] )
+
+ 5. Let monthDay be ? Invoke(calendar, "monthDayFromFields", « fields, options »).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const options = {
+ extra: "property",
+};
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ monthDayFromFields(...args) {
+ assert.sameValue(args.length, 2, "args.length");
+ assert.sameValue(typeof args[0], "object", "args[0]");
+ assert.notSameValue(args[1], options, "args[1] is a copy of options");
+ assert.sameValue(args[1].extra, "property", "All properties are copied");
+ assert.sameValue(Object.getPrototypeOf(args[1]), null, "Copy has null prototype");
+ return super.monthDayFromFields(...args);
+ }
+}
+const plainMonthDay = new Temporal.PlainMonthDay(7, 2, new CustomCalendar());
+const result = plainMonthDay.with({ monthCode: "M05" }, options);
+TemporalHelpers.assertPlainMonthDay(result, "M05", 2);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/calendar-fields-iterable.js
new file mode 100644
index 0000000000..6d944a6cf7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/calendar-fields-iterable.js
@@ -0,0 +1,32 @@
+// |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.plainmonthday.prototype.with
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plainmonthday.prototype.with step 9:
+ 9. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+const monthday = new Temporal.PlainMonthDay(5, 2, calendar);
+monthday.with({ day: 6 });
+
+assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/calendar-fromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/calendar-fromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..f7f5b57d89
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/calendar-fromfields-called-with-null-prototype-fields.js
@@ -0,0 +1,18 @@
+// |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.plainmonthday.prototype.with
+description: >
+ Calendar.monthDayFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.PlainMonthDay(5, 2, calendar);
+instance.with({ day: 24 });
+assert.sameValue(calendar.monthDayFromFieldsCallCount, 1, "monthDayFromFields should have been called on the calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/calendar-merge-fields-returns-primitive.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/calendar-merge-fields-returns-primitive.js
new file mode 100644
index 0000000000..c181271486
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/calendar-merge-fields-returns-primitive.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.plainmonthday.prototype.with
+description: >
+ with() should throw a TypeError if mergeFields() returns a primitive,
+ without passing the value on to any other calendar methods
+includes: [compareArray.js, temporalHelpers.js]
+features: [BigInt, Symbol, Temporal]
+---*/
+
+[undefined, null, true, 3.14159, "bad value", Symbol("no"), 7n].forEach((primitive) => {
+ const calendar = TemporalHelpers.calendarMergeFieldsReturnsPrimitive(primitive);
+ const instance = new Temporal.PlainMonthDay(5, 2, calendar);
+ assert.throws(TypeError, () => instance.with({ year: 2005 }), "bad return from mergeFields() throws");
+ assert.sameValue(calendar.monthDayFromFieldsCallCount, 0, "monthDayFromFields() never called");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/calendar-mergefields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/calendar-mergefields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..a91186566a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/calendar-mergefields-called-with-null-prototype-fields.js
@@ -0,0 +1,18 @@
+// |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.plainmonthday.prototype.with
+description: >
+ Calendar.mergeFields method is called with null-prototype fields objects
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckMergeFieldsPrototypePollution();
+const instance = new Temporal.PlainMonthDay(5, 2, calendar);
+instance.with({ day: 24 });
+assert.sameValue(calendar.mergeFieldsCallCount, 1, "mergeFields should have been called on the calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..6d56671dde
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/constructor-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |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.plainmonthday.prototype.with
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const md = new Temporal.PlainMonthDay(5, 1, calendar);
+
+assert.throws(RangeError, () => md.with({day: 15}));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/copies-merge-fields-object.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/copies-merge-fields-object.js
new file mode 100644
index 0000000000..36b0941bb1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/copies-merge-fields-object.js
@@ -0,0 +1,34 @@
+// |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.plainmonthday.prototype.with
+description: The object returned from mergeFields() is copied before being passed to monthDayFromFields().
+info: |
+ sec-temporal.plainmonthday.prototype.with steps 13–15:
+ 13. Set _fields_ to ? CalendarMergeFields(_calendar_, _fields_, _partialMonthDay_).
+ 14. Set _fields_ to ? PrepareTemporalFields(_fields_, _fieldNames_, «»).
+ 15. Return ? MonthDayFromFields(_calendar_, _fields_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "get day",
+ "get day.valueOf",
+ "call day.valueOf",
+ "get month", // PlainMonthDay.month property does not exist, no valueOf
+ "get monthCode",
+ "get monthCode.toString",
+ "call monthCode.toString",
+ "get year", // undefined, no valueOf
+];
+
+const calendar = TemporalHelpers.calendarMergeFieldsGetters();
+const monthday = new Temporal.PlainMonthDay(3, 31, calendar);
+monthday.with({ day: 1 });
+
+assert.compareArray(calendar.mergeFieldsReturnOperations, expected, "getters called on mergeFields return");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/copy-properties-not-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/copy-properties-not-undefined.js
new file mode 100644
index 0000000000..8458c5bb3b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/copy-properties-not-undefined.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.plainmonthday.prototype.with
+description: PreparePartialTemporalFields copies only defined properties of source object
+info: |
+ 4. For each value _property_ of _fieldNames_, do
+ a. Let _value_ be ? Get(_fields_, _property_).
+ b. If _value_ is not *undefined*, then
+ ...
+ iii. Perform ! CreateDataPropertyOrThrow(_result_, _property_, _value_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainMonthDay = new Temporal.PlainMonthDay(10, 31);
+
+TemporalHelpers.assertPlainMonthDay(plainMonthDay.with({ day: 1, monthCode: undefined }),
+ "M10", 1,
+ "only the properties that are present and defined in the plain object are copied"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/duplicate-calendar-fields.js
new file mode 100644
index 0000000000..566b013308
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/duplicate-calendar-fields.js
@@ -0,0 +1,18 @@
+// |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.plainmonthday.prototype.with
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['monthCode'], ['day'], ['month'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const md = new Temporal.PlainMonthDay(5, 1, calendar);
+
+ assert.throws(RangeError, () => md.with({day: 15}));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..bc7b3da926
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/infinity-throws-rangeerror.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.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plainmonthday.prototype.with
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainMonthDay(5, 2);
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["constrain", "reject"].forEach((overflow) => {
+ assert.throws(RangeError, () => instance.with({ day: inf }, { overflow }), `day property cannot be ${inf} (overflow ${overflow}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, "day");
+ assert.throws(RangeError, () => instance.with({ day: obj }, { overflow }));
+ assert.compareArray(calls, ["get day.valueOf", "call day.valueOf"], "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/length.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/length.js
new file mode 100644
index 0000000000..dd129874fa
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/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.plainmonthday.prototype.with
+description: Temporal.PlainMonthDay.prototype.with.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.PlainMonthDay.prototype.with, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/name.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/name.js
new file mode 100644
index 0000000000..c1a4873a71
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/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.plainmonthday.prototype.with
+description: Temporal.PlainMonthDay.prototype.with.name is "with".
+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.PlainMonthDay.prototype.with, "name", {
+ value: "with",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/not-a-constructor.js
new file mode 100644
index 0000000000..d47adfe1c3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/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.plainmonthday.prototype.with
+description: >
+ Temporal.PlainMonthDay.prototype.with 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.PlainMonthDay.prototype.with();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainMonthDay.prototype.with), false,
+ "isConstructor(Temporal.PlainMonthDay.prototype.with)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/options-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/options-invalid.js
new file mode 100644
index 0000000000..d51439c247
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/options-invalid.js
@@ -0,0 +1,16 @@
+// |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.plainmonthday.prototype.with
+description: TypeError thrown when options argument is a primitive
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.PlainMonthDay(2, 2);
+[null, true, "hello", Symbol("foo"), 1, 1n].forEach((badOptions) =>
+ assert.throws(TypeError, () => instance.with({ day: 17 }, badOptions))
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/options-object.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/options-object.js
new file mode 100644
index 0000000000..29c79e3104
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/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.plainmonthday.prototype.with
+description: Empty or a function object may be used as options
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainMonthDay(5, 2);
+
+const result1 = instance.with({ day: 5 }, {});
+TemporalHelpers.assertPlainMonthDay(
+ result1, "M05", 5,
+ "options may be an empty plain object"
+);
+
+const result2 = instance.with({ day: 5 }, () => {});
+TemporalHelpers.assertPlainMonthDay(
+ result2, "M05", 5,
+ "options may be a function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/options-undefined.js
new file mode 100644
index 0000000000..24134ef2ba
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/options-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.plainmonthday.prototype.with
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const monthday = new Temporal.PlainMonthDay(2, 2);
+const fields = { day: 100 };
+
+const explicit = monthday.with(fields, undefined);
+assert.sameValue(explicit.day, 29, "default overflow is constrain");
+
+const implicit = monthday.with(fields);
+assert.sameValue(implicit.day, 29, "default overflow is constrain");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/options-wrong-type.js
new file mode 100644
index 0000000000..c4026fa84c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/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.plainmonthday.prototype.with
+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.PlainMonthDay(5, 2);
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.with({ day: 5 }, value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/order-of-operations.js
new file mode 100644
index 0000000000..2311bf7a6f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/order-of-operations.js
@@ -0,0 +1,76 @@
+// |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.plainmonthday.prototype.with
+description: Properties on an object passed to with() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ // RejectObjectWithCalendarOrTimeZone
+ "get fields.calendar",
+ "get fields.timeZone",
+ // CopyDataProperties
+ "ownKeys options",
+ "getOwnPropertyDescriptor options.overflow",
+ "get options.overflow",
+ "getOwnPropertyDescriptor options.extra",
+ "get options.extra",
+ // lookup
+ "get this.calendar.fields",
+ "get this.calendar.mergeFields",
+ "get this.calendar.monthDayFromFields",
+ // CalendarFields
+ "call this.calendar.fields",
+ // PrepareTemporalFields on receiver
+ "get this.calendar.day",
+ "call this.calendar.day",
+ "get this.calendar.monthCode",
+ "call this.calendar.monthCode",
+ // PrepareTemporalFields on argument
+ "get fields.day",
+ "get fields.day.valueOf",
+ "call fields.day.valueOf",
+ "get fields.month",
+ "get fields.month.valueOf",
+ "call fields.month.valueOf",
+ "get fields.monthCode",
+ "get fields.monthCode.toString",
+ "call fields.monthCode.toString",
+ "get fields.year",
+ "get fields.year.valueOf",
+ "call fields.year.valueOf",
+ // CalendarMergeFields
+ "call this.calendar.mergeFields",
+ // CalendarMonthDayFromFields
+ "call this.calendar.monthDayFromFields",
+ // inside Calendar.p.monthDayFromFields
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+];
+const actual = [];
+
+const calendar = TemporalHelpers.calendarObserver(actual, "this.calendar");
+const instance = new Temporal.PlainMonthDay(5, 2, calendar);
+// clear observable operations that occurred during the constructor call
+actual.splice(0);
+
+const fields = TemporalHelpers.propertyBagObserver(actual, {
+ year: 1.7,
+ month: 1.7,
+ monthCode: "M01",
+ day: 1.7,
+}, "fields");
+
+const options = TemporalHelpers.propertyBagObserver(actual, {
+ overflow: "constrain",
+ extra: "property",
+}, "options");
+
+instance.with(fields, options);
+assert.compareArray(actual, expected, "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/overflow-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/overflow-invalid-string.js
new file mode 100644
index 0000000000..bc845f5bb7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/overflow-invalid-string.js
@@ -0,0 +1,31 @@
+// |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.plainmonthday.prototype.with
+description: RangeError thrown when overflow option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-isomonthdayfromfields step 2:
+ 2. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plainmonthday.prototype.with step 16:
+ 16. Return ? MonthDayFromFields(_calendar_, _fields_, _options_).
+features: [Temporal]
+---*/
+
+const monthday = new Temporal.PlainMonthDay(5, 2);
+
+const badOverflows = ["", "CONSTRAIN", "balance", "other string", "constra\u0131n", "reject\0"];
+for (const overflow of badOverflows) {
+ assert.throws(
+ RangeError,
+ () => monthday.with({ day: 8 }, { overflow }),
+ `invalid overflow ("${overflow}")`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/overflow-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/overflow-undefined.js
new file mode 100644
index 0000000000..0f93fe809d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/overflow-undefined.js
@@ -0,0 +1,29 @@
+// |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.plainmonthday.prototype.with
+description: Fallback value for overflow option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-isomonthdayfromfields step 2:
+ 2. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plainmonthday.prototype.with step 16:
+ 16. Return ? MonthDayFromFields(_calendar_, _fields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const monthday = new Temporal.PlainMonthDay(5, 2);
+const explicit = monthday.with({ day: 33 }, { overflow: undefined });
+TemporalHelpers.assertPlainMonthDay(explicit, "M05", 31, "default overflow is constrain");
+const implicit = monthday.with({ day: 33 }, {});
+TemporalHelpers.assertPlainMonthDay(implicit, "M05", 31, "default overflow is constrain");
+const lambda = monthday.with({ day: 33 }, () => {});
+TemporalHelpers.assertPlainMonthDay(lambda, "M05", 31, "default overflow is constrain");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/overflow-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/overflow-wrong-type.js
new file mode 100644
index 0000000000..f6d4a649e8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/overflow-wrong-type.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.plainmonthday.prototype.with
+description: Type conversions for overflow option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-isomonthdayfromfields step 2:
+ 2. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plainmonthday.prototype.with step 16:
+ 16. Return ? MonthDayFromFields(_calendar_, _fields_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const monthday = new Temporal.PlainMonthDay(5, 2);
+TemporalHelpers.checkStringOptionWrongType("overflow", "constrain",
+ (overflow) => monthday.with({ day: 8 }, { overflow }),
+ (result, descr) => TemporalHelpers.assertPlainMonthDay(result, "M05", 8, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/prop-desc.js
new file mode 100644
index 0000000000..32126c982f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/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.plainmonthday.prototype.with
+description: The "with" property of Temporal.PlainMonthDay.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainMonthDay.prototype.with,
+ "function",
+ "`typeof PlainMonthDay.prototype.with` is `function`"
+);
+
+verifyProperty(Temporal.PlainMonthDay.prototype, "with", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/proto-in-calendar-fields.js
new file mode 100644
index 0000000000..805706edb6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/proto-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |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.plainmonthday.prototype.with
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const md = new Temporal.PlainMonthDay(5, 1, calendar);
+
+assert.throws(RangeError, () => md.with({day: 15}));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/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/PlainMonthDay/prototype/with/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/subclassing-ignored.js
new file mode 100644
index 0000000000..a232d529d0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/with/subclassing-ignored.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.plainmonthday.prototype.with
+description: Objects of a subclass are never created as return values for with()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.PlainMonthDay,
+ [5, 2],
+ "with",
+ [{ day: 20 }],
+ (result) => TemporalHelpers.assertPlainMonthDay(result, "M05", 20),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/refisoyear-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/refisoyear-out-of-range.js
new file mode 100644
index 0000000000..bf6049a1cb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/refisoyear-out-of-range.js
@@ -0,0 +1,16 @@
+// |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.plainmonthday
+description: referenceISOYear argument, if given, can cause RangeError
+features: [Temporal]
+---*/
+
+const calendar = new Temporal.Calendar("iso8601");
+
+assert.throws(RangeError, () => new Temporal.PlainMonthDay(9, 14, calendar, 275760), "after the maximum ISO date");
+assert.throws(RangeError, () => new Temporal.PlainMonthDay(4, 18, calendar, -271821), "before the minimum ISO date")
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/refisoyear-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/refisoyear-undefined.js
new file mode 100644
index 0000000000..572d76a1e2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/refisoyear-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.plainmonthday
+description: referenceISOYear argument defaults to 1972 if not given
+features: [Temporal]
+---*/
+
+const calendar = new Temporal.Calendar("iso8601");
+const args = [5, 2, calendar];
+
+const dateExplicit = new Temporal.PlainMonthDay(...args, undefined);
+assert.sameValue(dateExplicit.getISOFields().isoYear, 1972, "default referenceISOYear is 1972");
+
+const dateImplicit = new Temporal.PlainMonthDay(...args);
+assert.sameValue(dateImplicit.getISOFields().isoYear, 1972, "default referenceISOYear is 1972");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/subclass.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/subclass.js
new file mode 100644
index 0000000000..8040302fb0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/subclass.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.plainmonthday
+description: Test for Temporal.PlainMonthDay subclassing.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+class CustomPlainMonthDay extends Temporal.PlainMonthDay {
+}
+
+const instance = new CustomPlainMonthDay(5, 2);
+TemporalHelpers.assertPlainMonthDay(instance, "M05", 2);
+assert.sameValue(Object.getPrototypeOf(instance), CustomPlainMonthDay.prototype, "Instance of CustomPlainMonthDay");
+assert(instance instanceof CustomPlainMonthDay, "Instance of CustomPlainMonthDay");
+assert(instance instanceof Temporal.PlainMonthDay, "Instance of Temporal.PlainMonthDay");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/basic.js
new file mode 100644
index 0000000000..fad1f11b39
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/basic.js
@@ -0,0 +1,16 @@
+// |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
+description: Basic tests for the PlainTime constructor.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const args = [15, 23, 30, 123, 456, 789];
+const plainTime = new Temporal.PlainTime(...args);
+TemporalHelpers.assertPlainTime(plainTime, ...args);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/builtin.js
new file mode 100644
index 0000000000..26de31a9aa
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/builtin.js
@@ -0,0 +1,30 @@
+// |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
+description: Tests that Temporal.PlainTime meets the requirements for built-in objects
+info: |
+ 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),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainTime),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainTime),
+ Function.prototype, "prototype");
+
+assert.sameValue(typeof Temporal.PlainTime.prototype,
+ "object", "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-cast.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-cast.js
new file mode 100644
index 0000000000..d253be6629
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-cast.js
@@ -0,0 +1,22 @@
+// |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.compare
+description: compare() casts its arguments
+features: [Temporal]
+---*/
+
+const t1 = Temporal.PlainTime.from("08:44:15.321");
+const t2 = Temporal.PlainTime.from("14:23:30.123");
+
+assert.sameValue(Temporal.PlainTime.compare({ hour: 16, minute: 34 }, t2), 1, "one object");
+assert.sameValue(Temporal.PlainTime.compare("16:34", t2), 1, "one string");
+assert.throws(TypeError, () => Temporal.PlainTime.compare({ hours: 16 }, t2), "one missing property");
+
+assert.sameValue(Temporal.PlainTime.compare(t1, { hour: 16, minute: 34 }), -1, "two object");
+assert.sameValue(Temporal.PlainTime.compare(t1, "16:34"), -1, "two string");
+assert.throws(TypeError, () => Temporal.PlainTime.compare(t1, { hours: 16 }), "two missing property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-number.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-number.js
new file mode 100644
index 0000000000..91e29a6769
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-number.js
@@ -0,0 +1,31 @@
+// |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.compare
+description: A number is invalid in place of an ISO string for Temporal.PlainTime
+features: [Temporal]
+---*/
+
+const numbers = [
+ 1,
+ -123456.987654321,
+ 1234567,
+ 123456.9876543219,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => Temporal.PlainTime.compare(arg, new Temporal.PlainTime(12, 34, 56, 987, 654, 321)),
+ `A number (${arg}) is not a valid ISO string for PlainTime (first argument)`
+ );
+ assert.throws(
+ TypeError,
+ () => Temporal.PlainTime.compare(new Temporal.PlainTime(12, 34, 56, 987, 654, 321), arg),
+ `A number (${arg}) is not a valid ISO string for PlainTime (second argument)`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..7d2b108223
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-calendar-annotation.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.compare
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+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"],
+ ["1970-01-01T12:34:56.987654321[u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.PlainTime.compare(arg, arg);
+
+ assert.sameValue(
+ result,
+ 0,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..d553e57b96
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-critical-unknown-annotation.js
@@ -0,0 +1,34 @@
+// |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.compare
+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]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainTime.compare(arg, new Temporal.PlainTime(12, 34, 56, 987, 654, 321)),
+ `reject unknown annotation with critical flag: ${arg} (first argument)`
+ );
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainTime.compare(new Temporal.PlainTime(12, 34, 56, 987, 654, 321), arg),
+ `reject unknown annotation with critical flag: ${arg} (second argument)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..08ceab76c6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-date-with-utc-offset.js
@@ -0,0 +1,55 @@
+// |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.compare
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+---*/
+
+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 = Temporal.PlainTime.compare(arg, arg);
+
+ assert.sameValue(
+ result,
+ 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,
+ () => Temporal.PlainTime.compare(arg, new Temporal.PlainTime(12, 34, 56, 987, 654, 321)),
+ `"${arg}" UTC offset without time is not valid for PlainTime (first argument)`
+ );
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainTime.compare(new Temporal.PlainTime(12, 34, 56, 987, 654, 321), arg),
+ `"${arg}" UTC offset without time is not valid for PlainTime (second argument)`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..5ce6745e94
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-multiple-calendar.js
@@ -0,0 +1,37 @@
+// |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.compare
+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]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainTime.compare(arg, new Temporal.PlainTime(12, 34, 56, 987, 654, 321)),
+ `reject more than one calendar annotation if any critical: ${arg} (first argument)`
+ );
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainTime.compare(new Temporal.PlainTime(12, 34, 56, 987, 654, 321), arg),
+ `reject more than one calendar annotation if any critical: ${arg} (first argument)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..2343613c5a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-multiple-time-zone.js
@@ -0,0 +1,34 @@
+// |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.compare
+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]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainTime.compare(arg, new Temporal.PlainTime(12, 34, 56, 987, 654, 321)),
+ `reject more than one time zone annotation: ${arg} (first argument)`
+ );
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainTime.compare(new Temporal.PlainTime(12, 34, 56, 987, 654, 321), arg),
+ `reject more than one time zone annotation: ${arg} (second argument)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-no-implicit-midnight.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-no-implicit-midnight.js
new file mode 100644
index 0000000000..67e51e9a45
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-no-implicit-midnight.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.compare
+description: RangeError thrown if a date-only string is passed in a PlainTime context
+features: [Temporal, arrow-function]
+---*/
+
+const arg = "2019-10-01";
+const midnight = new Temporal.PlainTime();
+assert.throws(
+ RangeError,
+ () => Temporal.PlainTime.compare(arg, midnight),
+ "Date-only string throws, does not implicitly convert to midnight (first argument)"
+);
+assert.throws(
+ RangeError,
+ () => Temporal.PlainTime.compare(midnight, arg),
+ "Date-only string throws, does not implicitly convert to midnight (second argument)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-time-designator-required-for-disambiguation.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-time-designator-required-for-disambiguation.js
new file mode 100644
index 0000000000..d6a099db5e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-time-designator-required-for-disambiguation.js
@@ -0,0 +1,50 @@
+// |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.compare
+description: ISO 8601 time designator "T" required in cases of ambiguity
+includes: [temporalHelpers.js]
+features: [Temporal, arrow-function]
+---*/
+
+const midnight = new Temporal.PlainTime();
+
+TemporalHelpers.ISO.plainTimeStringsAmbiguous().forEach((string) => {
+ let arg = string;
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainTime.compare(arg, midnight),
+ `'${arg}' is ambiguous and requires T prefix (first argument)`
+ );
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainTime.compare(midnight, arg),
+ `'${arg}' is ambiguous and requires T prefix (second argument)`
+ );
+ // The same string with a T prefix should not throw:
+ arg = `T${string}`;
+ Temporal.PlainTime.compare(arg, midnight);
+ Temporal.PlainTime.compare(midnight, arg);
+
+ arg = ` ${string}`;
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainTime.compare(arg, midnight),
+ `space is not accepted as a substitute for T prefix (first argument): '${arg}'`
+ );
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainTime.compare(midnight, arg),
+ `space is not accepted as a substitute for T prefix (second argument): '${arg}'`
+ );
+});
+
+// None of these should throw without a T prefix, because they are unambiguously time strings:
+TemporalHelpers.ISO.plainTimeStringsUnambiguous().forEach((arg) => {
+ Temporal.PlainTime.compare(arg, midnight);
+ Temporal.PlainTime.compare(midnight, arg);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-time-separators.js
new file mode 100644
index 0000000000..d98f91a43a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-time-separators.js
@@ -0,0 +1,34 @@
+// |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.compare
+description: Time separator in string argument can vary
+features: [Temporal]
+---*/
+
+const plainTime = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+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"],
+];
+
+tests.forEach(([arg, description]) => {
+ assert.sameValue(
+ Temporal.PlainTime.compare(arg, plainTime),
+ 0,
+ `variant time separators (${description}), first argument`
+ );
+
+ assert.sameValue(
+ Temporal.PlainTime.compare(plainTime, arg),
+ 0,
+ `variant time separators (${description}), second argument`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..272daa9d91
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-time-zone-annotation.js
@@ -0,0 +1,48 @@
+// |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.compare
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+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 !"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.PlainTime.compare(arg, arg);
+
+ assert.sameValue(
+ result,
+ 0,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..ddfba8993f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-unknown-annotation.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.compare
+description: Various forms of unknown annotation
+features: [Temporal]
+---*/
+
+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"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.PlainTime.compare(arg, arg);
+
+ assert.sameValue(
+ result,
+ 0,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-with-time-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-with-time-designator.js
new file mode 100644
index 0000000000..3bfde29db9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-with-time-designator.js
@@ -0,0 +1,31 @@
+// |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.compare
+description: ISO 8601 time designator "T" allowed at the start of PlainTime strings
+features: [Temporal, arrow-function]
+---*/
+
+const halfPast = new Temporal.PlainTime(0, 30);
+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) => {
+ assert.sameValue(Temporal.PlainTime.compare(arg, halfPast), 0, `T prefix is accepted: ${arg} (first argument)`);
+ assert.sameValue(Temporal.PlainTime.compare(halfPast, arg), 0, `T prefix is accepted: ${arg} (second argument)`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..abff07e6ff
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-string-with-utc-designator.js
@@ -0,0 +1,31 @@
+// |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.compare
+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 plainTime = new Temporal.PlainTime();
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainTime.compare(arg, plainTime),
+ "String with UTC designator should not be valid as a PlainTime (first argument)"
+ );
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainTime.compare(plainTime, arg),
+ "String with UTC designator should not be valid as a PlainTime (second argument)"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-wrong-type.js
new file mode 100644
index 0000000000..225a8d16c9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-wrong-type.js
@@ -0,0 +1,47 @@
+// |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.compare
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainTime
+features: [BigInt, Symbol, Temporal]
+---*/
+
+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,
+ () => Temporal.PlainTime.compare(arg, new Temporal.PlainTime(12, 34, 56, 987, 654, 321)),
+ `${description} does not convert to a valid ISO string (first argument)`
+ );
+ assert.throws(
+ typeof arg === 'string' ? RangeError : TypeError,
+ () => Temporal.PlainTime.compare(new Temporal.PlainTime(12, 34, 56, 987, 654, 321), arg),
+ `${description} does not convert to a valid ISO string (second argument)`
+ );
+}
+
+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, () => Temporal.PlainTime.compare(arg, new Temporal.PlainTime(12, 34, 56, 987, 654, 321)), `${description} is not a valid property bag and does not convert to a string (first argument)`);
+ assert.throws(TypeError, () => Temporal.PlainTime.compare(new Temporal.PlainTime(12, 34, 56, 987, 654, 321), arg), `${description} is not a valid property bag and does not convert to a string (second argument)`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-zoneddatetime-negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-zoneddatetime-negative-epochnanoseconds.js
new file mode 100644
index 0000000000..55145852e3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-zoneddatetime-negative-epochnanoseconds.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.compare
+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]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC");
+const time = new Temporal.PlainTime(16, 50, 35, 0, 0, 1);
+
+// This code path shows up anywhere we convert an exact time, before the Unix
+// epoch, with nonzero microseconds or nanoseconds, into a wall time.
+
+const result1 = Temporal.PlainTime.compare(time, datetime);
+assert.sameValue(result1, 0);
+
+const result2 = Temporal.PlainTime.compare(datetime, time);
+assert.sameValue(result2, 0);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..13008889fa
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.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.compare
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+ assert.throws(RangeError, () => Temporal.PlainTime.compare(datetime, time));
+ assert.throws(RangeError, () => Temporal.PlainTime.compare(time, datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..c16b088148
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.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.compare
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => Temporal.PlainTime.compare(datetime, time),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+ assert.throws(
+ TypeError,
+ () => Temporal.PlainTime.compare(time, 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/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..53767252b9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.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.compare
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+ assert.throws(RangeError, () => Temporal.PlainTime.compare(datetime, time));
+ assert.throws(RangeError, () => Temporal.PlainTime.compare(time, datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..22209b6693
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,30 @@
+// |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.compare
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+ assert.throws(TypeError, () => Temporal.PlainTime.compare(datetime, time));
+ assert.throws(TypeError, () => Temporal.PlainTime.compare(time, datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/basic.js
new file mode 100644
index 0000000000..212dc43f85
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/basic.js
@@ -0,0 +1,20 @@
+// |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.compare
+description: Basic tests for compare()
+features: [Temporal]
+---*/
+
+const t1 = Temporal.PlainTime.from("08:44:15.321");
+const t1bis = Temporal.PlainTime.from("08:44:15.321");
+const t2 = Temporal.PlainTime.from("14:23:30.123");
+
+assert.sameValue(Temporal.PlainTime.compare(t1, t1), 0, "same object");
+assert.sameValue(Temporal.PlainTime.compare(t1, t1bis), 0, "different object");
+assert.sameValue(Temporal.PlainTime.compare(t1, t2), -1, "before");
+assert.sameValue(Temporal.PlainTime.compare(t2, t1), 1, "after");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/builtin.js
new file mode 100644
index 0000000000..3d5821934a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/builtin.js
@@ -0,0 +1,33 @@
+// |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.compare
+description: Tests that Temporal.PlainTime.compare meets the requirements for built-in objects
+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.compare),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainTime.compare),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainTime.compare),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainTime.compare.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/exhaustive.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/exhaustive.js
new file mode 100644
index 0000000000..d711fce237
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/exhaustive.js
@@ -0,0 +1,119 @@
+// |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.compare
+description: Tests for compare() with each possible outcome
+features: [Temporal]
+---*/
+
+const cal1 = "iso8601";
+const cal2 = new (class extends Temporal.Calendar { id = "custom"; })("iso8601");
+
+assert.sameValue(
+ Temporal.PlainTime.compare(
+ new Temporal.PlainTime(12, 15, 45, 333, 777, 111, cal1),
+ new Temporal.PlainTime(6, 15, 45, 333, 777, 111, cal2)
+ ),
+ 1,
+ "hour >"
+);
+assert.sameValue(
+ Temporal.PlainTime.compare(
+ new Temporal.PlainTime(6, 30, 15, 222, 444, 6, cal1),
+ new Temporal.PlainTime(22, 30, 15, 222, 444, 6, cal2)
+ ),
+ -1,
+ "hour <"
+);
+assert.sameValue(
+ Temporal.PlainTime.compare(
+ new Temporal.PlainTime(12, 45, 15, 333, 777, 111, cal1),
+ new Temporal.PlainTime(12, 15, 22, 333, 777, 111, cal2)
+ ),
+ 1,
+ "minute >"
+);
+assert.sameValue(
+ Temporal.PlainTime.compare(
+ new Temporal.PlainTime(6, 30, 15, 222, 444, 6, cal1),
+ new Temporal.PlainTime(6, 57, 15, 222, 444, 6, cal2)
+ ),
+ -1,
+ "minute <"
+);
+assert.sameValue(
+ Temporal.PlainTime.compare(
+ new Temporal.PlainTime(12, 15, 6, 333, 777, 111, cal1),
+ new Temporal.PlainTime(12, 15, 5, 333, 777, 111, cal2)
+ ),
+ 1,
+ "second >"
+);
+assert.sameValue(
+ Temporal.PlainTime.compare(
+ new Temporal.PlainTime(6, 30, 3, 222, 444, 6, cal1),
+ new Temporal.PlainTime(6, 30, 4, 222, 444, 6, cal2)
+ ),
+ -1,
+ "second <"
+);
+assert.sameValue(
+ Temporal.PlainTime.compare(
+ new Temporal.PlainTime(12, 15, 45, 6, 777, 111, cal1),
+ new Temporal.PlainTime(12, 15, 45, 5, 777, 111, cal2)
+ ),
+ 1,
+ "millisecond >"
+);
+assert.sameValue(
+ Temporal.PlainTime.compare(
+ new Temporal.PlainTime(6, 30, 15, 3, 444, 6, cal1),
+ new Temporal.PlainTime(6, 30, 15, 4, 444, 6, cal2)
+ ),
+ -1,
+ "millisecond <"
+);
+assert.sameValue(
+ Temporal.PlainTime.compare(
+ new Temporal.PlainTime(12, 15, 45, 333, 6, 111, cal1),
+ new Temporal.PlainTime(12, 15, 45, 333, 5, 111, cal2)
+ ),
+ 1,
+ "microsecond >"
+);
+assert.sameValue(
+ Temporal.PlainTime.compare(
+ new Temporal.PlainTime(6, 30, 15, 222, 3, 6, cal1),
+ new Temporal.PlainTime(6, 30, 15, 222, 4, 6, cal2)
+ ),
+ -1,
+ "microsecond <"
+);
+assert.sameValue(
+ Temporal.PlainTime.compare(
+ new Temporal.PlainTime(12, 15, 45, 333, 777, 999, cal1),
+ new Temporal.PlainTime(12, 15, 45, 333, 777, 111, cal2)
+ ),
+ 1,
+ "nanosecond >"
+);
+assert.sameValue(
+ Temporal.PlainTime.compare(
+ new Temporal.PlainTime(6, 30, 15, 222, 444, 0, cal1),
+ new Temporal.PlainTime(6, 30, 15, 222, 444, 6, cal2)
+ ),
+ -1,
+ "nanosecond <"
+);
+assert.sameValue(
+ Temporal.PlainTime.compare(
+ new Temporal.PlainTime(12, 15, 45, 333, 777, 111, cal1),
+ new Temporal.PlainTime(12, 15, 45, 333, 777, 111, cal2)
+ ),
+ 0,
+ "="
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/leap-second.js
new file mode 100644
index 0000000000..d9d9b320dd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/leap-second.js
@@ -0,0 +1,23 @@
+// |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.compare
+description: Leap second is a valid ISO string for PlainTime
+features: [Temporal]
+---*/
+
+let arg = "2016-12-31T23:59:60";
+const result1 = Temporal.PlainTime.compare(arg, new Temporal.PlainTime(23, 59, 59));
+assert.sameValue(result1, 0, "leap second is a valid ISO string for PlainTime (first argument)");
+const result2 = Temporal.PlainTime.compare(new Temporal.PlainTime(23, 59, 59), arg);
+assert.sameValue(result2, 0, "leap second is a valid ISO string for PlainTime (first argument)");
+
+arg = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+const result3 = Temporal.PlainTime.compare(arg, new Temporal.PlainTime(23, 59, 59));
+assert.sameValue(result3, 0, "second: 60 is ignored in property bag for PlainTime (first argument)");
+const result4 = Temporal.PlainTime.compare(new Temporal.PlainTime(23, 59, 59), arg);
+assert.sameValue(result4, 0, "second: 60 is ignored in property bag for PlainTime (second argument)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/length.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/length.js
new file mode 100644
index 0000000000..e917ed6a97
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/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.compare
+description: Temporal.PlainTime.compare.length is 2
+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.compare, "length", {
+ value: 2,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/name.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/name.js
new file mode 100644
index 0000000000..ab741acddb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/name.js
@@ -0,0 +1,26 @@
+// |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.compare
+description: Temporal.PlainTime.compare.name is "compare"
+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.compare, "name", {
+ value: "compare",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/not-a-constructor.js
new file mode 100644
index 0000000000..849f81c831
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/not-a-constructor.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.compare
+description: Temporal.PlainTime.compare 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.compare();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainTime.compare), false,
+ "isConstructor(Temporal.PlainTime.compare)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/plaintime-propertybag-no-time-units.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/plaintime-propertybag-no-time-units.js
new file mode 100644
index 0000000000..24afb94488
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/plaintime-propertybag-no-time-units.js
@@ -0,0 +1,18 @@
+// |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.compare
+description: Missing time units in property bag default to 0
+features: [Temporal]
+---*/
+
+const props = {};
+assert.throws(TypeError, () => Temporal.PlainTime.compare(props, new Temporal.PlainTime(0, 30)), "TypeError if no properties are present");
+
+props.minute = 30;
+const result = Temporal.PlainTime.compare(props, new Temporal.PlainTime(0, 30));
+assert.sameValue(result, 0, "missing time units default to 0");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/prop-desc.js
new file mode 100644
index 0000000000..03225cf214
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/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.compare
+description: The "compare" property of Temporal.PlainTime
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainTime.compare,
+ "function",
+ "`typeof PlainTime.compare` is `function`"
+);
+
+verifyProperty(Temporal.PlainTime, "compare", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/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/compare/use-internal-slots.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/use-internal-slots.js
new file mode 100644
index 0000000000..67cee1bbca
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/use-internal-slots.js
@@ -0,0 +1,38 @@
+// |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-comparetemporaltime
+description: compare() ignores the observable properties and uses internal slots
+features: [Temporal]
+---*/
+
+function CustomError() {}
+
+class AvoidGettersTime extends Temporal.PlainTime {
+ get hour() {
+ throw new CustomError();
+ }
+ get minute() {
+ throw new CustomError();
+ }
+ get second() {
+ throw new CustomError();
+ }
+ get millisecond() {
+ throw new CustomError();
+ }
+ get microsecond() {
+ throw new CustomError();
+ }
+ get nanosecond() {
+ throw new CustomError();
+ }
+}
+
+const one = new AvoidGettersTime(12, 34, 56, 987, 654, 321);
+const two = new AvoidGettersTime(6, 54, 32, 123, 456, 789);
+assert.sameValue(Temporal.PlainTime.compare(one, two), 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/year-zero.js
new file mode 100644
index 0000000000..5ec802a047
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/year-zero.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.compare
+description: Negative zero, as an extended year, fails
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+const invalidStrings = [
+ "-000000-12-07T03:24:30",
+ "-000000-12-07T03:24:30+01:00",
+ "-000000-12-07T03:24:30+00:00[UTC]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainTime.compare(arg, time),
+ "Cannot use minus zero as extended year (first argument)"
+ );
+
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainTime.compare(time, arg),
+ "Cannot use minus zero as extended year (second argument)"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/constructor.js
new file mode 100644
index 0000000000..85b3df5864
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/constructor.js
@@ -0,0 +1,15 @@
+// |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
+description: Temporal.PlainTime constructor cannot be called as a function
+info: |
+ 1. If NewTarget is undefined, throw a TypeError exception.
+features: [Temporal]
+---*/
+
+assert.throws(TypeError, () => Temporal.PlainTime(12, 0, 0));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-number.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-number.js
new file mode 100644
index 0000000000..38a4faf253
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-number.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.from
+description: A number is invalid in place of an ISO string for Temporal.PlainTime
+features: [Temporal]
+---*/
+
+const numbers = [
+ 1,
+ -123456.987654321,
+ 1234567,
+ 123456.9876543219,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => Temporal.PlainTime.from(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/from/argument-object-leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-object-leap-second.js
new file mode 100644
index 0000000000..edf7e02e9b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-object-leap-second.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.from
+description: Object argument handles leap seconds according to the overflow option.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const options of [undefined, {}, { overflow: "constrain" }]) {
+ TemporalHelpers.assertPlainTime(Temporal.PlainTime.from({ hour: 23, minute: 59, second: 60 }, options),
+ 23, 59, 59, 0, 0, 0);
+ TemporalHelpers.assertPlainTime(Temporal.PlainTime.from({ hour: 12, minute: 30, second: 60 }, options),
+ 12, 30, 59, 0, 0, 0);
+ TemporalHelpers.assertPlainTime(Temporal.PlainTime.from({ hour: 23, minute: 59, second: 60, millisecond: 170 }, options),
+ 23, 59, 59, 170, 0, 0);
+}
+
+const options = { overflow: "reject" };
+assert.throws(RangeError, () => Temporal.PlainTime.from({ hour: 23, minute: 59, second: 60 }, options));
+assert.throws(RangeError, () => Temporal.PlainTime.from({ hour: 12, minute: 30, second: 60 }, options));
+assert.throws(RangeError, () => Temporal.PlainTime.from({ hour: 23, minute: 59, second: 60, millisecond: 170 }, options));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-object.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-object.js
new file mode 100644
index 0000000000..56a7af892e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-object.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.from
+description: Plain object argument is supported and ignores plural properties
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.assertPlainTime(Temporal.PlainTime.from({ hour: 15, minute: 23 }),
+ 15, 23, 0, 0, 0, 0);
+TemporalHelpers.assertPlainTime(Temporal.PlainTime.from({ minute: 30, microsecond: 555 }),
+ 0, 30, 0, 0, 555, 0);
+TemporalHelpers.assertPlainTime(Temporal.PlainTime.from({ year: 2019, month: 10, day: 1, hour: 14, minute: 20, second: 36 }),
+ 14, 20, 36, 0, 0, 0);
+TemporalHelpers.assertPlainTime(Temporal.PlainTime.from({ hours: 2, minute: 30, microsecond: 555 }),
+ 0, 30, 0, 0, 555, 0);
+
+assert.throws(TypeError, () => Temporal.PlainTime.from({}));
+assert.throws(TypeError, () => Temporal.PlainTime.from({ minutes: 12 }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-plaindatetime.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-plaindatetime.js
new file mode 100644
index 0000000000..920c3150ac
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-plaindatetime.js
@@ -0,0 +1,17 @@
+// |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.from
+description: Fast path for converting Temporal.PlainDateTime to Temporal.PlainTime by reading internal slots
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkPlainDateTimeConversionFastPath((plainDateTime) => {
+ const result = Temporal.PlainTime.from(plainDateTime);
+ TemporalHelpers.assertPlainTime(result, 12, 34, 56, 987, 654, 321);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-plaintime.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-plaintime.js
new file mode 100644
index 0000000000..33fe44b8d0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-plaintime.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.from
+description: A PlainTime object is copied, not returned directly
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const orig = new Temporal.PlainTime(11, 42, 0, 0, 0, 0);
+const result = Temporal.PlainTime.from(orig);
+
+TemporalHelpers.assertPlainTime(
+ result,
+ 11, 42, 0, 0, 0, 0,
+ "PlainTime is copied"
+);
+
+assert.notSameValue(
+ result,
+ orig,
+ "When a PlainTime is given, the returned value is not the original PlainTime"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..917b78d3ed
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-calendar-annotation.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.from
+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"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.PlainTime.from(arg);
+
+ TemporalHelpers.assertPlainTime(
+ result,
+ 12, 34, 56, 987, 654, 321,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..ed8825b591
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/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.from
+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]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainTime.from(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..d715e51791
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-date-with-utc-offset.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.from
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+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 = Temporal.PlainTime.from(arg);
+
+ TemporalHelpers.assertPlainTime(
+ result,
+ 12, 34, 56, 987, 654, 321,
+ `"${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,
+ () => Temporal.PlainTime.from(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/from/argument-string-leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-leap-second.js
new file mode 100644
index 0000000000..ef9582da8b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-leap-second.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.from
+description: Leap second is replaced by :59 in ISO strings.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const options of [undefined, {}, { overflow: "constrain" }, { overflow: "reject" }]) {
+ TemporalHelpers.assertPlainTime(Temporal.PlainTime.from("23:59:60", options),
+ 23, 59, 59, 0, 0, 0);
+ TemporalHelpers.assertPlainTime(Temporal.PlainTime.from("12:30:60", options),
+ 12, 30, 59, 0, 0, 0);
+ TemporalHelpers.assertPlainTime(Temporal.PlainTime.from("23:59:60.170", options),
+ 23, 59, 59, 170, 0, 0);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..3e3f964b70
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/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.from
+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]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainTime.from(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/from/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..6ed71fbbc4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/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.from
+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]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainTime.from(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-no-implicit-midnight.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-no-implicit-midnight.js
new file mode 100644
index 0000000000..3555c8f350
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-no-implicit-midnight.js
@@ -0,0 +1,18 @@
+// |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.from
+description: RangeError thrown if a date-only string is passed in a PlainTime context
+features: [Temporal, arrow-function]
+---*/
+
+const arg = "2019-10-01";
+assert.throws(
+ RangeError,
+ () => Temporal.PlainTime.from(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/from/argument-string-time-designator-required-for-disambiguation.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-time-designator-required-for-disambiguation.js
new file mode 100644
index 0000000000..30a22c3072
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-time-designator-required-for-disambiguation.js
@@ -0,0 +1,35 @@
+// |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.from
+description: ISO 8601 time designator "T" required in cases of ambiguity
+includes: [temporalHelpers.js]
+features: [Temporal, arrow-function]
+---*/
+
+TemporalHelpers.ISO.plainTimeStringsAmbiguous().forEach((string) => {
+ let arg = string;
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainTime.from(arg),
+ `'${arg}' is ambiguous and requires T prefix`
+ );
+ // The same string with a T prefix should not throw:
+ arg = `T${string}`;
+ Temporal.PlainTime.from(arg);
+
+ arg = ` ${string}`;
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainTime.from(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) => Temporal.PlainTime.from(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-time-separators.js
new file mode 100644
index 0000000000..2e2645a48e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-time-separators.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.from
+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"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.PlainTime.from(arg);
+
+ TemporalHelpers.assertPlainTime(
+ result,
+ 12, 34, 56, 987, 654, 321,
+ `variant time separators (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..6459e18647
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-time-zone-annotation.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.from
+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 !"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.PlainTime.from(arg);
+
+ TemporalHelpers.assertPlainTime(
+ result,
+ 12, 34, 56, 987, 654, 321,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-trailing-junk.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-trailing-junk.js
new file mode 100644
index 0000000000..782b168939
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-trailing-junk.js
@@ -0,0 +1,14 @@
+// |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.from
+description: RangeError thrown if a string argument has trailing junk
+features: [Temporal, arrow-function]
+---*/
+
+const arg = "15:23:30.100junk";
+assert.throws(RangeError, () => Temporal.PlainTime.from(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..7af06b9583
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-unknown-annotation.js
@@ -0,0 +1,38 @@
+// |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.from
+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"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.PlainTime.from(arg);
+
+ TemporalHelpers.assertPlainTime(
+ result,
+ 12, 34, 56, 987, 654, 321,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-with-time-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-with-time-designator.js
new file mode 100644
index 0000000000..865af83c0d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-with-time-designator.js
@@ -0,0 +1,31 @@
+// |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.from
+description: ISO 8601 time designator "T" allowed at the start of PlainTime strings
+includes: [temporalHelpers.js]
+features: [Temporal, arrow-function]
+---*/
+
+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 = Temporal.PlainTime.from(arg);
+ TemporalHelpers.assertPlainTime(result, 0, 30, 0, 0, 0, 0, `T prefix is accepted: ${arg}`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..7a8e7ad7e9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-with-utc-designator.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.from
+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",
+];
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainTime.from(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/from/argument-string.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string.js
new file mode 100644
index 0000000000..b96d370c32
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string.js
@@ -0,0 +1,60 @@
+// |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.from
+description: Various ISO strings supported
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const tests = [
+ ["15:23", 15, 23, 0, 0, 0, 0],
+ ["15:23:30", 15, 23, 30, 0, 0, 0],
+ ["15:23:30.123", 15, 23, 30, 123, 0, 0],
+ ["15:23:30.123456", 15, 23, 30, 123, 456, 0],
+ ["15:23:30.123456789", 15, 23, 30, 123, 456, 789],
+ ["1976-11-18T15:23:30.1", 15, 23, 30, 100, 0, 0],
+ ["1976-11-18T15:23:30.12", 15, 23, 30, 120, 0, 0],
+ ["1976-11-18T15:23:30.123", 15, 23, 30, 123, 0, 0],
+ ["1976-11-18T15:23:30.1234", 15, 23, 30, 123, 400, 0],
+ ["1976-11-18T15:23:30.12345", 15, 23, 30, 123, 450, 0],
+ ["1976-11-18T15:23:30.123456", 15, 23, 30, 123, 456, 0],
+ ["1976-11-18T15:23:30.1234567", 15, 23, 30, 123, 456, 700],
+ ["1976-11-18T15:23:30.12345678", 15, 23, 30, 123, 456, 780],
+ ["1976-11-18T15:23:30.123456789", 15, 23, 30, 123, 456, 789],
+ ["1976-11-18T15:23:30,12", 15, 23, 30, 120, 0, 0],
+ ["1976-11-18T15:23:30.12\u221202:00", 15, 23, 30, 120, 0, 0],
+ ["152330", 15, 23, 30, 0, 0, 0],
+ ["152330.1", 15, 23, 30, 100, 0, 0],
+ ["152330-08", 15, 23, 30, 0, 0, 0],
+ ["152330.1-08", 15, 23, 30, 100, 0, 0],
+ ["152330-0800", 15, 23, 30, 0, 0, 0],
+ ["152330.1-0800", 15, 23, 30, 100, 0, 0],
+ ["1976-11-18T152330.1+00:00", 15, 23, 30, 100, 0, 0],
+ ["19761118T15:23:30.1+00:00", 15, 23, 30, 100, 0, 0],
+ ["1976-11-18T15:23:30.1+0000", 15, 23, 30, 100, 0, 0],
+ ["1976-11-18T152330.1+0000", 15, 23, 30, 100, 0, 0],
+ ["19761118T15:23:30.1+0000", 15, 23, 30, 100, 0, 0],
+ ["19761118T152330.1+00:00", 15, 23, 30, 100, 0, 0],
+ ["19761118T152330.1+0000", 15, 23, 30, 100, 0, 0],
+ ["+001976-11-18T152330.1+00:00", 15, 23, 30, 100, 0, 0],
+ ["+0019761118T15:23:30.1+00:00", 15, 23, 30, 100, 0, 0],
+ ["+001976-11-18T15:23:30.1+0000", 15, 23, 30, 100, 0, 0],
+ ["+001976-11-18T152330.1+0000", 15, 23, 30, 100, 0, 0],
+ ["+0019761118T15:23:30.1+0000", 15, 23, 30, 100, 0, 0],
+ ["+0019761118T152330.1+00:00", 15, 23, 30, 100, 0, 0],
+ ["+0019761118T152330.1+0000", 15, 23, 30, 100, 0, 0],
+ ["15", 15, 0, 0, 0, 0, 0],
+ ["T15:23:30", 15, 23, 30, 0, 0, 0],
+ ["t152330", 15, 23, 30, 0, 0, 0],
+];
+
+for (const [input, ...expected] of tests) {
+ const result = Temporal.PlainTime.from(input);
+ assert.sameValue(expected.length, 6, input);
+ TemporalHelpers.assertPlainTime(result, ...expected, input);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-wrong-type.js
new file mode 100644
index 0000000000..4a3b01707d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-wrong-type.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.from
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainTime
+features: [BigInt, Symbol, Temporal]
+---*/
+
+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,
+ () => Temporal.PlainTime.from(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, () => Temporal.PlainTime.from(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/from/argument-zoneddatetime-balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-zoneddatetime-balance-negative-time-units.js
new file mode 100644
index 0000000000..12356ae9e9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/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.from
+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.from step 4:
+ 4. Return ? ToTemporalTime(_temporalTime_).
+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 time = Temporal.PlainTime.from(datetime);
+
+TemporalHelpers.assertPlainTime(time, 1, 1, 1, 1, 0, 999);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-zoneddatetime-negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-zoneddatetime-negative-epochnanoseconds.js
new file mode 100644
index 0000000000..e442577be0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-zoneddatetime-negative-epochnanoseconds.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.from
+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 result = Temporal.PlainTime.from(datetime);
+TemporalHelpers.assertPlainTime(result, 16, 50, 35, 0, 0, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..3cc1f97e08
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,18 @@
+// |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.from
+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].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => Temporal.PlainTime.from(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..c5cfaeb4b2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.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.from
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => Temporal.PlainTime.from(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/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..f9d35b416f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,18 @@
+// |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.from
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => Temporal.PlainTime.from(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..86c7e90fd6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.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.from
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => Temporal.PlainTime.from(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/builtin.js
new file mode 100644
index 0000000000..05d60e28ff
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/builtin.js
@@ -0,0 +1,33 @@
+// |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.from
+description: Tests that Temporal.PlainTime.from meets the requirements for built-in objects
+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.from),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainTime.from),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainTime.from),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainTime.from.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..a605d6c2d3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/infinity-throws-rangeerror.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.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plaintime.from
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const base = { hour: 15, minute: 30, second: 45, millisecond: 987, microsecond: 654, nanosecond: 321 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((prop) => {
+ ["constrain", "reject"].forEach((overflow) => {
+ assert.throws(RangeError, () => Temporal.PlainTime.from({ ...base, [prop]: inf }, { overflow }), `${prop} property cannot be ${inf} (overflow ${overflow}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => Temporal.PlainTime.from({ ...base, [prop]: obj }, { overflow }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/leap-second.js
new file mode 100644
index 0000000000..22694cb864
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/leap-second.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.from
+description: Leap second is a valid ISO string for PlainTime
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+let arg = "2016-12-31T23:59:60";
+
+const result1 = Temporal.PlainTime.from(arg);
+TemporalHelpers.assertPlainTime(
+ result1,
+ 23, 59, 59, 0, 0, 0,
+ "leap second is a valid ISO string for PlainTime"
+);
+
+const result2 = Temporal.PlainTime.from(arg, { overflow: "reject" });
+TemporalHelpers.assertPlainTime(
+ result2,
+ 23, 59, 59, 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 result3 = Temporal.PlainTime.from(arg);
+TemporalHelpers.assertPlainTime(
+ result3,
+ 23, 59, 59, 0, 0, 0,
+ "second: 60 is ignored in property bag for PlainTime"
+);
+
+assert.throws(
+ RangeError,
+ () => Temporal.PlainTime.from(arg, { overflow: "reject" }),
+ "second: 60 is rejected in property bag for PlainTime with overflow: reject"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/length.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/length.js
new file mode 100644
index 0000000000..cae3856a6e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/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.from
+description: Temporal.PlainTime.from.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.from, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/name.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/name.js
new file mode 100644
index 0000000000..ecd305e0a4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/name.js
@@ -0,0 +1,26 @@
+// |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.from
+description: Temporal.PlainTime.from.name is "from"
+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.from, "name", {
+ value: "from",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/not-a-constructor.js
new file mode 100644
index 0000000000..c93e2cfba9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/not-a-constructor.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.from
+description: Temporal.PlainTime.from 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.from();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainTime.from), false,
+ "isConstructor(Temporal.PlainTime.from)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/observable-get-overflow-argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/observable-get-overflow-argument-string-invalid.js
new file mode 100644
index 0000000000..54a4045b5f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/observable-get-overflow-argument-string-invalid.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.from
+description: overflow property is extracted with ISO-invalid string argument.
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "get overflow",
+ "get overflow.toString",
+ "call overflow.toString",
+];
+
+let actual = [];
+const object = {
+ get overflow() {
+ actual.push("get overflow");
+ return TemporalHelpers.toPrimitiveObserver(actual, "constrain", "overflow");
+ }
+};
+
+assert.throws(RangeError, () => Temporal.PlainTime.from("24:60", object));
+assert.compareArray(actual, expected);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/options-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/options-invalid.js
new file mode 100644
index 0000000000..270bd8e3cd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/options-invalid.js
@@ -0,0 +1,17 @@
+// |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.from
+description: TypeError thrown when a primitive is passed as the options argument
+features: [Temporal]
+---*/
+
+const values = [null, true, "hello", Symbol("foo"), 1, 1n];
+
+for (const badOptions of values) {
+ assert.throws(TypeError, () => Temporal.PlainTime.from({ hours: 12 }, badOptions));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/options-object.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/options-object.js
new file mode 100644
index 0000000000..66d716cc23
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/options-object.js
@@ -0,0 +1,22 @@
+// |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.from
+description: Empty object may be used as options
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.assertPlainTime(
+ Temporal.PlainTime.from({ hour: 12, minute: 34 }, {}), 12, 34, 0, 0, 0, 0,
+ "options may be an empty plain object"
+);
+
+TemporalHelpers.assertPlainTime(
+ Temporal.PlainTime.from({ hour: 12, minute: 34 }, () => {}), 12, 34, 0, 0, 0, 0,
+ "options may be an empty function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/options-undefined.js
new file mode 100644
index 0000000000..f9c0f77f64
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/options-undefined.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.from
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const fields = { hour: 12, minute: 60 };
+
+const explicit = Temporal.PlainTime.from(fields, undefined);
+assert.sameValue(explicit.minute, 59, "default overflow is constrain");
+
+const implicit = Temporal.PlainTime.from(fields);
+assert.sameValue(implicit.minute, 59, "default overflow is constrain");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/options-wrong-type.js
new file mode 100644
index 0000000000..ae4cb12dd2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/options-wrong-type.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.from
+description: TypeError thrown when options argument is a primitive
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const badOptions = [
+ null,
+ true,
+ "some string",
+ Symbol(),
+ 1,
+ 2n,
+];
+
+for (const value of badOptions) {
+ assert.throws(TypeError, () => Temporal.PlainTime.from({ hour: 12, minute: 34 }, value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/order-of-operations.js
new file mode 100644
index 0000000000..e523323801
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/order-of-operations.js
@@ -0,0 +1,55 @@
+// |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.from
+description: Properties on an object passed to from() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "get options.overflow",
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+ // ToTemporalTimeRecord
+ "get fields.hour",
+ "get fields.hour.valueOf",
+ "call fields.hour.valueOf",
+ "get fields.microsecond",
+ "get fields.microsecond.valueOf",
+ "call fields.microsecond.valueOf",
+ "get fields.millisecond",
+ "get fields.millisecond.valueOf",
+ "call fields.millisecond.valueOf",
+ "get fields.minute",
+ "get fields.minute.valueOf",
+ "call fields.minute.valueOf",
+ "get fields.nanosecond",
+ "get fields.nanosecond.valueOf",
+ "call fields.nanosecond.valueOf",
+ "get fields.second",
+ "get fields.second.valueOf",
+ "call fields.second.valueOf",
+];
+const actual = [];
+
+const fields = TemporalHelpers.propertyBagObserver(actual, {
+ hour: 1.7,
+ minute: 1.7,
+ second: 1.7,
+ millisecond: 1.7,
+ microsecond: 1.7,
+ nanosecond: 1.7,
+ calendar: "iso8601",
+}, "fields");
+
+const options = TemporalHelpers.propertyBagObserver(actual, {
+ overflow: "constrain",
+}, "options");
+
+const result = Temporal.PlainTime.from(fields, options);
+assert.compareArray(actual, expected, "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/overflow-constrain.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/overflow-constrain.js
new file mode 100644
index 0000000000..5a306b7296
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/overflow-constrain.js
@@ -0,0 +1,17 @@
+// |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.from
+description: constrain value for overflow option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.assertPlainTime(Temporal.PlainTime.from({ hour: 26 }, { overflow: "constrain" }),
+ 23, 0, 0, 0, 0, 0);
+TemporalHelpers.assertPlainTime(Temporal.PlainTime.from({ hour: 22 }, { overflow: "constrain" }),
+ 22, 0, 0, 0, 0, 0);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/overflow-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/overflow-invalid-string.js
new file mode 100644
index 0000000000..dc3a9672ec
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/overflow-invalid-string.js
@@ -0,0 +1,35 @@
+// |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.from
+description: RangeError thrown when overflow option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal.plaintime.from step 2:
+ 2. Let _overflow_ be ? ToTemporalOverflow(_options_).
+features: [Temporal]
+---*/
+
+const validValues = [
+ new Temporal.PlainTime(12),
+ { hour: 12 },
+ "12:00",
+];
+
+const badOverflows = ["", "CONSTRAIN", "balance", "other string", "constra\u0131n", "reject\0"];
+for (const value of validValues) {
+ for (const overflow of badOverflows) {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainTime.from(value, { overflow }),
+ `invalid overflow ("${overflow}")`
+ );
+ }
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/overflow-reject.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/overflow-reject.js
new file mode 100644
index 0000000000..9c2d998dc2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/overflow-reject.js
@@ -0,0 +1,16 @@
+// |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.from
+description: reject value for overflow option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+assert.throws(RangeError, () => Temporal.PlainTime.from({ hour: 26 }, { overflow: "reject" }));
+TemporalHelpers.assertPlainTime(Temporal.PlainTime.from({ hour: 22 }, { overflow: "reject" }),
+ 22, 0, 0, 0, 0, 0);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/overflow-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/overflow-undefined.js
new file mode 100644
index 0000000000..8fb5547388
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/overflow-undefined.js
@@ -0,0 +1,40 @@
+// |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.from
+description: Fallback value for overflow option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal.plaintime.from step 2:
+ 2. Let _overflow_ be ? ToTemporalOverflow(_options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const validValues = [
+ new Temporal.PlainTime(12),
+ "12:00",
+];
+validValues.forEach((value) => {
+ const explicit = Temporal.PlainTime.from(value, { overflow: undefined });
+ TemporalHelpers.assertPlainTime(explicit, 12, 0, 0, 0, 0, 0, "overflow is ignored");
+ const implicit = Temporal.PlainTime.from(value, {});
+ TemporalHelpers.assertPlainTime(implicit, 12, 0, 0, 0, 0, 0, "overflow is ignored");
+ const lambda = Temporal.PlainTime.from(value, () => {});
+ TemporalHelpers.assertPlainTime(lambda, 12, 0, 0, 0, 0, 0, "overflow is ignored");
+});
+
+const propertyBag = { hour: 26 };
+const explicit = Temporal.PlainTime.from(propertyBag, { overflow: undefined });
+TemporalHelpers.assertPlainTime(explicit, 23, 0, 0, 0, 0, 0, "default overflow is constrain");
+const implicit = Temporal.PlainTime.from(propertyBag, {});
+TemporalHelpers.assertPlainTime(implicit, 23, 0, 0, 0, 0, 0, "default overflow is constrain");
+const lambda = Temporal.PlainTime.from(propertyBag, () => {});
+TemporalHelpers.assertPlainTime(lambda, 23, 0, 0, 0, 0, 0, "default overflow is constrain");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/overflow-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/overflow-wrong-type.js
new file mode 100644
index 0000000000..ef10d18d2f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/overflow-wrong-type.js
@@ -0,0 +1,29 @@
+// |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.from
+description: Type conversions for overflow option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal.plaintime.from step 2:
+ 2. Let _overflow_ be ? ToTemporalOverflow(_options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const validValues = [
+ new Temporal.PlainTime(12),
+ { hour: 12 },
+ "12:00",
+];
+validValues.forEach((value) => TemporalHelpers.checkStringOptionWrongType("overflow", "constrain",
+ (overflow) => Temporal.PlainTime.from(value, { overflow }),
+ (result, descr) => TemporalHelpers.assertPlainTime(result, 12, 0, 0, 0, 0, 0, descr),
+));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/plaintime-propertybag-no-time-units.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/plaintime-propertybag-no-time-units.js
new file mode 100644
index 0000000000..efe7b95c3d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/plaintime-propertybag-no-time-units.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.from
+description: Missing time units in property bag default to 0
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const props = {};
+assert.throws(TypeError, () => Temporal.PlainTime.from(props), "TypeError if at least one property is not present");
+
+props.minute = 30;
+const result = Temporal.PlainTime.from(props);
+TemporalHelpers.assertPlainTime(result, 0, 30, 0, 0, 0, 0, "missing time units default to 0");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/prop-desc.js
new file mode 100644
index 0000000000..171870e39c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/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.from
+description: The "from" property of Temporal.PlainTime
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainTime.from,
+ "function",
+ "`typeof PlainTime.from` is `function`"
+);
+
+verifyProperty(Temporal.PlainTime, "from", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/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/from/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/subclassing-ignored.js
new file mode 100644
index 0000000000..db63caf97f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/subclassing-ignored.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.from
+description: The receiver is never called when calling from()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnoredStatic(
+ Temporal.PlainTime,
+ "from",
+ ["12:34:56.987654321"],
+ (result) => TemporalHelpers.assertPlainTime(result, 12, 34, 56, 987, 654, 321),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/year-zero.js
new file mode 100644
index 0000000000..b42a38694c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/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.from
+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]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainTime.from(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/hour-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/hour-undefined.js
new file mode 100644
index 0000000000..db5a1f48e4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/hour-undefined.js
@@ -0,0 +1,18 @@
+// |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
+description: Hour argument defaults to 0 if not given
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const explicit = new Temporal.PlainTime(undefined);
+TemporalHelpers.assertPlainTime(explicit, 0, 0, 0, 0, 0, 0, "explicit");
+
+const implicit = new Temporal.PlainTime();
+TemporalHelpers.assertPlainTime(implicit, 0, 0, 0, 0, 0, 0, "implicit");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..ba30e558fc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/infinity-throws-rangeerror.js
@@ -0,0 +1,60 @@
+// |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.
+
+/*---
+description: Temporal.PlainTime throws a RangeError if any value is Infinity
+esid: sec-temporal.plaintime
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+assert.throws(RangeError, () => new Temporal.PlainTime(Infinity));
+assert.throws(RangeError, () => new Temporal.PlainTime(0, Infinity));
+assert.throws(RangeError, () => new Temporal.PlainTime(0, 0, Infinity));
+assert.throws(RangeError, () => new Temporal.PlainTime(0, 0, 0, Infinity));
+assert.throws(RangeError, () => new Temporal.PlainTime(0, 0, 0, 0, Infinity));
+assert.throws(RangeError, () => new Temporal.PlainTime(0, 0, 0, 0, 0, Infinity));
+
+const O = (primitiveValue, propertyName) => (calls) => TemporalHelpers.toPrimitiveObserver(calls, primitiveValue, propertyName);
+const tests = [
+ [
+ "infinite hour",
+ [O(Infinity, "hour"), O(1, "minute"), O(1, "second"), O(1, "millisecond"), O(1, "microsecond"), O(1, "nanosecond")],
+ ["get hour.valueOf", "call hour.valueOf"]
+ ],
+ [
+ "infinite minute",
+ [O(1, "hour"), O(Infinity, "minute"), O(1, "second"), O(1, "millisecond"), O(1, "microsecond"), O(1, "nanosecond")],
+ ["get hour.valueOf", "call hour.valueOf", "get minute.valueOf", "call minute.valueOf"]
+ ],
+ [
+ "infinite second",
+ [O(1, "hour"), O(1, "minute"), O(Infinity, "second"), O(1, "millisecond"), O(1, "microsecond"), O(1, "nanosecond")],
+ ["get hour.valueOf", "call hour.valueOf", "get minute.valueOf", "call minute.valueOf", "get second.valueOf", "call second.valueOf"]
+ ],
+ [
+ "infinite millisecond",
+ [O(1, "hour"), O(1, "minute"), O(1, "second"), O(Infinity, "millisecond"), O(1, "microsecond"), O(1, "nanosecond")],
+ ["get hour.valueOf", "call hour.valueOf", "get minute.valueOf", "call minute.valueOf", "get second.valueOf", "call second.valueOf", "get millisecond.valueOf", "call millisecond.valueOf"]
+ ],
+ [
+ "infinite microsecond",
+ [O(1, "hour"), O(1, "minute"), O(1, "second"), O(1, "millisecond"), O(Infinity, "microsecond"), O(1, "nanosecond")],
+ ["get hour.valueOf", "call hour.valueOf", "get minute.valueOf", "call minute.valueOf", "get second.valueOf", "call second.valueOf", "get millisecond.valueOf", "call millisecond.valueOf", "get microsecond.valueOf", "call microsecond.valueOf"]
+ ],
+ [
+ "infinite nanosecond",
+ [O(1, "hour"), O(1, "minute"), O(1, "second"), O(1, "millisecond"), O(1, "microsecond"), O(Infinity, "nanosecond")],
+ ["get hour.valueOf", "call hour.valueOf", "get minute.valueOf", "call minute.valueOf", "get second.valueOf", "call second.valueOf", "get millisecond.valueOf", "call millisecond.valueOf", "get microsecond.valueOf", "call microsecond.valueOf", "get nanosecond.valueOf", "call nanosecond.valueOf"]
+ ],
+];
+
+for (const [description, args, expected] of tests) {
+ const actual = [];
+ const args_ = args.map((o) => o(actual));
+ assert.throws(RangeError, () => new Temporal.PlainTime(...args_), description);
+ assert.compareArray(actual, expected, `${description} order of operations`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/length.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/length.js
new file mode 100644
index 0000000000..50e2cd496b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/length.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
+description: Temporal.PlainTime.length is 0
+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, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/microsecond-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/microsecond-undefined.js
new file mode 100644
index 0000000000..076adb01ee
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/microsecond-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
+description: Microsecond argument defaults to 0 if not given
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const args = [12, 34, 56, 123];
+
+const explicit = new Temporal.PlainTime(...args, undefined);
+TemporalHelpers.assertPlainTime(explicit, ...args, 0, 0, "explicit");
+
+const implicit = new Temporal.PlainTime(...args);
+TemporalHelpers.assertPlainTime(implicit, ...args, 0, 0, "implicit");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/millisecond-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/millisecond-undefined.js
new file mode 100644
index 0000000000..cd32d9895b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/millisecond-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
+description: Millisecond argument defaults to 0 if not given
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const args = [12, 34, 56];
+
+const explicit = new Temporal.PlainTime(...args, undefined);
+TemporalHelpers.assertPlainTime(explicit, ...args, 0, 0, 0, "explicit");
+
+const implicit = new Temporal.PlainTime(...args);
+TemporalHelpers.assertPlainTime(implicit, ...args, 0, 0, 0, "implicit");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/minute-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/minute-undefined.js
new file mode 100644
index 0000000000..877d433cea
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/minute-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
+description: Minute argument defaults to 0 if not given
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const hour = 12;
+
+const explicit = new Temporal.PlainTime(hour, undefined);
+TemporalHelpers.assertPlainTime(explicit, hour, 0, 0, 0, 0, 0, "explicit");
+
+const implicit = new Temporal.PlainTime(hour);
+TemporalHelpers.assertPlainTime(implicit, hour, 0, 0, 0, 0, 0, "implicit");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/name.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/name.js
new file mode 100644
index 0000000000..766cffff6c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/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
+description: Temporal.PlainTime.name is "PlainTime"
+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, "name", {
+ value: "PlainTime",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/nanosecond-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/nanosecond-undefined.js
new file mode 100644
index 0000000000..726f0efe4c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/nanosecond-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
+description: Nanosecond argument defaults to 0 if not given
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const args = [12, 34, 56, 123, 456];
+
+const explicit = new Temporal.PlainTime(...args, undefined);
+TemporalHelpers.assertPlainTime(explicit, ...args, 0, "explicit");
+
+const implicit = new Temporal.PlainTime(...args);
+TemporalHelpers.assertPlainTime(implicit, ...args, 0, "implicit");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/negative-infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/negative-infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..a3ca04739f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/negative-infinity-throws-rangeerror.js
@@ -0,0 +1,60 @@
+// |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.
+
+/*---
+description: Temporal.PlainDate throws a RangeError if any value is -Infinity
+esid: sec-temporal.plaintime
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+assert.throws(RangeError, () => new Temporal.PlainTime(-Infinity));
+assert.throws(RangeError, () => new Temporal.PlainTime(0, -Infinity));
+assert.throws(RangeError, () => new Temporal.PlainTime(0, 0, -Infinity));
+assert.throws(RangeError, () => new Temporal.PlainTime(0, 0, 0, -Infinity));
+assert.throws(RangeError, () => new Temporal.PlainTime(0, 0, 0, 0, -Infinity));
+assert.throws(RangeError, () => new Temporal.PlainTime(0, 0, 0, 0, 0, -Infinity));
+
+const O = (primitiveValue, propertyName) => (calls) => TemporalHelpers.toPrimitiveObserver(calls, primitiveValue, propertyName);
+const tests = [
+ [
+ "infinite hour",
+ [O(-Infinity, "hour"), O(1, "minute"), O(1, "second"), O(1, "millisecond"), O(1, "microsecond"), O(1, "nanosecond")],
+ ["get hour.valueOf", "call hour.valueOf"]
+ ],
+ [
+ "infinite minute",
+ [O(1, "hour"), O(-Infinity, "minute"), O(1, "second"), O(1, "millisecond"), O(1, "microsecond"), O(1, "nanosecond")],
+ ["get hour.valueOf", "call hour.valueOf", "get minute.valueOf", "call minute.valueOf"]
+ ],
+ [
+ "infinite second",
+ [O(1, "hour"), O(1, "minute"), O(-Infinity, "second"), O(1, "millisecond"), O(1, "microsecond"), O(1, "nanosecond")],
+ ["get hour.valueOf", "call hour.valueOf", "get minute.valueOf", "call minute.valueOf", "get second.valueOf", "call second.valueOf"]
+ ],
+ [
+ "infinite millisecond",
+ [O(1, "hour"), O(1, "minute"), O(1, "second"), O(-Infinity, "millisecond"), O(1, "microsecond"), O(1, "nanosecond")],
+ ["get hour.valueOf", "call hour.valueOf", "get minute.valueOf", "call minute.valueOf", "get second.valueOf", "call second.valueOf", "get millisecond.valueOf", "call millisecond.valueOf"]
+ ],
+ [
+ "infinite microsecond",
+ [O(1, "hour"), O(1, "minute"), O(1, "second"), O(1, "millisecond"), O(-Infinity, "microsecond"), O(1, "nanosecond")],
+ ["get hour.valueOf", "call hour.valueOf", "get minute.valueOf", "call minute.valueOf", "get second.valueOf", "call second.valueOf", "get millisecond.valueOf", "call millisecond.valueOf", "get microsecond.valueOf", "call microsecond.valueOf"]
+ ],
+ [
+ "infinite nanosecond",
+ [O(1, "hour"), O(1, "minute"), O(1, "second"), O(1, "millisecond"), O(1, "microsecond"), O(-Infinity, "nanosecond")],
+ ["get hour.valueOf", "call hour.valueOf", "get minute.valueOf", "call minute.valueOf", "get second.valueOf", "call second.valueOf", "get millisecond.valueOf", "call millisecond.valueOf", "get microsecond.valueOf", "call microsecond.valueOf", "get nanosecond.valueOf", "call nanosecond.valueOf"]
+ ],
+];
+
+for (const [description, args, expected] of tests) {
+ const actual = [];
+ const args_ = args.map((o) => o(actual));
+ assert.throws(RangeError, () => new Temporal.PlainTime(...args_), description);
+ assert.compareArray(actual, expected, `${description} order of operations`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/negative-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/negative-zero.js
new file mode 100644
index 0000000000..acb69c0b37
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/negative-zero.js
@@ -0,0 +1,15 @@
+// |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
+description: Negative zero arguments are treated as zero.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainTime = new Temporal.PlainTime(-0, -0, -0, -0, -0, -0);
+TemporalHelpers.assertPlainTime(plainTime, 0, 0, 0, 0, 0, 0);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prop-desc.js
new file mode 100644
index 0000000000..811fd97303
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/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
+description: The "PlainTime" property of Temporal
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainTime,
+ "function",
+ "`typeof PlainTime` is `function`"
+);
+
+verifyProperty(Temporal, "PlainTime", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-duration-max.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-duration-max.js
new file mode 100644
index 0000000000..bbaded9b35
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-duration-max.js
@@ -0,0 +1,58 @@
+// |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.add
+description: Maximum allowed duration
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime();
+
+const maxCases = [
+ ["P4294967295Y104249991374DT7H36M31.999999999S", "string with max years"],
+ [{ years: 4294967295, days: 104249991374, nanoseconds: 27391999999999 }, "property bag with max years"],
+ ["P4294967295M104249991374DT7H36M31.999999999S", "string with max weeks"],
+ [{ months: 4294967295, days: 104249991374, nanoseconds: 27391999999999 }, "property bag with max months"],
+ ["P4294967295W104249991374DT7H36M31.999999999S", "string with max weeks"],
+ [{ weeks: 4294967295, days: 104249991374, nanoseconds: 27391999999999 }, "property bag with max weeks"],
+ ["P104249991374DT7H36M31.999999999S", "string with max days"],
+ [{ days: 104249991374, nanoseconds: 27391999999999 }, "property bag with max days"],
+ ["PT2501999792983H36M31.999999999S", "string with max hours"],
+ [{ hours: 2501999792983, nanoseconds: 2191999999999 }, "property bag with max hours"],
+ ["PT150119987579016M31.999999999S", "string with max minutes"],
+ [{ minutes: 150119987579016, nanoseconds: 31999999999 }, "property bag with max minutes"],
+ ["PT9007199254740991.999999999S", "string with max seconds"],
+ [{ seconds: 9007199254740991, nanoseconds: 999999999 }, "property bag with max seconds"],
+];
+
+for (const [arg, descr] of maxCases) {
+ const result = instance.add(arg);
+ TemporalHelpers.assertPlainTime(result, 7, 36, 31, 999, 999, 999, `operation succeeds with ${descr}`);
+}
+
+const minCases = [
+ ["-P4294967295Y104249991374DT7H36M31.999999999S", "string with min years"],
+ [{ years: -4294967295, days: -104249991374, nanoseconds: -27391999999999 }, "property bag with min years"],
+ ["-P4294967295M104249991374DT7H36M31.999999999S", "string with min months"],
+ [{ months: -4294967295, days: -104249991374, nanoseconds: -27391999999999 }, "property bag with min months"],
+ ["-P4294967295W104249991374DT7H36M31.999999999S", "string with min weeks"],
+ [{ weeks: -4294967295, days: -104249991374, nanoseconds: -27391999999999 }, "property bag with min weeks"],
+ ["-P104249991374DT7H36M31.999999999S", "string with min days"],
+ [{ days: -104249991374, nanoseconds: -27391999999999 }, "property bag with min days"],
+ ["-PT2501999792983H36M31.999999999S", "string with min hours"],
+ [{ hours: -2501999792983, nanoseconds: -2191999999999 }, "property bag with min hours"],
+ ["-PT150119987579016M31.999999999S", "string with min minutes"],
+ [{ minutes: -150119987579016, nanoseconds: -31999999999 }, "property bag with min minutes"],
+ ["-PT9007199254740991.999999999S", "string with min seconds"],
+ [{ seconds: -9007199254740991, nanoseconds: -999999999 }, "property bag with min seconds"],
+];
+
+for (const [arg, descr] of minCases) {
+ const result = instance.add(arg);
+ TemporalHelpers.assertPlainTime(result, 16, 23, 28, 0, 0, 1, `operation succeeds with ${descr}`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-duration-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-duration-out-of-range.js
new file mode 100644
index 0000000000..7639ffc6f4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-duration-out-of-range.js
@@ -0,0 +1,75 @@
+// |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.add
+description: Duration-like argument that is out of range
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime();
+
+const cases = [
+ // 2^32 = 4294967296
+ ["P4294967296Y", "string with years > max"],
+ [{ years: 4294967296 }, "property bag with years > max"],
+ ["-P4294967296Y", "string with years < min"],
+ [{ years: -4294967296 }, "property bag with years < min"],
+ ["P4294967296M", "string with months > max"],
+ [{ months: 4294967296 }, "property bag with months > max"],
+ ["-P4294967296M", "string with months < min"],
+ [{ months: -4294967296 }, "property bag with months < min"],
+ ["P4294967296W", "string with weeks > max"],
+ [{ weeks: 4294967296 }, "property bag with weeks > max"],
+ ["-P4294967296W", "string with weeks < min"],
+ [{ weeks: -4294967296 }, "property bag with weeks < min"],
+
+ // ceil(max safe integer / 86400) = 104249991375
+ ["P104249991375D", "string with days > max"],
+ [{ days: 104249991375 }, "property bag with days > max"],
+ ["P104249991374DT24H", "string where hours balance into days > max"],
+ [{ days: 104249991374, hours: 24 }, "property bag where hours balance into days > max"],
+ ["-P104249991375D", "string with days < min"],
+ [{ days: -104249991375 }, "property bag with days < min"],
+ ["-P104249991374DT24H", "string where hours balance into days < min"],
+ [{ days: -104249991374, hours: -24 }, "property bag where hours balance into days < min"],
+
+ // ceil(max safe integer / 3600) = 2501999792984
+ ["PT2501999792984H", "string with hours > max"],
+ [{ hours: 2501999792984 }, "property bag with hours > max"],
+ ["PT2501999792983H60M", "string where minutes balance into hours > max"],
+ [{ hours: 2501999792983, minutes: 60 }, "property bag where minutes balance into hours > max"],
+ ["-PT2501999792984H", "string with hours < min"],
+ [{ hours: -2501999792984 }, "property bag with hours < min"],
+ ["-PT2501999792983H60M", "string where minutes balance into hours < min"],
+ [{ hours: -2501999792983, minutes: -60 }, "property bag where minutes balance into hours < min"],
+
+ // ceil(max safe integer / 60) = 150119987579017
+ ["PT150119987579017M", "string with minutes > max"],
+ [{ minutes: 150119987579017 }, "property bag with minutes > max"],
+ ["PT150119987579016M60S", "string where seconds balance into minutes > max"],
+ [{ minutes: 150119987579016, seconds: 60 }, "property bag where seconds balance into minutes > max"],
+ ["-PT150119987579017M", "string with minutes < min"],
+ [{ minutes: -150119987579017 }, "property bag with minutes < min"],
+ ["-PT150119987579016M60S", "string where seconds balance into minutes < min"],
+ [{ minutes: -150119987579016, seconds: -60 }, "property bag where seconds balance into minutes < min"],
+
+ // 2^53 = 9007199254740992
+ ["PT9007199254740992S", "string with seconds > max"],
+ [{ seconds: 9007199254740992 }, "property bag with seconds > max"],
+ [{ seconds: 9007199254740991, milliseconds: 1000 }, "property bag where milliseconds balance into seconds > max"],
+ [{ seconds: 9007199254740991, microseconds: 1000000 }, "property bag where microseconds balance into seconds > max"],
+ [{ seconds: 9007199254740991, nanoseconds: 1000000000 }, "property bag where nanoseconds balance into seconds > max"],
+ ["-PT9007199254740992S", "string with seconds < min"],
+ [{ seconds: -9007199254740992 }, "property bag with seconds < min"],
+ [{ seconds: -9007199254740991, milliseconds: -1000 }, "property bag where milliseconds balance into seconds < min"],
+ [{ seconds: -9007199254740991, microseconds: -1000000 }, "property bag where microseconds balance into seconds < min"],
+ [{ seconds: -9007199254740991, nanoseconds: -1000000000 }, "property bag where nanoseconds balance into seconds < min"],
+];
+
+for (const [arg, descr] of cases) {
+ assert.throws(RangeError, () => instance.add(arg), `${descr} is out of range`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-duration-precision-exact-numerical-values.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-duration-precision-exact-numerical-values.js
new file mode 100644
index 0000000000..571afaef87
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-duration-precision-exact-numerical-values.js
@@ -0,0 +1,40 @@
+// |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.add
+description: >
+ Duration-like argument performs the range check with minimal floating point
+ precision loss
+features: [Temporal]
+---*/
+
+// Based on a test case by André Bargull
+
+const instance = new Temporal.PlainTime();
+
+const cases = [
+ [
+ {
+ milliseconds: 4503599627370497_000, // ℝ(𝔽(4503599627370497000)) = 4503599627370497024
+ microseconds: 4503599627370495_000000, // ℝ(𝔽(4503599627370495000000)) = 4503599627370494951424
+ },
+ // 4503599627370497024 / 1000 + 4503599627370494951424 / 1000000 is
+ // 9007199254740991.975424, which is below the limit of 2**53
+ "case where floating point inaccuracy brings total below limit, positive"
+ ],
+ [
+ {
+ milliseconds: -4503599627370497_000,
+ microseconds: -4503599627370495_000000,
+ },
+ "case where floating point inaccuracy brings total below limit, negative"
+ ],
+];
+
+for (const [arg, descr] of cases) {
+ instance.add(arg); // should not throw
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-duration.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-duration.js
new file mode 100644
index 0000000000..961d154bbb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-duration.js
@@ -0,0 +1,17 @@
+// |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.add
+description: Duration arguments are supported.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainTime = new Temporal.PlainTime(15, 23, 30, 123, 456, 789);
+const duration = Temporal.Duration.from("PT16H");
+TemporalHelpers.assertPlainTime(plainTime.add(duration),
+ 7, 23, 30, 123, 456, 789);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-higher-units.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-higher-units.js
new file mode 100644
index 0000000000..54462bcdca
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-higher-units.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.add
+description: Higher units are ignored.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainTime = new Temporal.PlainTime(15, 23, 30, 123, 456, 789);
+const values = [
+ new Temporal.Duration(0, 0, 0, 1),
+ new Temporal.Duration(0, 0, 1),
+ new Temporal.Duration(0, 1),
+ new Temporal.Duration(1),
+ { days: 1 },
+ { weeks: 1 },
+ { months: 1 },
+ { years: 1 },
+ "P1D",
+ "P1W",
+ "P1M",
+ "P1Y",
+];
+for (const value of values) {
+ TemporalHelpers.assertPlainTime(plainTime.add(value),
+ 15, 23, 30, 123, 456, 789);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-invalid-property.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-invalid-property.js
new file mode 100644
index 0000000000..c3c89846a1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-invalid-property.js
@@ -0,0 +1,31 @@
+// |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.add
+description: temporalDurationLike object must contain at least one correctly spelled property
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(15, 30, 45, 987, 654, 321);
+
+assert.throws(
+ TypeError,
+ () => instance.add({}),
+ "Throws TypeError if no property is present"
+);
+
+assert.throws(
+ TypeError,
+ () => instance.add({ nonsense: true }),
+ "Throws TypeError if no recognized property is present"
+);
+
+assert.throws(
+ TypeError,
+ () => instance.add({ sign: 1 }),
+ "Sign property is not recognized"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-mixed-sign.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-mixed-sign.js
new file mode 100644
index 0000000000..b95c4a0269
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-mixed-sign.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.add
+description: Positive and negative values in the temporalDurationLike argument are not acceptable
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(15, 30, 45, 987, 654, 321);
+
+assert.throws(
+ RangeError,
+ () => instance.add({ hours: 1, minutes: -30 }),
+ `mixed positive and negative values always throw`
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-not-object.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-not-object.js
new file mode 100644
index 0000000000..76496e794e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-not-object.js
@@ -0,0 +1,22 @@
+// |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.add
+description: Passing a primitive other than string to add() throws
+features: [Symbol, Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(15, 30, 45, 987, 654, 321);
+assert.throws(TypeError, () => instance.add(undefined), "undefined");
+assert.throws(TypeError, () => instance.add(null), "null");
+assert.throws(TypeError, () => instance.add(true), "boolean");
+assert.throws(RangeError, () => instance.add(""), "empty string");
+assert.throws(TypeError, () => instance.add(Symbol()), "Symbol");
+assert.throws(TypeError, () => instance.add(7), "number");
+assert.throws(TypeError, () => instance.add(7n), "bigint");
+assert.throws(TypeError, () => instance.add([]), "array");
+assert.throws(TypeError, () => instance.add(() => {}), "function");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-object.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-object.js
new file mode 100644
index 0000000000..665cf6f3b6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-object.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.add
+description: Plain object arguments are supported.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainTime = new Temporal.PlainTime(15, 23, 30, 123, 456, 789);
+TemporalHelpers.assertPlainTime(plainTime.add({ hours: 16 }),
+ 7, 23, 30, 123, 456, 789, "add 16 hours across midnight boundary");
+TemporalHelpers.assertPlainTime(plainTime.add({ minutes: 45 }),
+ 16, 8, 30, 123, 456, 789, "add 45 minutes");
+TemporalHelpers.assertPlainTime(plainTime.add({ seconds: 800 }),
+ 15, 36, 50, 123, 456, 789, "add 800 seconds");
+TemporalHelpers.assertPlainTime(plainTime.add({ milliseconds: 800 }),
+ 15, 23, 30, 923, 456, 789, "add 800 milliseconds");
+TemporalHelpers.assertPlainTime(plainTime.add({ microseconds: 800 }),
+ 15, 23, 30, 124, 256, 789, "add 800 microseconds");
+TemporalHelpers.assertPlainTime(plainTime.add({ nanoseconds: 300 }),
+ 15, 23, 30, 123, 457, 89, "add 300 nanoseconds");
+TemporalHelpers.assertPlainTime(Temporal.PlainTime.from("07:23:30.123456789").add({ hours: -16 }),
+ 15, 23, 30, 123, 456, 789, "add -16 hours across midnight boundary");
+TemporalHelpers.assertPlainTime(Temporal.PlainTime.from("16:08:30.123456789").add({ minutes: -45 }),
+ 15, 23, 30, 123, 456, 789, "add -45 minutes");
+TemporalHelpers.assertPlainTime(Temporal.PlainTime.from("15:36:50.123456789").add({ seconds: -800 }),
+ 15, 23, 30, 123, 456, 789, "add -800 seconds");
+TemporalHelpers.assertPlainTime(Temporal.PlainTime.from("15:23:30.923456789").add({ milliseconds: -800 }),
+ 15, 23, 30, 123, 456, 789, "add -800 milliseconds");
+TemporalHelpers.assertPlainTime(Temporal.PlainTime.from("15:23:30.124256789").add({ microseconds: -800 }),
+ 15, 23, 30, 123, 456, 789, "add -800 microseconds");
+TemporalHelpers.assertPlainTime(Temporal.PlainTime.from("15:23:30.123457089").add({ nanoseconds: -300 }),
+ 15, 23, 30, 123, 456, 789, "add -300 nanoseconds");
+TemporalHelpers.assertPlainTime(plainTime.add({ minute: 1, hours: 1 }),
+ 16, 23, 30, 123, 456, 789, "misspelled property is ignored");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-singular-properties.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-singular-properties.js
new file mode 100644
index 0000000000..415dbad921
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-singular-properties.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.add
+description: Singular properties in the property bag are always ignored
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(15, 30, 45, 987, 654, 321);
+
+[
+ { year: 1 },
+ { month: 2 },
+ { week: 3 },
+ { day: 4 },
+ { hour: 5 },
+ { minute: 6 },
+ { second: 7 },
+ { millisecond: 8 },
+ { microsecond: 9 },
+ { nanosecond: 10 },
+].forEach((badObject) => {
+ assert.throws(TypeError, () => instance.add(badObject),
+ "Throw TypeError if temporalDurationLike is not valid");
+});
+
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-string-duration-too-large.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-string-duration-too-large.js
new file mode 100644
index 0000000000..e155515611
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-string-duration-too-large.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.add
+description: >
+ ParseTemporalDurationString throws a RangeError when the result is too large.
+features: [Temporal]
+---*/
+
+// Number string too long to be representable as a Number value.
+var ones = "1".repeat(1000);
+assert.sameValue(Number(ones), Infinity);
+
+var time = new Temporal.PlainTime();
+var str = "PT" + ones + "S";
+
+assert.throws(RangeError, () => time.add(str));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-string-fractional-units-rounding-mode.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-string-fractional-units-rounding-mode.js
new file mode 100644
index 0000000000..db31649d15
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-string-fractional-units-rounding-mode.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.add
+description: Strings with fractional duration units are rounded with the correct rounding mode
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const midnight = new Temporal.PlainTime();
+
+TemporalHelpers.assertPlainTime(midnight.add("PT1.03125H"), 1, 1, 52, 500, 0, 0,
+ "positive fractional units rounded with correct rounding mode");
+TemporalHelpers.assertPlainTime(midnight.add("-PT1.03125H"), 22, 58, 7, 500, 0, 0,
+ "negative fractional units rounded with correct rounding mode");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-string-negative-fractional-units.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-string-negative-fractional-units.js
new file mode 100644
index 0000000000..e604c8b210
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-string-negative-fractional-units.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.add
+description: Strings with fractional duration units are treated with the correct sign
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime();
+
+const resultHours = instance.add("-PT24.567890123H");
+TemporalHelpers.assertPlainTime(resultHours, 23, 25, 55, 595, 557, 200, "negative fractional hours");
+
+const resultMinutes = instance.add("-PT1440.567890123M");
+TemporalHelpers.assertPlainTime(resultMinutes, 23, 59, 25, 926, 592, 620, "negative fractional minutes");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-string.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-string.js
new file mode 100644
index 0000000000..370e3af18c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/argument-string.js
@@ -0,0 +1,16 @@
+// |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.add
+description: A string is parsed into the correct object when passed as the argument
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = Temporal.PlainTime.from({ hour: 12, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321 });
+const result = instance.add("PT3M");
+TemporalHelpers.assertPlainTime(result, 12, 37, 56, 987, 654, 321);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/balance-negative-time-units.js
new file mode 100644
index 0000000000..ee6367ad1e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/balance-negative-time-units.js
@@ -0,0 +1,50 @@
+// |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.add
+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-addtime step 8:
+ 8. Return ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_).
+ sec-temporal.plaintime.prototype.add step 4:
+ 4. Let _result_ be ? AddTime(_temporalTime_.[[ISOHour]], _temporalTime_.[[ISOMinute]], _temporalTime_.[[ISOSecond]], _temporalTime_.[[ISOMillisecond]], _temporalTime_.[[ISOMicrosecond]], _temporalTime_.[[ISONanosecond]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]]).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(1, 1, 1, 1, 1, 1);
+
+const result1 = time.add(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, -2));
+TemporalHelpers.assertPlainTime(result1, 1, 1, 1, 1, 0, 999, "nanoseconds balance");
+
+const result2 = time.add(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, -2));
+TemporalHelpers.assertPlainTime(result2, 1, 1, 1, 0, 999, 1, "microseconds balance");
+
+const result3 = time.add(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, -2));
+TemporalHelpers.assertPlainTime(result3, 1, 1, 0, 999, 1, 1, "milliseconds balance");
+
+const result4 = time.add(new Temporal.Duration(0, 0, 0, 0, 0, 0, -2));
+TemporalHelpers.assertPlainTime(result4, 1, 0, 59, 1, 1, 1, "seconds balance");
+
+const result5 = time.add(new Temporal.Duration(0, 0, 0, 0, 0, -2));
+TemporalHelpers.assertPlainTime(result5, 0, 59, 1, 1, 1, 1, "minutes balance");
+
+const result6 = time.add(new Temporal.Duration(0, 0, 0, 0, -2));
+TemporalHelpers.assertPlainTime(result6, 23, 1, 1, 1, 1, 1, "hours mod 24");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/branding.js
new file mode 100644
index 0000000000..6d04681c4e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/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.add
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const add = Temporal.PlainTime.prototype.add;
+
+assert.sameValue(typeof add, "function");
+
+const args = [new Temporal.Duration(0, 0, 0, 0, 5)];
+
+assert.throws(TypeError, () => add.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => add.apply(null, args), "null");
+assert.throws(TypeError, () => add.apply(true, args), "true");
+assert.throws(TypeError, () => add.apply("", args), "empty string");
+assert.throws(TypeError, () => add.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => add.apply(1, args), "1");
+assert.throws(TypeError, () => add.apply({}, args), "plain object");
+assert.throws(TypeError, () => add.apply(Temporal.PlainTime, args), "Temporal.PlainTime");
+assert.throws(TypeError, () => add.apply(Temporal.PlainTime.prototype, args), "Temporal.PlainTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/builtin.js
new file mode 100644
index 0000000000..3d3dc4cccd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/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.add
+description: >
+ Tests that Temporal.PlainTime.prototype.add
+ 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.add),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainTime.prototype.add),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainTime.prototype.add),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainTime.prototype.add.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..b6c769b42b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/infinity-throws-rangeerror.js
@@ -0,0 +1,38 @@
+// |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.
+
+/*---
+description: Temporal.PlainTime.prototype.add throws a RangeError if any value in a property bag is Infinity
+esid: sec-temporal.plaintime.prototype.add
+features: [Temporal]
+---*/
+
+const overflows = ["constrain", "reject"];
+const fields = ["years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"];
+
+const instance = Temporal.PlainTime.from({ hour: 12, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321 });
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.add({ [field]: Infinity }, { overflow }));
+ });
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return Infinity;
+ }
+};
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => instance.add({ [field]: obj }, { overflow }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/length.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/length.js
new file mode 100644
index 0000000000..825d286d11
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/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.add
+description: Temporal.PlainTime.prototype.add.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.add, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/name.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/name.js
new file mode 100644
index 0000000000..053decb679
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/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.add
+description: Temporal.PlainTime.prototype.add.name is "add".
+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.add, "name", {
+ value: "add",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/negative-infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/negative-infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..2ec5d1f3f6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/negative-infinity-throws-rangeerror.js
@@ -0,0 +1,38 @@
+// |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.
+
+/*---
+description: Temporal.PlainTime.prototype.add throws a RangeError if any value in a property bag is -Infinity
+esid: sec-temporal.plaintime.prototype.add
+features: [Temporal]
+---*/
+
+const overflows = ["constrain", "reject"];
+const fields = ["years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"];
+
+const instance = Temporal.PlainTime.from({ hour: 12, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321 });
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.add({ [field]: -Infinity }, { overflow }));
+ });
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return -Infinity;
+ }
+};
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => instance.add({ [field]: obj }, { overflow }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/non-integer-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/non-integer-throws-rangeerror.js
new file mode 100644
index 0000000000..f9d007e9bc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/non-integer-throws-rangeerror.js
@@ -0,0 +1,29 @@
+// |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.add
+description: A non-integer value for any recognized property in the property bag, throws a RangeError
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(15, 30, 45, 987, 654, 321);
+const fields = [
+ "years",
+ "months",
+ "weeks",
+ "days",
+ "hours",
+ "minutes",
+ "seconds",
+ "milliseconds",
+ "microseconds",
+ "nanoseconds",
+];
+fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.add({ [field]: 1.5 }));
+ assert.throws(RangeError, () => instance.add({ [field]: -1.5 }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/not-a-constructor.js
new file mode 100644
index 0000000000..e375de5ad7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/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.add
+description: >
+ Temporal.PlainTime.prototype.add 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.add();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainTime.prototype.add), false,
+ "isConstructor(Temporal.PlainTime.prototype.add)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/options-ignored.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/options-ignored.js
new file mode 100644
index 0000000000..e8f2bcc5cd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/options-ignored.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.add
+description: Options argument is ignored.
+includes: [temporalHelpers.js]
+features: [Symbol, Temporal]
+---*/
+
+const values = [
+ undefined,
+ null,
+ true,
+ "hello",
+ Symbol("foo"),
+ 1,
+ 1n,
+ {},
+ () => {},
+ { get overflow() { throw new Test262Error("should not get overflow") } },
+];
+
+const time = Temporal.PlainTime.from("15:23:30.123456789");
+for (const options of values) {
+ TemporalHelpers.assertPlainTime(time.add({ hours: 1 }, options),
+ 16, 23, 30, 123, 456, 789);
+}
+
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/order-of-operations.js
new file mode 100644
index 0000000000..ac4524dc17
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/order-of-operations.js
@@ -0,0 +1,62 @@
+// |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.add
+description: Properties on an object passed to add() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+const expected = [
+ "get fields.days",
+ "get fields.days.valueOf",
+ "call fields.days.valueOf",
+ "get fields.hours",
+ "get fields.hours.valueOf",
+ "call fields.hours.valueOf",
+ "get fields.microseconds",
+ "get fields.microseconds.valueOf",
+ "call fields.microseconds.valueOf",
+ "get fields.milliseconds",
+ "get fields.milliseconds.valueOf",
+ "call fields.milliseconds.valueOf",
+ "get fields.minutes",
+ "get fields.minutes.valueOf",
+ "call fields.minutes.valueOf",
+ "get fields.months",
+ "get fields.months.valueOf",
+ "call fields.months.valueOf",
+ "get fields.nanoseconds",
+ "get fields.nanoseconds.valueOf",
+ "call fields.nanoseconds.valueOf",
+ "get fields.seconds",
+ "get fields.seconds.valueOf",
+ "call fields.seconds.valueOf",
+ "get fields.weeks",
+ "get fields.weeks.valueOf",
+ "call fields.weeks.valueOf",
+ "get fields.years",
+ "get fields.years.valueOf",
+ "call fields.years.valueOf",
+];
+const actual = [];
+const fields = TemporalHelpers.propertyBagObserver(actual, {
+ years: 1,
+ months: 1,
+ weeks: 1,
+ days: 1,
+ hours: 1,
+ minutes: 1,
+ seconds: 1,
+ milliseconds: 1,
+ microseconds: 1,
+ nanoseconds: 1,
+}, "fields");
+const result = instance.add(fields);
+TemporalHelpers.assertPlainTime(result, 13, 35, 57, 988, 655, 322);
+assert.compareArray(actual, expected, "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/precision-exact-mathematical-values-1.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/precision-exact-mathematical-values-1.js
new file mode 100644
index 0000000000..5c765a761b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/precision-exact-mathematical-values-1.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.add
+description: >
+ Duration components are precise mathematical integers.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+let duration = Temporal.Duration.from({
+ microseconds: Number.MAX_SAFE_INTEGER,
+ nanoseconds: 1000,
+});
+
+let time = Temporal.PlainTime.from({
+ microsecond: 1,
+});
+
+let result = time.add(duration);
+
+TemporalHelpers.assertPlainTime(result, 23, 47, 34, 740, 993, 0);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/precision-exact-mathematical-values-2.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/precision-exact-mathematical-values-2.js
new file mode 100644
index 0000000000..3bee5992cc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/precision-exact-mathematical-values-2.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.add
+description: >
+ Duration components are precise mathematical integers.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+let duration = Temporal.Duration.from({
+ seconds: Number.MAX_SAFE_INTEGER,
+ nanoseconds: 999_999_999,
+});
+
+let time = new Temporal.PlainTime(0, 0, 0, 0, 0, 0);
+
+let result = time.add(duration);
+
+TemporalHelpers.assertPlainTime(result, 7, 36, 31, 999, 999, 999);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/prop-desc.js
new file mode 100644
index 0000000000..293e29f763
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/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.add
+description: The "add" property of Temporal.PlainTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainTime.prototype.add,
+ "function",
+ "`typeof PlainTime.prototype.add` is `function`"
+);
+
+verifyProperty(Temporal.PlainTime.prototype, "add", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/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/add/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/subclassing-ignored.js
new file mode 100644
index 0000000000..2f88171099
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/add/subclassing-ignored.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.add
+description: Objects of a subclass are never created as return values for add()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.PlainTime,
+ [12, 34, 56, 987, 654, 321],
+ "add",
+ [{ nanoseconds: 1 }],
+ (result) => TemporalHelpers.assertPlainTime(result, 12, 34, 56, 987, 654, 322),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/constructor.js
new file mode 100644
index 0000000000..43a54d65c6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/constructor.js
@@ -0,0 +1,20 @@
+// |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.constructor
+description: Test for Temporal.PlainTime.prototype.constructor.
+info: The initial value of Temporal.PlainTime.prototype.constructor is %Temporal.PlainTime%.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainTime.prototype, "constructor", {
+ value: Temporal.PlainTime,
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-cast.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-cast.js
new file mode 100644
index 0000000000..114c98ea27
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-cast.js
@@ -0,0 +1,20 @@
+// |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.equals
+description: equals() casts its argument
+features: [Temporal]
+---*/
+
+const t1 = Temporal.PlainTime.from("08:44:15.321");
+
+assert.sameValue(t1.equals({ hour: 14, minute: 23, second: 30, millisecond: 123 }), false, "object");
+assert.sameValue(t1.equals({ hour: 8, minute: 44, second: 15, millisecond: 321 }), true, "object");
+assert.sameValue(t1.equals("14:23:30.123"), false, "string");
+assert.sameValue(t1.equals("08:44:15.321"), true, "string");
+assert.throws(TypeError, () => t1.equals({}), "no properties");
+assert.throws(TypeError, () => t1.equals({ hours: 8 }), "only plural property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-number.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-number.js
new file mode 100644
index 0000000000..d2909de6f6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/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.equals
+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.equals(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/equals/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..baf1fa2472
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-calendar-annotation.js
@@ -0,0 +1,42 @@
+// |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.equals
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+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.equals(arg);
+
+ assert.sameValue(
+ result,
+ true,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..45706fa2c0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/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.equals
+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.equals(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..eacfcf8bda
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-date-with-utc-offset.js
@@ -0,0 +1,52 @@
+// |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.equals
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+---*/
+
+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.equals(arg);
+
+ assert.sameValue(
+ result,
+ true,
+ `"${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.equals(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/equals/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..e4989f5525
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/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.equals
+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.equals(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/equals/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..d3026d1fb9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/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.equals
+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.equals(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-no-implicit-midnight.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-no-implicit-midnight.js
new file mode 100644
index 0000000000..b918920ca4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/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.equals
+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.equals(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/equals/argument-string-time-designator-required-for-disambiguation.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-time-designator-required-for-disambiguation.js
new file mode 100644
index 0000000000..29548d9e14
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/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.equals
+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.equals(arg),
+ `'${arg}' is ambiguous and requires T prefix`
+ );
+ // The same string with a T prefix should not throw:
+ arg = `T${string}`;
+ instance.equals(arg);
+
+ arg = ` ${string}`;
+ assert.throws(
+ RangeError,
+ () => instance.equals(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.equals(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-time-separators.js
new file mode 100644
index 0000000000..b7da0089c8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-time-separators.js
@@ -0,0 +1,31 @@
+// |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.equals
+description: Time separator in string argument can vary
+features: [Temporal]
+---*/
+
+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.equals(arg);
+
+ assert.sameValue(
+ result,
+ true,
+ `variant time separators (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..25e895d7f8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-time-zone-annotation.js
@@ -0,0 +1,50 @@
+// |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.equals
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+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.equals(arg);
+
+ assert.sameValue(
+ result,
+ true,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..62c6c39c71
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-unknown-annotation.js
@@ -0,0 +1,39 @@
+// |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.equals
+description: Various forms of unknown annotation
+features: [Temporal]
+---*/
+
+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.equals(arg);
+
+ assert.sameValue(
+ result,
+ true,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-with-time-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-with-time-designator.js
new file mode 100644
index 0000000000..8177135fd5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-with-time-designator.js
@@ -0,0 +1,31 @@
+// |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.equals
+description: ISO 8601 time designator "T" allowed at the start of PlainTime strings
+features: [Temporal, arrow-function]
+---*/
+
+const instance = new Temporal.PlainTime(0, 30, 0, 0, 0, 0);
+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.equals(arg);
+ assert.sameValue(result, true, `T prefix is accepted: ${arg}`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..1dd6f0ebb1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/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.equals
+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.equals(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/equals/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-wrong-type.js
new file mode 100644
index 0000000000..dd4a481c5c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/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.equals
+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.equals(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.equals(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/equals/argument-zoneddatetime-balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-zoneddatetime-balance-negative-time-units.js
new file mode 100644
index 0000000000..7fdefea96b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-zoneddatetime-balance-negative-time-units.js
@@ -0,0 +1,44 @@
+// |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.equals
+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.equals 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);
+
+assert(new Temporal.PlainTime(1, 1, 1, 1, 0, 999).equals(datetime));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-zoneddatetime-negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-zoneddatetime-negative-epochnanoseconds.js
new file mode 100644
index 0000000000..022a102f3e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-zoneddatetime-negative-epochnanoseconds.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.equals
+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]
+---*/
+
+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(16, 50, 35, 0, 0, 1);
+const result = instance.equals(datetime);
+assert.sameValue(result, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..b59852bc69
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/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.equals
+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.equals(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..f6ab478702
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/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.equals
+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.equals(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/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..753983489c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/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.equals
+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.equals(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..b7af51bb67
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/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.equals
+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.equals(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/basic.js
new file mode 100644
index 0000000000..7f9a88ca8f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/basic.js
@@ -0,0 +1,18 @@
+// |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.equals
+description: Basic tests for equals()
+features: [Temporal]
+---*/
+
+const t1 = Temporal.PlainTime.from("08:44:15.321");
+const t1bis = Temporal.PlainTime.from("08:44:15.321");
+const t2 = Temporal.PlainTime.from("14:23:30.123");
+assert.sameValue(t1.equals(t1), true, "same object");
+assert.sameValue(t1.equals(t1bis), true, "different object");
+assert.sameValue(t1.equals(t2), false, "different times");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/branding.js
new file mode 100644
index 0000000000..130ebfd20f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/branding.js
@@ -0,0 +1,27 @@
+// |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.equals
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const equals = Temporal.PlainTime.prototype.equals;
+
+assert.sameValue(typeof equals, "function");
+
+const args = [new Temporal.PlainTime(12)];
+
+assert.throws(TypeError, () => equals.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => equals.apply(null, args), "null");
+assert.throws(TypeError, () => equals.apply(true, args), "true");
+assert.throws(TypeError, () => equals.apply("", args), "empty string");
+assert.throws(TypeError, () => equals.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => equals.apply(1, args), "1");
+assert.throws(TypeError, () => equals.apply({}, args), "plain object");
+assert.throws(TypeError, () => equals.apply(Temporal.PlainTime, args), "Temporal.PlainTime");
+assert.throws(TypeError, () => equals.apply(Temporal.PlainTime.prototype, args), "Temporal.PlainTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/builtin.js
new file mode 100644
index 0000000000..24538a40b9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/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.equals
+description: >
+ Tests that Temporal.PlainTime.prototype.equals
+ 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.equals),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainTime.prototype.equals),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainTime.prototype.equals),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainTime.prototype.equals.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/leap-second.js
new file mode 100644
index 0000000000..f2e3800021
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/leap-second.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.equals
+description: Leap second is a valid ISO string for PlainTime
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(23, 59, 59);
+
+let arg = "2016-12-31T23:59:60";
+const result1 = instance.equals(arg);
+assert.sameValue(
+ result1,
+ true,
+ "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.equals(arg);
+assert.sameValue(
+ result2,
+ true,
+ "second: 60 is ignored in property bag for PlainTime"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/length.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/length.js
new file mode 100644
index 0000000000..754520f733
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/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.equals
+description: Temporal.PlainTime.prototype.equals.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.equals, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/name.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/name.js
new file mode 100644
index 0000000000..3861880ffb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/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.equals
+description: Temporal.PlainTime.prototype.equals.name is "equals".
+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.equals, "name", {
+ value: "equals",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/not-a-constructor.js
new file mode 100644
index 0000000000..665cb307aa
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/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.equals
+description: >
+ Temporal.PlainTime.prototype.equals 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.equals();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainTime.prototype.equals), false,
+ "isConstructor(Temporal.PlainTime.prototype.equals)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/plaintime-propertybag-no-time-units.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/plaintime-propertybag-no-time-units.js
new file mode 100644
index 0000000000..46d372ec5a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/plaintime-propertybag-no-time-units.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.equals
+description: Missing time units in property bag default to 0
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(0, 30, 0, 0, 0, 0);
+
+const props = {};
+assert.throws(TypeError, () => instance.equals(props), "TypeError if no properties are present");
+
+props.minute = 30;
+const result = instance.equals(props);
+assert.sameValue(result, true, "missing time units default to 0");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/prop-desc.js
new file mode 100644
index 0000000000..377b8bde6f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/prop-desc.js
@@ -0,0 +1,24 @@
+// |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.equals
+description: The "equals" property of Temporal.PlainTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainTime.prototype.equals,
+ "function",
+ "`typeof PlainTime.prototype.equals` is `function`"
+);
+
+verifyProperty(Temporal.PlainTime.prototype, "equals", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/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/equals/year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/year-zero.js
new file mode 100644
index 0000000000..160eae9767
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/equals/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.equals
+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.equals(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/branding.js
new file mode 100644
index 0000000000..d363874fc6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/branding.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.getisofields
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const getISOFields = Temporal.PlainTime.prototype.getISOFields;
+
+assert.sameValue(typeof getISOFields, "function");
+
+assert.throws(TypeError, () => getISOFields.call(undefined), "undefined");
+assert.throws(TypeError, () => getISOFields.call(null), "null");
+assert.throws(TypeError, () => getISOFields.call(true), "true");
+assert.throws(TypeError, () => getISOFields.call(""), "empty string");
+assert.throws(TypeError, () => getISOFields.call(Symbol()), "symbol");
+assert.throws(TypeError, () => getISOFields.call(1), "1");
+assert.throws(TypeError, () => getISOFields.call({}), "plain object");
+assert.throws(TypeError, () => getISOFields.call(Temporal.PlainTime), "Temporal.PlainTime");
+assert.throws(TypeError, () => getISOFields.call(Temporal.PlainTime.prototype), "Temporal.PlainTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/builtin.js
new file mode 100644
index 0000000000..d0535e6c9a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/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.getisofields
+description: >
+ Tests that Temporal.PlainTime.prototype.getISOFields
+ 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.getISOFields),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainTime.prototype.getISOFields),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainTime.prototype.getISOFields),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainTime.prototype.getISOFields.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/field-names.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/field-names.js
new file mode 100644
index 0000000000..efab816b91
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/field-names.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.getisofields
+description: Correct field names on the object returned from getISOFields
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+const result = time.getISOFields();
+assert.sameValue(result.isoHour, 12, "isoHour result");
+assert.sameValue(result.isoMinute, 34, "isoMinute result");
+assert.sameValue(result.isoSecond, 56, "isoSecond result");
+assert.sameValue(result.isoMillisecond, 987, "isoMillisecond result");
+assert.sameValue(result.isoMicrosecond, 654, "isoMicrosecond result");
+assert.sameValue(result.isoNanosecond, 321, "isoNanosecond result");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/field-prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/field-prop-desc.js
new file mode 100644
index 0000000000..8c7f4fd3e8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/field-prop-desc.js
@@ -0,0 +1,32 @@
+// |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.getisofields
+description: Properties on the returned object have the correct descriptor
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "isoHour",
+ "isoMicrosecond",
+ "isoMillisecond",
+ "isoMinute",
+ "isoNanosecond",
+ "isoSecond",
+];
+
+const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+const result = time.getISOFields();
+
+for (const property of expected) {
+ verifyProperty(result, property, {
+ writable: true,
+ enumerable: true,
+ configurable: true,
+ });
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/field-traversal-order.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/field-traversal-order.js
new file mode 100644
index 0000000000..7c8e654af0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/field-traversal-order.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.getisofields
+description: Properties added in correct order to object returned from getISOFields
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "isoHour",
+ "isoMicrosecond",
+ "isoMillisecond",
+ "isoMinute",
+ "isoNanosecond",
+ "isoSecond",
+];
+
+const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+const result = time.getISOFields();
+
+assert.compareArray(Object.keys(result), expected);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/length.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/length.js
new file mode 100644
index 0000000000..2732f4a9df
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/length.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.getisofields
+description: Temporal.PlainTime.prototype.getISOFields.length is 0
+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.getISOFields, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/name.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/name.js
new file mode 100644
index 0000000000..a7c83c1466
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/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.getisofields
+description: Temporal.PlainTime.prototype.getISOFields.name is "getISOFields".
+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.getISOFields, "name", {
+ value: "getISOFields",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/not-a-constructor.js
new file mode 100644
index 0000000000..057ed2c4fc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/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.getisofields
+description: >
+ Temporal.PlainTime.prototype.getISOFields 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.getISOFields();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainTime.prototype.getISOFields), false,
+ "isConstructor(Temporal.PlainTime.prototype.getISOFields)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/prop-desc.js
new file mode 100644
index 0000000000..c530cf5adb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/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.getisofields
+description: The "getISOFields" property of Temporal.PlainTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainTime.prototype.getISOFields,
+ "function",
+ "`typeof PlainTime.prototype.getISOFields` is `function`"
+);
+
+verifyProperty(Temporal.PlainTime.prototype, "getISOFields", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/prototype.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/prototype.js
new file mode 100644
index 0000000000..086916464a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/prototype.js
@@ -0,0 +1,15 @@
+// |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.getisofields
+description: Correct prototype on the object returned from getISOFields
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+const result = instance.getISOFields();
+assert.sameValue(Object.getPrototypeOf(result), Object.prototype, "prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/getISOFields/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/hour/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/hour/branding.js
new file mode 100644
index 0000000000..0329ac3688
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/hour/branding.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-get-temporal.plaintime.prototype.hour
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const hour = Object.getOwnPropertyDescriptor(Temporal.PlainTime.prototype, "hour").get;
+
+assert.sameValue(typeof hour, "function");
+
+assert.throws(TypeError, () => hour.call(undefined), "undefined");
+assert.throws(TypeError, () => hour.call(null), "null");
+assert.throws(TypeError, () => hour.call(true), "true");
+assert.throws(TypeError, () => hour.call(""), "empty string");
+assert.throws(TypeError, () => hour.call(Symbol()), "symbol");
+assert.throws(TypeError, () => hour.call(1), "1");
+assert.throws(TypeError, () => hour.call({}), "plain object");
+assert.throws(TypeError, () => hour.call(Temporal.PlainTime), "Temporal.PlainTime");
+assert.throws(TypeError, () => hour.call(Temporal.PlainTime.prototype), "Temporal.PlainTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/hour/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/hour/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/hour/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/hour/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/hour/prop-desc.js
new file mode 100644
index 0000000000..2a54f27891
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/hour/prop-desc.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-get-temporal.plaintime.prototype.hour
+description: The "hour" property of Temporal.PlainTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainTime.prototype, "hour");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/hour/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/hour/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/hour/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/microsecond/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/microsecond/branding.js
new file mode 100644
index 0000000000..412d16146a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/microsecond/branding.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-get-temporal.plaintime.prototype.microsecond
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const microsecond = Object.getOwnPropertyDescriptor(Temporal.PlainTime.prototype, "microsecond").get;
+
+assert.sameValue(typeof microsecond, "function");
+
+assert.throws(TypeError, () => microsecond.call(undefined), "undefined");
+assert.throws(TypeError, () => microsecond.call(null), "null");
+assert.throws(TypeError, () => microsecond.call(true), "true");
+assert.throws(TypeError, () => microsecond.call(""), "empty string");
+assert.throws(TypeError, () => microsecond.call(Symbol()), "symbol");
+assert.throws(TypeError, () => microsecond.call(1), "1");
+assert.throws(TypeError, () => microsecond.call({}), "plain object");
+assert.throws(TypeError, () => microsecond.call(Temporal.PlainTime), "Temporal.PlainTime");
+assert.throws(TypeError, () => microsecond.call(Temporal.PlainTime.prototype), "Temporal.PlainTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/microsecond/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/microsecond/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/microsecond/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/microsecond/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/microsecond/prop-desc.js
new file mode 100644
index 0000000000..d05750e737
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/microsecond/prop-desc.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-get-temporal.plaintime.prototype.microsecond
+description: The "microsecond" property of Temporal.PlainTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainTime.prototype, "microsecond");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/microsecond/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/microsecond/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/microsecond/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/millisecond/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/millisecond/branding.js
new file mode 100644
index 0000000000..18c94ea454
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/millisecond/branding.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-get-temporal.plaintime.prototype.millisecond
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const millisecond = Object.getOwnPropertyDescriptor(Temporal.PlainTime.prototype, "millisecond").get;
+
+assert.sameValue(typeof millisecond, "function");
+
+assert.throws(TypeError, () => millisecond.call(undefined), "undefined");
+assert.throws(TypeError, () => millisecond.call(null), "null");
+assert.throws(TypeError, () => millisecond.call(true), "true");
+assert.throws(TypeError, () => millisecond.call(""), "empty string");
+assert.throws(TypeError, () => millisecond.call(Symbol()), "symbol");
+assert.throws(TypeError, () => millisecond.call(1), "1");
+assert.throws(TypeError, () => millisecond.call({}), "plain object");
+assert.throws(TypeError, () => millisecond.call(Temporal.PlainTime), "Temporal.PlainTime");
+assert.throws(TypeError, () => millisecond.call(Temporal.PlainTime.prototype), "Temporal.PlainTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/millisecond/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/millisecond/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/millisecond/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/millisecond/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/millisecond/prop-desc.js
new file mode 100644
index 0000000000..cda2bc0bea
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/millisecond/prop-desc.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-get-temporal.plaintime.prototype.millisecond
+description: The "millisecond" property of Temporal.PlainTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainTime.prototype, "millisecond");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/millisecond/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/millisecond/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/millisecond/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/minute/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/minute/branding.js
new file mode 100644
index 0000000000..b981434142
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/minute/branding.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-get-temporal.plaintime.prototype.minute
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const minute = Object.getOwnPropertyDescriptor(Temporal.PlainTime.prototype, "minute").get;
+
+assert.sameValue(typeof minute, "function");
+
+assert.throws(TypeError, () => minute.call(undefined), "undefined");
+assert.throws(TypeError, () => minute.call(null), "null");
+assert.throws(TypeError, () => minute.call(true), "true");
+assert.throws(TypeError, () => minute.call(""), "empty string");
+assert.throws(TypeError, () => minute.call(Symbol()), "symbol");
+assert.throws(TypeError, () => minute.call(1), "1");
+assert.throws(TypeError, () => minute.call({}), "plain object");
+assert.throws(TypeError, () => minute.call(Temporal.PlainTime), "Temporal.PlainTime");
+assert.throws(TypeError, () => minute.call(Temporal.PlainTime.prototype), "Temporal.PlainTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/minute/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/minute/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/minute/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/minute/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/minute/prop-desc.js
new file mode 100644
index 0000000000..c64a296a50
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/minute/prop-desc.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-get-temporal.plaintime.prototype.minute
+description: The "minute" property of Temporal.PlainTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainTime.prototype, "minute");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/minute/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/minute/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/minute/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/nanosecond/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/nanosecond/branding.js
new file mode 100644
index 0000000000..04da9afca7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/nanosecond/branding.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-get-temporal.plaintime.prototype.nanosecond
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const nanosecond = Object.getOwnPropertyDescriptor(Temporal.PlainTime.prototype, "nanosecond").get;
+
+assert.sameValue(typeof nanosecond, "function");
+
+assert.throws(TypeError, () => nanosecond.call(undefined), "undefined");
+assert.throws(TypeError, () => nanosecond.call(null), "null");
+assert.throws(TypeError, () => nanosecond.call(true), "true");
+assert.throws(TypeError, () => nanosecond.call(""), "empty string");
+assert.throws(TypeError, () => nanosecond.call(Symbol()), "symbol");
+assert.throws(TypeError, () => nanosecond.call(1), "1");
+assert.throws(TypeError, () => nanosecond.call({}), "plain object");
+assert.throws(TypeError, () => nanosecond.call(Temporal.PlainTime), "Temporal.PlainTime");
+assert.throws(TypeError, () => nanosecond.call(Temporal.PlainTime.prototype), "Temporal.PlainTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/nanosecond/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/nanosecond/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/nanosecond/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/nanosecond/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/nanosecond/prop-desc.js
new file mode 100644
index 0000000000..9b47e79e69
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/nanosecond/prop-desc.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-get-temporal.plaintime.prototype.nanosecond
+description: The "nanosecond" property of Temporal.PlainTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainTime.prototype, "nanosecond");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/nanosecond/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/nanosecond/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/nanosecond/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/prop-desc.js
new file mode 100644
index 0000000000..3e99b4fe25
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/prop-desc.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
+description: The "prototype" property of Temporal.PlainTime
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(typeof Temporal.PlainTime.prototype, "object");
+assert.notSameValue(Temporal.PlainTime.prototype, null);
+
+verifyProperty(Temporal.PlainTime, "prototype", {
+ writable: false,
+ enumerable: false,
+ configurable: false,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/branding.js
new file mode 100644
index 0000000000..97588eae2e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/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.round
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const round = Temporal.PlainTime.prototype.round;
+
+assert.sameValue(typeof round, "function");
+
+const args = ["hour"];
+
+assert.throws(TypeError, () => round.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => round.apply(null, args), "null");
+assert.throws(TypeError, () => round.apply(true, args), "true");
+assert.throws(TypeError, () => round.apply("", args), "empty string");
+assert.throws(TypeError, () => round.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => round.apply(1, args), "1");
+assert.throws(TypeError, () => round.apply({}, args), "plain object");
+assert.throws(TypeError, () => round.apply(Temporal.PlainTime, args), "Temporal.PlainTime");
+assert.throws(TypeError, () => round.apply(Temporal.PlainTime.prototype, args), "Temporal.PlainTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/builtin.js
new file mode 100644
index 0000000000..ac46ab8fc4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/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.round
+description: >
+ Tests that Temporal.PlainTime.prototype.round
+ 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.round),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainTime.prototype.round),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainTime.prototype.round),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainTime.prototype.round.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/length.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/length.js
new file mode 100644
index 0000000000..f4ff8fb86e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/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.round
+description: Temporal.PlainTime.prototype.round.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.round, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/name.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/name.js
new file mode 100644
index 0000000000..4f6f5b4c87
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/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.round
+description: Temporal.PlainTime.prototype.round.name is "round".
+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.round, "name", {
+ value: "round",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/not-a-constructor.js
new file mode 100644
index 0000000000..591cc2d3a0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/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.round
+description: >
+ Temporal.PlainTime.prototype.round 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.round();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainTime.prototype.round), false,
+ "isConstructor(Temporal.PlainTime.prototype.round)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/options-wrong-type.js
new file mode 100644
index 0000000000..c576d9cebd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/options-wrong-type.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.round
+description: TypeError thrown when options argument is missing or a non-string primitive
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const badOptions = [
+ undefined,
+ null,
+ true,
+ Symbol(),
+ 1,
+ 2n,
+];
+
+const instance = new Temporal.PlainTime();
+assert.throws(TypeError, () => instance.round(), "TypeError on missing options argument");
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.round(value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/prop-desc.js
new file mode 100644
index 0000000000..13c7270880
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/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.round
+description: The "round" property of Temporal.PlainTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainTime.prototype.round,
+ "function",
+ "`typeof PlainTime.prototype.round` is `function`"
+);
+
+verifyProperty(Temporal.PlainTime.prototype, "round", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/rounding-cross-midnight.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/rounding-cross-midnight.js
new file mode 100644
index 0000000000..294ba531ed
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/rounding-cross-midnight.js
@@ -0,0 +1,17 @@
+// |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.round
+description: Rounding can cross midnight
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainTime = Temporal.PlainTime.from("23:59:59.999999999");
+for (const smallestUnit of ["hour", "minute", "second", "millisecond", "microsecond"]) {
+ TemporalHelpers.assertPlainTime(plainTime.round({ smallestUnit }), 0, 0, 0, 0, 0, 0);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-hours.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-hours.js
new file mode 100644
index 0000000000..ec7cbe2b56
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-hours.js
@@ -0,0 +1,36 @@
+// |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.round
+description: Valid values for roundingIncrement option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainTime = new Temporal.PlainTime(3, 34, 56, 987, 654, 321);
+
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "hours", roundingIncrement: 1 }),
+ 4, 0, 0, 0, 0, 0, "hours");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "hours", roundingIncrement: 2 }),
+ 4, 0, 0, 0, 0, 0, "hours");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "hours", roundingIncrement: 3 }),
+ 3, 0, 0, 0, 0, 0, "hours");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "hours", roundingIncrement: 4 }),
+ 4, 0, 0, 0, 0, 0, "hours");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "hours", roundingIncrement: 6 }),
+ 6, 0, 0, 0, 0, 0, "hours");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "hours", roundingIncrement: 8 }),
+ 0, 0, 0, 0, 0, 0, "hours");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "hours", roundingIncrement: 12 }),
+ 0, 0, 0, 0, 0, 0, "hours");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-invalid.js
new file mode 100644
index 0000000000..46eab63aa7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-invalid.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.round
+description: Tests roundingIncrement restrictions.
+features: [Temporal]
+---*/
+
+const plainTime = Temporal.PlainTime.from("08:22:36.123456789");
+
+assert.throws(RangeError, () => plainTime.round({ smallestUnit: "hours", roundingIncrement: 11 }));
+assert.throws(RangeError, () => plainTime.round({ smallestUnit: "minutes", roundingIncrement: 29 }));
+assert.throws(RangeError, () => plainTime.round({ smallestUnit: "seconds", roundingIncrement: 29 }));
+assert.throws(RangeError, () => plainTime.round({ smallestUnit: "milliseconds", roundingIncrement: 29 }));
+assert.throws(RangeError, () => plainTime.round({ smallestUnit: "microseconds", roundingIncrement: 29 }));
+assert.throws(RangeError, () => plainTime.round({ smallestUnit: "nanoseconds", roundingIncrement: 29 }));
+assert.throws(RangeError, () => plainTime.round({ smallestUnit: "hours", roundingIncrement: 24 }));
+assert.throws(RangeError, () => plainTime.round({ smallestUnit: "minutes", roundingIncrement: 60 }));
+assert.throws(RangeError, () => plainTime.round({ smallestUnit: "seconds", roundingIncrement: 60 }));
+assert.throws(RangeError, () => plainTime.round({ smallestUnit: "milliseconds", roundingIncrement: 1000 }));
+assert.throws(RangeError, () => plainTime.round({ smallestUnit: "microseconds", roundingIncrement: 1000 }));
+assert.throws(RangeError, () => plainTime.round({ smallestUnit: "nanoseconds", roundingIncrement: 1000 }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-microseconds.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-microseconds.js
new file mode 100644
index 0000000000..20d4a13ded
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-microseconds.js
@@ -0,0 +1,60 @@
+// |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.round
+description: Valid values for roundingIncrement option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainTime = new Temporal.PlainTime(3, 34, 56, 987, 654, 321);
+
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "microseconds", roundingIncrement: 1 }),
+ 3, 34, 56, 987, 654, 0, "microseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "microseconds", roundingIncrement: 2 }),
+ 3, 34, 56, 987, 654, 0, "microseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "microseconds", roundingIncrement: 4 }),
+ 3, 34, 56, 987, 656, 0, "microseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "microseconds", roundingIncrement: 5 }),
+ 3, 34, 56, 987, 655, 0, "microseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "microseconds", roundingIncrement: 8 }),
+ 3, 34, 56, 987, 656, 0, "microseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "microseconds", roundingIncrement: 10 }),
+ 3, 34, 56, 987, 650, 0, "microseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "microseconds", roundingIncrement: 20 }),
+ 3, 34, 56, 987, 660, 0, "microseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "microseconds", roundingIncrement: 25 }),
+ 3, 34, 56, 987, 650, 0, "microseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "microseconds", roundingIncrement: 40 }),
+ 3, 34, 56, 987, 640, 0, "microseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "microseconds", roundingIncrement: 50 }),
+ 3, 34, 56, 987, 650, 0, "microseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "microseconds", roundingIncrement: 100 }),
+ 3, 34, 56, 987, 700, 0, "microseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "microseconds", roundingIncrement: 125 }),
+ 3, 34, 56, 987, 625, 0, "microseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "microseconds", roundingIncrement: 200 }),
+ 3, 34, 56, 987, 600, 0, "microseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "microseconds", roundingIncrement: 250 }),
+ 3, 34, 56, 987, 750, 0, "microseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "microseconds", roundingIncrement: 500 }),
+ 3, 34, 56, 987, 500, 0, "microseconds");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-milliseconds.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-milliseconds.js
new file mode 100644
index 0000000000..e84da703b2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-milliseconds.js
@@ -0,0 +1,60 @@
+// |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.round
+description: Valid values for roundingIncrement option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainTime = new Temporal.PlainTime(3, 34, 56, 987, 654, 321);
+
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "milliseconds", roundingIncrement: 1 }),
+ 3, 34, 56, 988, 0, 0, "milliseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "milliseconds", roundingIncrement: 2 }),
+ 3, 34, 56, 988, 0, 0, "milliseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "milliseconds", roundingIncrement: 4 }),
+ 3, 34, 56, 988, 0, 0, "milliseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "milliseconds", roundingIncrement: 5 }),
+ 3, 34, 56, 990, 0, 0, "milliseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "milliseconds", roundingIncrement: 8 }),
+ 3, 34, 56, 984, 0, 0, "milliseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "milliseconds", roundingIncrement: 10 }),
+ 3, 34, 56, 990, 0, 0, "milliseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "milliseconds", roundingIncrement: 20 }),
+ 3, 34, 56, 980, 0, 0, "milliseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "milliseconds", roundingIncrement: 25 }),
+ 3, 34, 57, 0, 0, 0, "milliseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "milliseconds", roundingIncrement: 40 }),
+ 3, 34, 57, 0, 0, 0, "milliseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "milliseconds", roundingIncrement: 50 }),
+ 3, 34, 57, 0, 0, 0, "milliseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "milliseconds", roundingIncrement: 100 }),
+ 3, 34, 57, 0, 0, 0, "milliseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "milliseconds", roundingIncrement: 125 }),
+ 3, 34, 57, 0, 0, 0, "milliseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "milliseconds", roundingIncrement: 200 }),
+ 3, 34, 57, 0, 0, 0, "milliseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "milliseconds", roundingIncrement: 250 }),
+ 3, 34, 57, 0, 0, 0, "milliseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "milliseconds", roundingIncrement: 500 }),
+ 3, 34, 57, 0, 0, 0, "milliseconds");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-minutes.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-minutes.js
new file mode 100644
index 0000000000..88b8a73dd1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-minutes.js
@@ -0,0 +1,48 @@
+// |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.round
+description: Valid values for roundingIncrement option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainTime = new Temporal.PlainTime(3, 34, 56, 987, 654, 321);
+
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "minutes", roundingIncrement: 1 }),
+ 3, 35, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "minutes", roundingIncrement: 2 }),
+ 3, 34, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "minutes", roundingIncrement: 3 }),
+ 3, 36, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "minutes", roundingIncrement: 4 }),
+ 3, 36, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "minutes", roundingIncrement: 5 }),
+ 3, 35, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "minutes", roundingIncrement: 6 }),
+ 3, 36, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "minutes", roundingIncrement: 10 }),
+ 3, 30, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "minutes", roundingIncrement: 12 }),
+ 3, 36, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "minutes", roundingIncrement: 15 }),
+ 3, 30, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "minutes", roundingIncrement: 20 }),
+ 3, 40, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "minutes", roundingIncrement: 30 }),
+ 3, 30, 0, 0, 0, 0, "minutes");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-nan.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-nan.js
new file mode 100644
index 0000000000..c1874a2bd7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-nan.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.round
+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.round step 11:
+ 10. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*).
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+assert.throws(RangeError, () => time.round({ smallestUnit: 'second', roundingIncrement: NaN }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-nanoseconds.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-nanoseconds.js
new file mode 100644
index 0000000000..9b6a1c8f77
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-nanoseconds.js
@@ -0,0 +1,60 @@
+// |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.round
+description: Valid values for roundingIncrement option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainTime = new Temporal.PlainTime(3, 34, 56, 987, 654, 321);
+
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "nanoseconds", roundingIncrement: 1 }),
+ 3, 34, 56, 987, 654, 321, "nanoseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "nanoseconds", roundingIncrement: 2 }),
+ 3, 34, 56, 987, 654, 322, "nanoseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "nanoseconds", roundingIncrement: 4 }),
+ 3, 34, 56, 987, 654, 320, "nanoseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "nanoseconds", roundingIncrement: 5 }),
+ 3, 34, 56, 987, 654, 320, "nanoseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "nanoseconds", roundingIncrement: 8 }),
+ 3, 34, 56, 987, 654, 320, "nanoseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "nanoseconds", roundingIncrement: 10 }),
+ 3, 34, 56, 987, 654, 320, "nanoseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "nanoseconds", roundingIncrement: 20 }),
+ 3, 34, 56, 987, 654, 320, "nanoseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "nanoseconds", roundingIncrement: 25 }),
+ 3, 34, 56, 987, 654, 325, "nanoseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "nanoseconds", roundingIncrement: 40 }),
+ 3, 34, 56, 987, 654, 320, "nanoseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "nanoseconds", roundingIncrement: 50 }),
+ 3, 34, 56, 987, 654, 300, "nanoseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "nanoseconds", roundingIncrement: 100 }),
+ 3, 34, 56, 987, 654, 300, "nanoseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "nanoseconds", roundingIncrement: 125 }),
+ 3, 34, 56, 987, 654, 375, "nanoseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "nanoseconds", roundingIncrement: 200 }),
+ 3, 34, 56, 987, 654, 400, "nanoseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "nanoseconds", roundingIncrement: 250 }),
+ 3, 34, 56, 987, 654, 250, "nanoseconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "nanoseconds", roundingIncrement: 500 }),
+ 3, 34, 56, 987, 654, 500, "nanoseconds");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-non-integer.js
new file mode 100644
index 0000000000..e70e20ea85
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-non-integer.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.round
+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 time = new Temporal.PlainTime(12, 34, 56, 0, 0, 5);
+const result = time.round({ smallestUnit: "nanosecond", roundingIncrement: 2.5, roundingMode: "expand" });
+TemporalHelpers.assertPlainTime(result, 12, 34, 56, 0, 0, 6, "roundingIncrement 2.5 truncates to 2");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-out-of-range.js
new file mode 100644
index 0000000000..832bae5f60
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-out-of-range.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.round
+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 time = new Temporal.PlainTime(12, 34, 56, 0, 0, 5);
+assert.throws(RangeError, () => time.round({ smallestUnit: "nanoseconds", roundingIncrement: -Infinity }));
+assert.throws(RangeError, () => time.round({ smallestUnit: "nanoseconds", roundingIncrement: -1 }));
+assert.throws(RangeError, () => time.round({ smallestUnit: "nanoseconds", roundingIncrement: 0 }));
+assert.throws(RangeError, () => time.round({ smallestUnit: "nanoseconds", roundingIncrement: 0.9 }));
+assert.throws(RangeError, () => time.round({ smallestUnit: "nanoseconds", roundingIncrement: 1e9 + 1 }));
+assert.throws(RangeError, () => time.round({ smallestUnit: "nanoseconds", roundingIncrement: Infinity }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-seconds.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-seconds.js
new file mode 100644
index 0000000000..342bccbf37
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-seconds.js
@@ -0,0 +1,48 @@
+// |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.round
+description: Valid values for roundingIncrement option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainTime = new Temporal.PlainTime(3, 34, 56, 987, 654, 321);
+
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "seconds", roundingIncrement: 1 }),
+ 3, 34, 57, 0, 0, 0, "seconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "seconds", roundingIncrement: 2 }),
+ 3, 34, 56, 0, 0, 0, "seconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "seconds", roundingIncrement: 3 }),
+ 3, 34, 57, 0, 0, 0, "seconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "seconds", roundingIncrement: 4 }),
+ 3, 34, 56, 0, 0, 0, "seconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "seconds", roundingIncrement: 5 }),
+ 3, 34, 55, 0, 0, 0, "seconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "seconds", roundingIncrement: 6 }),
+ 3, 34, 54, 0, 0, 0, "seconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "seconds", roundingIncrement: 10 }),
+ 3, 35, 0, 0, 0, 0, "seconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "seconds", roundingIncrement: 12 }),
+ 3, 35, 0, 0, 0, 0, "seconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "seconds", roundingIncrement: 15 }),
+ 3, 35, 0, 0, 0, 0, "seconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "seconds", roundingIncrement: 20 }),
+ 3, 35, 0, 0, 0, 0, "seconds");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "seconds", roundingIncrement: 30 }),
+ 3, 35, 0, 0, 0, 0, "seconds");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-undefined.js
new file mode 100644
index 0000000000..a587e8c163
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-undefined.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.round
+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.round step 11:
+ 11. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+const explicit = time.round({ smallestUnit: 'second', roundingIncrement: undefined });
+TemporalHelpers.assertPlainTime(explicit, 12, 34, 57, 0, 0, 0, "default roundingIncrement is 1");
+
+const implicit = time.round({ smallestUnit: 'second' });
+TemporalHelpers.assertPlainTime(implicit, 12, 34, 57, 0, 0, 0, "default roundingIncrement is 1");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-wrong-type.js
new file mode 100644
index 0000000000..fa9fbc8404
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-wrong-type.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.round
+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.round step 11:
+ 11. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*).
+includes: [temporalHelpers.js, compareArray.js]
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+TemporalHelpers.checkRoundingIncrementOptionWrongType(
+ (roundingIncrement) => time.round({ smallestUnit: 'second', roundingIncrement }),
+ (result, descr) => TemporalHelpers.assertPlainTime(result, 12, 34, 57, 0, 0, 0, descr),
+ (result, descr) => TemporalHelpers.assertPlainTime(result, 12, 34, 56, 0, 0, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-ceil.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-ceil.js
new file mode 100644
index 0000000000..022e5156f0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-ceil.js
@@ -0,0 +1,34 @@
+// |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.round
+description: Tests calculations with roundingMode "ceil".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(13, 46, 23, 123, 987, 500);
+
+const expected = [
+ ["hour", [14]],
+ ["minute", [13, 47]],
+ ["second", [13, 46, 24]],
+ ["millisecond", [13, 46, 23, 124]],
+ ["microsecond", [13, 46, 23, 123, 988]],
+ ["nanosecond", [13, 46, 23, 123, 987, 500]],
+];
+
+const roundingMode = "ceil";
+
+expected.forEach(([smallestUnit, expected]) => {
+ const [h, min = 0, s = 0, ms = 0, µs = 0, ns = 0] = expected;
+ TemporalHelpers.assertPlainTime(
+ instance.round({ smallestUnit, roundingMode }),
+ h, min, s, ms, µs, ns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-expand.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-expand.js
new file mode 100644
index 0000000000..2cfffad15d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-expand.js
@@ -0,0 +1,34 @@
+// |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.round
+description: Tests calculations with roundingMode "expand".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(13, 46, 23, 123, 987, 500);
+
+const expected = [
+ ["hour", [14]],
+ ["minute", [13, 47]],
+ ["second", [13, 46, 24]],
+ ["millisecond", [13, 46, 23, 124]],
+ ["microsecond", [13, 46, 23, 123, 988]],
+ ["nanosecond", [13, 46, 23, 123, 987, 500]],
+];
+
+const roundingMode = "expand";
+
+expected.forEach(([smallestUnit, expected]) => {
+ const [h, min = 0, s = 0, ms = 0, µs = 0, ns = 0] = expected;
+ TemporalHelpers.assertPlainTime(
+ instance.round({ smallestUnit, roundingMode }),
+ h, min, s, ms, µs, ns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-floor.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-floor.js
new file mode 100644
index 0000000000..e8616991f2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-floor.js
@@ -0,0 +1,34 @@
+// |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.round
+description: Tests calculations with roundingMode "floor".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(13, 46, 23, 123, 987, 500);
+
+const expected = [
+ ["hour", [13]],
+ ["minute", [13, 46]],
+ ["second", [13, 46, 23]],
+ ["millisecond", [13, 46, 23, 123]],
+ ["microsecond", [13, 46, 23, 123, 987]],
+ ["nanosecond", [13, 46, 23, 123, 987, 500]],
+];
+
+const roundingMode = "floor";
+
+expected.forEach(([smallestUnit, expected]) => {
+ const [h, min = 0, s = 0, ms = 0, µs = 0, ns = 0] = expected;
+ TemporalHelpers.assertPlainTime(
+ instance.round({ smallestUnit, roundingMode }),
+ h, min, s, ms, µs, ns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-halfCeil.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-halfCeil.js
new file mode 100644
index 0000000000..d026326ec0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-halfCeil.js
@@ -0,0 +1,34 @@
+// |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.round
+description: Tests calculations with roundingMode "halfCeil".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(13, 46, 23, 123, 987, 500);
+
+const expected = [
+ ["hour", [14]],
+ ["minute", [13, 46]],
+ ["second", [13, 46, 23]],
+ ["millisecond", [13, 46, 23, 124]],
+ ["microsecond", [13, 46, 23, 123, 988]],
+ ["nanosecond", [13, 46, 23, 123, 987, 500]],
+];
+
+const roundingMode = "halfCeil";
+
+expected.forEach(([smallestUnit, expected]) => {
+ const [h, min = 0, s = 0, ms = 0, µs = 0, ns = 0] = expected;
+ TemporalHelpers.assertPlainTime(
+ instance.round({ smallestUnit, roundingMode }),
+ h, min, s, ms, µs, ns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-halfEven.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-halfEven.js
new file mode 100644
index 0000000000..1b1896efe1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-halfEven.js
@@ -0,0 +1,34 @@
+// |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.round
+description: Tests calculations with roundingMode "halfEven".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(13, 46, 23, 123, 987, 500);
+
+const expected = [
+ ["hour", [14]],
+ ["minute", [13, 46]],
+ ["second", [13, 46, 23]],
+ ["millisecond", [13, 46, 23, 124]],
+ ["microsecond", [13, 46, 23, 123, 988]],
+ ["nanosecond", [13, 46, 23, 123, 987, 500]],
+];
+
+const roundingMode = "halfEven";
+
+expected.forEach(([smallestUnit, expected]) => {
+ const [h, min = 0, s = 0, ms = 0, µs = 0, ns = 0] = expected;
+ TemporalHelpers.assertPlainTime(
+ instance.round({ smallestUnit, roundingMode }),
+ h, min, s, ms, µs, ns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-halfExpand.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-halfExpand.js
new file mode 100644
index 0000000000..c0f93790fb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-halfExpand.js
@@ -0,0 +1,34 @@
+// |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.round
+description: Tests calculations with roundingMode "halfExpand".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(13, 46, 23, 123, 987, 500);
+
+const expected = [
+ ["hour", [14]],
+ ["minute", [13, 46]],
+ ["second", [13, 46, 23]],
+ ["millisecond", [13, 46, 23, 124]],
+ ["microsecond", [13, 46, 23, 123, 988]],
+ ["nanosecond", [13, 46, 23, 123, 987, 500]],
+];
+
+const roundingMode = "halfExpand";
+
+expected.forEach(([smallestUnit, expected]) => {
+ const [h, min = 0, s = 0, ms = 0, µs = 0, ns = 0] = expected;
+ TemporalHelpers.assertPlainTime(
+ instance.round({ smallestUnit, roundingMode }),
+ h, min, s, ms, µs, ns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-halfFloor.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-halfFloor.js
new file mode 100644
index 0000000000..0bfe52baff
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-halfFloor.js
@@ -0,0 +1,34 @@
+// |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.round
+description: Tests calculations with roundingMode "halfFloor".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(13, 46, 23, 123, 987, 500);
+
+const expected = [
+ ["hour", [14]],
+ ["minute", [13, 46]],
+ ["second", [13, 46, 23]],
+ ["millisecond", [13, 46, 23, 124]],
+ ["microsecond", [13, 46, 23, 123, 987]],
+ ["nanosecond", [13, 46, 23, 123, 987, 500]],
+];
+
+const roundingMode = "halfFloor";
+
+expected.forEach(([smallestUnit, expected]) => {
+ const [h, min = 0, s = 0, ms = 0, µs = 0, ns = 0] = expected;
+ TemporalHelpers.assertPlainTime(
+ instance.round({ smallestUnit, roundingMode }),
+ h, min, s, ms, µs, ns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-halfTrunc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-halfTrunc.js
new file mode 100644
index 0000000000..bcab3dd219
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-halfTrunc.js
@@ -0,0 +1,34 @@
+// |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.round
+description: Tests calculations with roundingMode "halfTrunc".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(13, 46, 23, 123, 987, 500);
+
+const expected = [
+ ["hour", [14]],
+ ["minute", [13, 46]],
+ ["second", [13, 46, 23]],
+ ["millisecond", [13, 46, 23, 124]],
+ ["microsecond", [13, 46, 23, 123, 987]],
+ ["nanosecond", [13, 46, 23, 123, 987, 500]],
+];
+
+const roundingMode = "halfTrunc";
+
+expected.forEach(([smallestUnit, expected]) => {
+ const [h, min = 0, s = 0, ms = 0, µs = 0, ns = 0] = expected;
+ TemporalHelpers.assertPlainTime(
+ instance.round({ smallestUnit, roundingMode }),
+ h, min, s, ms, µs, ns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-invalid-string.js
new file mode 100644
index 0000000000..619f34d9fb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-invalid-string.js
@@ -0,0 +1,16 @@
+// |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.round
+description: RangeError thrown when roundingMode option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 123, 987, 500);
+for (const roundingMode of ["other string", "cile", "CEIL", "ce\u0131l", "auto", "halfexpand", "floor\0"]) {
+ assert.throws(RangeError, () => time.round({ smallestUnit: "microsecond", roundingMode }));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-trunc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-trunc.js
new file mode 100644
index 0000000000..a687834ecc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-trunc.js
@@ -0,0 +1,34 @@
+// |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.round
+description: Tests calculations with roundingMode "trunc".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(13, 46, 23, 123, 987, 500);
+
+const expected = [
+ ["hour", [13]],
+ ["minute", [13, 46]],
+ ["second", [13, 46, 23]],
+ ["millisecond", [13, 46, 23, 123]],
+ ["microsecond", [13, 46, 23, 123, 987]],
+ ["nanosecond", [13, 46, 23, 123, 987, 500]],
+];
+
+const roundingMode = "trunc";
+
+expected.forEach(([smallestUnit, expected]) => {
+ const [h, min = 0, s = 0, ms = 0, µs = 0, ns = 0] = expected;
+ TemporalHelpers.assertPlainTime(
+ instance.round({ smallestUnit, roundingMode }),
+ h, min, s, ms, µs, ns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-undefined.js
new file mode 100644
index 0000000000..8f1d4d44a8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-undefined.js
@@ -0,0 +1,56 @@
+// |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.round
+description: Tests calculations with roundingMode undefined.
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const plainTime = Temporal.PlainTime.from("13:46:23.123456789");
+
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "hour", roundingMode: undefined }),
+ 14, 0, 0, 0, 0, 0, "hour");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "hour" }),
+ 14, 0, 0, 0, 0, 0, "hour");
+
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "minute", roundingMode: undefined }),
+ 13, 46, 0, 0, 0, 0, "minute");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "minute" }),
+ 13, 46, 0, 0, 0, 0, "minute");
+
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "second", roundingMode: undefined }),
+ 13, 46, 23, 0, 0, 0, "second");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "second" }),
+ 13, 46, 23, 0, 0, 0, "second");
+
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "millisecond", roundingMode: undefined }),
+ 13, 46, 23, 123, 0, 0, "millisecond");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "millisecond" }),
+ 13, 46, 23, 123, 0, 0, "millisecond");
+
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "microsecond", roundingMode: undefined }),
+ 13, 46, 23, 123, 457, 0, "microsecond");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "microsecond" }),
+ 13, 46, 23, 123, 457, 0, "microsecond");
+
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "nanosecond", roundingMode: undefined }),
+ 13, 46, 23, 123, 456, 789, "nanosecond");
+TemporalHelpers.assertPlainTime(
+ plainTime.round({ smallestUnit: "nanosecond" }),
+ 13, 46, 23, 123, 456, 789, "nanosecond");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-wrong-type.js
new file mode 100644
index 0000000000..3c8d77339d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingmode-wrong-type.js
@@ -0,0 +1,18 @@
+// |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.round
+description: Type conversions for roundingMode option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 123, 987, 500);
+TemporalHelpers.checkStringOptionWrongType("roundingMode", "halfExpand",
+ (roundingMode) => time.round({ smallestUnit: "microsecond", roundingMode }),
+ (result, descr) => TemporalHelpers.assertPlainTime(result, 12, 34, 56, 123, 988, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundto-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundto-invalid-string.js
new file mode 100644
index 0000000000..90b17f979c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundto-invalid-string.js
@@ -0,0 +1,38 @@
+// |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.round
+description: RangeError thrown when smallestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 123, 987, 500);
+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, () => time.round(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/round/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/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/round/smallestunit-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/smallestunit-invalid-string.js
new file mode 100644
index 0000000000..23e2b56612
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/smallestunit-invalid-string.js
@@ -0,0 +1,38 @@
+// |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.round
+description: RangeError thrown when smallestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 123, 987, 500);
+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, () => time.round({ 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/round/smallestunit-missing.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/smallestunit-missing.js
new file mode 100644
index 0000000000..0a1df6022a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/smallestunit-missing.js
@@ -0,0 +1,16 @@
+// |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.round
+description: RangeError thrown when smallestUnit option is missing
+features: [Temporal]
+---*/
+
+const plainTime = new Temporal.PlainTime(12, 34, 56, 123, 987, 500);
+assert.throws(TypeError, () => plainTime.round());
+assert.throws(RangeError, () => plainTime.round({}));
+assert.throws(RangeError, () => plainTime.round({ roundingIncrement: 1, roundingMode: "ceil" }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/smallestunit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/smallestunit-plurals-accepted.js
new file mode 100644
index 0000000000..8cc10926f8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/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.round
+description: Plural units are accepted as well for the smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal, arrow-function]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 789, 999, 999);
+const validUnits = [
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+];
+TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => time.round({ smallestUnit }), validUnits);
+TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => time.round(smallestUnit), validUnits);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/smallestunit-string-shorthand.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/smallestunit-string-shorthand.js
new file mode 100644
index 0000000000..e3cc87fa1b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/smallestunit-string-shorthand.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.round
+description: String as first argument is equivalent to options bag with smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal, arrow-function]
+---*/
+
+const instance = new Temporal.PlainTime(12, 34, 56, 789, 999, 999);
+const validUnits = [
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+];
+validUnits.forEach((smallestUnit) => {
+ const full = instance.round({ smallestUnit });
+ const shorthand = instance.round(smallestUnit);
+ TemporalHelpers.assertPlainTimesEqual(shorthand, full, `"${smallestUnit}" as first argument to round is equivalent to options bag`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/smallestunit-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/smallestunit-wrong-type.js
new file mode 100644
index 0000000000..ccaabccf26
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/smallestunit-wrong-type.js
@@ -0,0 +1,18 @@
+// |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.round
+description: Type conversions for smallestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 123, 987, 500);
+TemporalHelpers.checkStringOptionWrongType("smallestUnit", "microsecond",
+ (smallestUnit) => time.round({ smallestUnit }),
+ (result, descr) => TemporalHelpers.assertPlainTime(result, 12, 34, 56, 123, 988, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/subclassing-ignored.js
new file mode 100644
index 0000000000..0b574f0a9d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/subclassing-ignored.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.round
+description: Objects of a subclass are never created as return values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.PlainTime,
+ [12, 34, 56, 987, 654, 321],
+ "round",
+ [{ smallestUnit: 'second' }],
+ (result) => TemporalHelpers.assertPlainTime(result, 12, 34, 57, 0, 0, 0),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/second/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/second/branding.js
new file mode 100644
index 0000000000..aa3af6a146
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/second/branding.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-get-temporal.plaintime.prototype.second
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const second = Object.getOwnPropertyDescriptor(Temporal.PlainTime.prototype, "second").get;
+
+assert.sameValue(typeof second, "function");
+
+assert.throws(TypeError, () => second.call(undefined), "undefined");
+assert.throws(TypeError, () => second.call(null), "null");
+assert.throws(TypeError, () => second.call(true), "true");
+assert.throws(TypeError, () => second.call(""), "empty string");
+assert.throws(TypeError, () => second.call(Symbol()), "symbol");
+assert.throws(TypeError, () => second.call(1), "1");
+assert.throws(TypeError, () => second.call({}), "plain object");
+assert.throws(TypeError, () => second.call(Temporal.PlainTime), "Temporal.PlainTime");
+assert.throws(TypeError, () => second.call(Temporal.PlainTime.prototype), "Temporal.PlainTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/second/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/second/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/second/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/second/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/second/prop-desc.js
new file mode 100644
index 0000000000..c4a03e47c4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/second/prop-desc.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-get-temporal.plaintime.prototype.second
+description: The "second" property of Temporal.PlainTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainTime.prototype, "second");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/second/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/second/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/second/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/shell.js
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);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-duration-max.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-duration-max.js
new file mode 100644
index 0000000000..6e24c81554
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-duration-max.js
@@ -0,0 +1,58 @@
+// |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.subtract
+description: Maximum allowed duration
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime();
+
+const maxCases = [
+ ["P4294967295Y104249991374DT7H36M31.999999999S", "string with max years"],
+ [{ years: 4294967295, days: 104249991374, nanoseconds: 27391999999999 }, "property bag with max years"],
+ ["P4294967295M104249991374DT7H36M31.999999999S", "string with max weeks"],
+ [{ months: 4294967295, days: 104249991374, nanoseconds: 27391999999999 }, "property bag with max months"],
+ ["P4294967295W104249991374DT7H36M31.999999999S", "string with max weeks"],
+ [{ weeks: 4294967295, days: 104249991374, nanoseconds: 27391999999999 }, "property bag with max weeks"],
+ ["P104249991374DT7H36M31.999999999S", "string with max days"],
+ [{ days: 104249991374, nanoseconds: 27391999999999 }, "property bag with max days"],
+ ["PT2501999792983H36M31.999999999S", "string with max hours"],
+ [{ hours: 2501999792983, nanoseconds: 2191999999999 }, "property bag with max hours"],
+ ["PT150119987579016M31.999999999S", "string with max minutes"],
+ [{ minutes: 150119987579016, nanoseconds: 31999999999 }, "property bag with max minutes"],
+ ["PT9007199254740991.999999999S", "string with max seconds"],
+ [{ seconds: 9007199254740991, nanoseconds: 999999999 }, "property bag with max seconds"],
+];
+
+for (const [arg, descr] of maxCases) {
+ const result = instance.subtract(arg);
+ TemporalHelpers.assertPlainTime(result, 16, 23, 28, 0, 0, 1, `operation succeeds with ${descr}`);
+}
+
+const minCases = [
+ ["-P4294967295Y104249991374DT7H36M31.999999999S", "string with min years"],
+ [{ years: -4294967295, days: -104249991374, nanoseconds: -27391999999999 }, "property bag with min years"],
+ ["-P4294967295M104249991374DT7H36M31.999999999S", "string with min months"],
+ [{ months: -4294967295, days: -104249991374, nanoseconds: -27391999999999 }, "property bag with min months"],
+ ["-P4294967295W104249991374DT7H36M31.999999999S", "string with min weeks"],
+ [{ weeks: -4294967295, days: -104249991374, nanoseconds: -27391999999999 }, "property bag with min weeks"],
+ ["-P104249991374DT7H36M31.999999999S", "string with min days"],
+ [{ days: -104249991374, nanoseconds: -27391999999999 }, "property bag with min days"],
+ ["-PT2501999792983H36M31.999999999S", "string with min hours"],
+ [{ hours: -2501999792983, nanoseconds: -2191999999999 }, "property bag with min hours"],
+ ["-PT150119987579016M31.999999999S", "string with min minutes"],
+ [{ minutes: -150119987579016, nanoseconds: -31999999999 }, "property bag with min minutes"],
+ ["-PT9007199254740991.999999999S", "string with min seconds"],
+ [{ seconds: -9007199254740991, nanoseconds: -999999999 }, "property bag with min seconds"],
+];
+
+for (const [arg, descr] of minCases) {
+ const result = instance.subtract(arg);
+ TemporalHelpers.assertPlainTime(result, 7, 36, 31, 999, 999, 999, `operation succeeds with ${descr}`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-duration-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-duration-out-of-range.js
new file mode 100644
index 0000000000..8227cc217b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-duration-out-of-range.js
@@ -0,0 +1,75 @@
+// |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.subtract
+description: Duration-like argument that is out of range
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime();
+
+const cases = [
+ // 2^32 = 4294967296
+ ["P4294967296Y", "string with years > max"],
+ [{ years: 4294967296 }, "property bag with years > max"],
+ ["-P4294967296Y", "string with years < min"],
+ [{ years: -4294967296 }, "property bag with years < min"],
+ ["P4294967296M", "string with months > max"],
+ [{ months: 4294967296 }, "property bag with months > max"],
+ ["-P4294967296M", "string with months < min"],
+ [{ months: -4294967296 }, "property bag with months < min"],
+ ["P4294967296W", "string with weeks > max"],
+ [{ weeks: 4294967296 }, "property bag with weeks > max"],
+ ["-P4294967296W", "string with weeks < min"],
+ [{ weeks: -4294967296 }, "property bag with weeks < min"],
+
+ // ceil(max safe integer / 86400) = 104249991375
+ ["P104249991375D", "string with days > max"],
+ [{ days: 104249991375 }, "property bag with days > max"],
+ ["P104249991374DT24H", "string where hours balance into days > max"],
+ [{ days: 104249991374, hours: 24 }, "property bag where hours balance into days > max"],
+ ["-P104249991375D", "string with days < min"],
+ [{ days: -104249991375 }, "property bag with days < min"],
+ ["-P104249991374DT24H", "string where hours balance into days < min"],
+ [{ days: -104249991374, hours: -24 }, "property bag where hours balance into days < min"],
+
+ // ceil(max safe integer / 3600) = 2501999792984
+ ["PT2501999792984H", "string with hours > max"],
+ [{ hours: 2501999792984 }, "property bag with hours > max"],
+ ["PT2501999792983H60M", "string where minutes balance into hours > max"],
+ [{ hours: 2501999792983, minutes: 60 }, "property bag where minutes balance into hours > max"],
+ ["-PT2501999792984H", "string with hours < min"],
+ [{ hours: -2501999792984 }, "property bag with hours < min"],
+ ["-PT2501999792983H60M", "string where minutes balance into hours < min"],
+ [{ hours: -2501999792983, minutes: -60 }, "property bag where minutes balance into hours < min"],
+
+ // ceil(max safe integer / 60) = 150119987579017
+ ["PT150119987579017M", "string with minutes > max"],
+ [{ minutes: 150119987579017 }, "property bag with minutes > max"],
+ ["PT150119987579016M60S", "string where seconds balance into minutes > max"],
+ [{ minutes: 150119987579016, seconds: 60 }, "property bag where seconds balance into minutes > max"],
+ ["-PT150119987579017M", "string with minutes < min"],
+ [{ minutes: -150119987579017 }, "property bag with minutes < min"],
+ ["-PT150119987579016M60S", "string where seconds balance into minutes < min"],
+ [{ minutes: -150119987579016, seconds: -60 }, "property bag where seconds balance into minutes < min"],
+
+ // 2^53 = 9007199254740992
+ ["PT9007199254740992S", "string with seconds > max"],
+ [{ seconds: 9007199254740992 }, "property bag with seconds > max"],
+ [{ seconds: 9007199254740991, milliseconds: 1000 }, "property bag where milliseconds balance into seconds > max"],
+ [{ seconds: 9007199254740991, microseconds: 1000000 }, "property bag where microseconds balance into seconds > max"],
+ [{ seconds: 9007199254740991, nanoseconds: 1000000000 }, "property bag where nanoseconds balance into seconds > max"],
+ ["-PT9007199254740992S", "string with seconds < min"],
+ [{ seconds: -9007199254740992 }, "property bag with seconds < min"],
+ [{ seconds: -9007199254740991, milliseconds: -1000 }, "property bag where milliseconds balance into seconds < min"],
+ [{ seconds: -9007199254740991, microseconds: -1000000 }, "property bag where microseconds balance into seconds < min"],
+ [{ seconds: -9007199254740991, nanoseconds: -1000000000 }, "property bag where nanoseconds balance into seconds < min"],
+];
+
+for (const [arg, descr] of cases) {
+ assert.throws(RangeError, () => instance.subtract(arg), `${descr} is out of range`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-duration-precision-exact-numerical-values.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-duration-precision-exact-numerical-values.js
new file mode 100644
index 0000000000..a595fa204f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-duration-precision-exact-numerical-values.js
@@ -0,0 +1,40 @@
+// |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.subtract
+description: >
+ Duration-like argument performs the range check with minimal floating point
+ precision loss
+features: [Temporal]
+---*/
+
+// Based on a test case by André Bargull
+
+const instance = new Temporal.PlainTime();
+
+const cases = [
+ [
+ {
+ milliseconds: 4503599627370497_000, // ℝ(𝔽(4503599627370497000)) = 4503599627370497024
+ microseconds: 4503599627370495_000000, // ℝ(𝔽(4503599627370495000000)) = 4503599627370494951424
+ },
+ // 4503599627370497024 / 1000 + 4503599627370494951424 / 1000000 is
+ // 9007199254740991.975424, which is below the limit of 2**53
+ "case where floating point inaccuracy brings total below limit, positive"
+ ],
+ [
+ {
+ milliseconds: -4503599627370497_000,
+ microseconds: -4503599627370495_000000,
+ },
+ "case where floating point inaccuracy brings total below limit, negative"
+ ],
+];
+
+for (const [arg, descr] of cases) {
+ instance.subtract(arg); // should not throw
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-duration.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-duration.js
new file mode 100644
index 0000000000..99c1990791
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-duration.js
@@ -0,0 +1,17 @@
+// |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.subtract
+description: Duration arguments are supported.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainTime = new Temporal.PlainTime(15, 23, 30, 123, 456, 789);
+const duration = Temporal.Duration.from("PT16H");
+TemporalHelpers.assertPlainTime(plainTime.subtract(duration),
+ 23, 23, 30, 123, 456, 789);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-higher-units.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-higher-units.js
new file mode 100644
index 0000000000..43588561a9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-higher-units.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.subtract
+description: Higher units are ignored.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainTime = new Temporal.PlainTime(15, 23, 30, 123, 456, 789);
+const values = [
+ new Temporal.Duration(0, 0, 0, 1),
+ new Temporal.Duration(0, 0, 1),
+ new Temporal.Duration(0, 1),
+ new Temporal.Duration(1),
+ { days: 1 },
+ { weeks: 1 },
+ { months: 1 },
+ { years: 1 },
+ "P1D",
+ "P1W",
+ "P1M",
+ "P1Y",
+];
+for (const value of values) {
+ TemporalHelpers.assertPlainTime(plainTime.subtract(value),
+ 15, 23, 30, 123, 456, 789);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-invalid-property.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-invalid-property.js
new file mode 100644
index 0000000000..6fab0a1c32
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-invalid-property.js
@@ -0,0 +1,31 @@
+// |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.subtract
+description: temporalDurationLike object must contain at least one correctly spelled property
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(15, 30, 45, 987, 654, 321);
+
+assert.throws(
+ TypeError,
+ () => instance.subtract({}),
+ "Throws TypeError if no property is present"
+);
+
+assert.throws(
+ TypeError,
+ () => instance.subtract({ nonsense: true }),
+ "Throws TypeError if no recognized property is present"
+);
+
+assert.throws(
+ TypeError,
+ () => instance.subtract({ sign: 1 }),
+ "Sign property is not recognized"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-mixed-sign.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-mixed-sign.js
new file mode 100644
index 0000000000..234ad0fedb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-mixed-sign.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.subtract
+description: Positive and negative values in the temporalDurationLike argument are not acceptable
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(15, 30, 45, 987, 654, 321);
+
+assert.throws(
+ RangeError,
+ () => instance.subtract({ hours: 1, minutes: -30 }),
+ `mixed positive and negative values always throw`
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-not-object.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-not-object.js
new file mode 100644
index 0000000000..c77ced8f6b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-not-object.js
@@ -0,0 +1,22 @@
+// |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.subtract
+description: Passing a primitive other than string to subtract() throws
+features: [Symbol, Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(15, 30, 45, 987, 654, 321);
+assert.throws(TypeError, () => instance.subtract(undefined), "undefined");
+assert.throws(TypeError, () => instance.subtract(null), "null");
+assert.throws(TypeError, () => instance.subtract(true), "boolean");
+assert.throws(RangeError, () => instance.subtract(""), "empty string");
+assert.throws(TypeError, () => instance.subtract(Symbol()), "Symbol");
+assert.throws(TypeError, () => instance.subtract(7), "number");
+assert.throws(TypeError, () => instance.subtract(7n), "bigint");
+assert.throws(TypeError, () => instance.subtract([]), "array");
+assert.throws(TypeError, () => instance.subtract(() => {}), "function");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-object.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-object.js
new file mode 100644
index 0000000000..38290a6bf9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-object.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.subtract
+description: Plain object arguments are supported.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainTime = new Temporal.PlainTime(15, 23, 30, 123, 456, 789);
+TemporalHelpers.assertPlainTime(plainTime.subtract({ hours: 16 }),
+ 23, 23, 30, 123, 456, 789, "subtract 16 hours across midnight boundary");
+TemporalHelpers.assertPlainTime(plainTime.subtract({ minutes: 45 }),
+ 14, 38, 30, 123, 456, 789, "subtract 45 minutes");
+TemporalHelpers.assertPlainTime(plainTime.subtract({ seconds: 45 }),
+ 15, 22, 45, 123, 456, 789, "subtract 45 seconds");
+TemporalHelpers.assertPlainTime(plainTime.subtract({ milliseconds: 800 }),
+ 15, 23, 29, 323, 456, 789, "subtract 800 milliseconds");
+TemporalHelpers.assertPlainTime(plainTime.subtract({ microseconds: 800 }),
+ 15, 23, 30, 122, 656, 789, "subtract 800 microseconds");
+TemporalHelpers.assertPlainTime(plainTime.subtract({ nanoseconds: 800 }),
+ 15, 23, 30, 123, 455, 989, "subtract 800 nanoseconds");
+TemporalHelpers.assertPlainTime(Temporal.PlainTime.from("23:23:30.123456789").subtract({ hours: -16 }),
+ 15, 23, 30, 123, 456, 789, "subtract -16 hours across midnight boundary");
+TemporalHelpers.assertPlainTime(Temporal.PlainTime.from("14:38:30.123456789").subtract({ minutes: -45 }),
+ 15, 23, 30, 123, 456, 789, "subtract -45 minutes");
+TemporalHelpers.assertPlainTime(Temporal.PlainTime.from("15:22:45.123456789").subtract({ seconds: -45 }),
+ 15, 23, 30, 123, 456, 789, "subtract -45 seconds");
+TemporalHelpers.assertPlainTime(Temporal.PlainTime.from("15:23:29.323456789").subtract({ milliseconds: -800 }),
+ 15, 23, 30, 123, 456, 789, "subtract -800 milliseconds");
+TemporalHelpers.assertPlainTime(Temporal.PlainTime.from("15:23:30.122656789").subtract({ microseconds: -800 }),
+ 15, 23, 30, 123, 456, 789, "subtract -800 microseconds");
+TemporalHelpers.assertPlainTime(Temporal.PlainTime.from("15:23:30.123455989").subtract({ nanoseconds: -800 }),
+ 15, 23, 30, 123, 456, 789, "subtract -800 nanoseconds");
+TemporalHelpers.assertPlainTime(plainTime.subtract({ minute: 1, hours: 1 }),
+ 14, 23, 30, 123, 456, 789, "misspelled property is ignored");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-singular-properties.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-singular-properties.js
new file mode 100644
index 0000000000..6bb1a76f9f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-singular-properties.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.subtract
+description: Singular properties in the property bag are always ignored
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(15, 30, 45, 987, 654, 321);
+
+[
+ { year: 1 },
+ { month: 2 },
+ { week: 3 },
+ { day: 4 },
+ { hour: 5 },
+ { minute: 6 },
+ { second: 7 },
+ { millisecond: 8 },
+ { microsecond: 9 },
+ { nanosecond: 10 },
+].forEach((badObject) => {
+ assert.throws(TypeError, () => instance.subtract(badObject),
+ "Throw TypeError if temporalDurationLike is not valid");
+});
+
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-string-duration-too-large.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-string-duration-too-large.js
new file mode 100644
index 0000000000..4d26a245df
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-string-duration-too-large.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.subtract
+description: >
+ ParseTemporalDurationString throws a RangeError when the result is too large.
+features: [Temporal]
+---*/
+
+// Number string too long to be representable as a Number value.
+var ones = "1".repeat(1000);
+assert.sameValue(Number(ones), Infinity);
+
+var time = new Temporal.PlainTime();
+var str = "PT" + ones + "S";
+
+assert.throws(RangeError, () => time.subtract(str));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-string-fractional-units-rounding-mode.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-string-fractional-units-rounding-mode.js
new file mode 100644
index 0000000000..c0b706f506
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-string-fractional-units-rounding-mode.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.subtract
+description: Strings with fractional duration units are rounded with the correct rounding mode
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const midnight = new Temporal.PlainTime();
+
+TemporalHelpers.assertPlainTime(midnight.subtract("PT1.03125H"), 22, 58, 7, 500, 0, 0,
+ "positive fractional units rounded with correct rounding mode");
+TemporalHelpers.assertPlainTime(midnight.subtract("-PT1.03125H"), 1, 1, 52, 500, 0, 0,
+ "negative fractional units rounded with correct rounding mode");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-string-negative-fractional-units.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-string-negative-fractional-units.js
new file mode 100644
index 0000000000..d11506a0cc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-string-negative-fractional-units.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.subtract
+description: Strings with fractional duration units are treated with the correct sign
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime();
+
+const resultHours = instance.subtract("-PT24.567890123H");
+TemporalHelpers.assertPlainTime(resultHours, 0, 34, 4, 404, 442, 800, "negative fractional hours");
+
+const resultMinutes = instance.subtract("-PT1440.567890123M");
+TemporalHelpers.assertPlainTime(resultMinutes, 0, 0, 34, 73, 407, 380, "negative fractional minutes");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-string.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-string.js
new file mode 100644
index 0000000000..5e05b5bae6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/argument-string.js
@@ -0,0 +1,16 @@
+// |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.subtract
+description: A string is parsed into the correct object when passed as the argument
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = Temporal.PlainTime.from({ hour: 12, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321 });
+const result = instance.subtract("PT3M");
+TemporalHelpers.assertPlainTime(result, 12, 31, 56, 987, 654, 321);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/balance-negative-time-units.js
new file mode 100644
index 0000000000..f0712a3d66
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/balance-negative-time-units.js
@@ -0,0 +1,50 @@
+// |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.subtract
+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-addtime step 8:
+ 8. Return ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_).
+ sec-temporal.plaintime.prototype.subtract step 4:
+ 4. Let _result_ be ? AddTime(_temporalTime_.[[ISOHour]], _temporalTime_.[[ISOMinute]], _temporalTime_.[[ISOSecond]], _temporalTime_.[[ISOMillisecond]], _temporalTime_.[[ISOMicrosecond]], _temporalTime_.[[ISONanosecond]], −_duration_.[[Hours]], −_duration_.[[Minutes]], −_duration_.[[Seconds]], −_duration_.[[Milliseconds]], −_duration_.[[Microseconds]], −_duration_.[[Nanoseconds]]).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(1, 1, 1, 1, 1, 1);
+
+const result1 = time.subtract(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, 2));
+TemporalHelpers.assertPlainTime(result1, 1, 1, 1, 1, 0, 999, "nanoseconds balance");
+
+const result2 = time.subtract(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 2));
+TemporalHelpers.assertPlainTime(result2, 1, 1, 1, 0, 999, 1, "microseconds balance");
+
+const result3 = time.subtract(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 2));
+TemporalHelpers.assertPlainTime(result3, 1, 1, 0, 999, 1, 1, "milliseconds balance");
+
+const result4 = time.subtract(new Temporal.Duration(0, 0, 0, 0, 0, 0, 2));
+TemporalHelpers.assertPlainTime(result4, 1, 0, 59, 1, 1, 1, "seconds balance");
+
+const result5 = time.subtract(new Temporal.Duration(0, 0, 0, 0, 0, 2));
+TemporalHelpers.assertPlainTime(result5, 0, 59, 1, 1, 1, 1, "minutes balance");
+
+const result6 = time.subtract(new Temporal.Duration(0, 0, 0, 0, 2));
+TemporalHelpers.assertPlainTime(result6, 23, 1, 1, 1, 1, 1, "hours mod 24");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/branding.js
new file mode 100644
index 0000000000..c51391219d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/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.subtract
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const subtract = Temporal.PlainTime.prototype.subtract;
+
+assert.sameValue(typeof subtract, "function");
+
+const args = [new Temporal.Duration(0, 0, 0, 0, 5)];
+
+assert.throws(TypeError, () => subtract.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => subtract.apply(null, args), "null");
+assert.throws(TypeError, () => subtract.apply(true, args), "true");
+assert.throws(TypeError, () => subtract.apply("", args), "empty string");
+assert.throws(TypeError, () => subtract.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => subtract.apply(1, args), "1");
+assert.throws(TypeError, () => subtract.apply({}, args), "plain object");
+assert.throws(TypeError, () => subtract.apply(Temporal.PlainTime, args), "Temporal.PlainTime");
+assert.throws(TypeError, () => subtract.apply(Temporal.PlainTime.prototype, args), "Temporal.PlainTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/builtin.js
new file mode 100644
index 0000000000..ee2826cf6d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/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.subtract
+description: >
+ Tests that Temporal.PlainTime.prototype.subtract
+ 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.subtract),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainTime.prototype.subtract),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainTime.prototype.subtract),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainTime.prototype.subtract.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..27a8ae2ee7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/infinity-throws-rangeerror.js
@@ -0,0 +1,38 @@
+// |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.
+
+/*---
+description: Temporal.PlainTime.prototype.subtract throws a RangeError if any value in a property bag is Infinity
+esid: sec-temporal.plaintime.prototype.subtract
+features: [Temporal]
+---*/
+
+const overflows = ["constrain", "reject"];
+const fields = ["years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"];
+
+const instance = Temporal.PlainTime.from({ hour: 12, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321 });
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.subtract({ [field]: Infinity }, { overflow }));
+ });
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return Infinity;
+ }
+};
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => instance.subtract({ [field]: obj }, { overflow }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/length.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/length.js
new file mode 100644
index 0000000000..14a130a438
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/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.subtract
+description: Temporal.PlainTime.prototype.subtract.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.subtract, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/name.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/name.js
new file mode 100644
index 0000000000..3bc47f8d59
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/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.subtract
+description: Temporal.PlainTime.prototype.subtract.name is "subtract".
+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.subtract, "name", {
+ value: "subtract",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/negative-infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/negative-infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..e9f557b0ee
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/negative-infinity-throws-rangeerror.js
@@ -0,0 +1,38 @@
+// |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.
+
+/*---
+description: Temporal.PlainTime.prototype.subtract throws a RangeError if any value in a property bag is -Infinity
+esid: sec-temporal.plaintime.prototype.subtract
+features: [Temporal]
+---*/
+
+const overflows = ["constrain", "reject"];
+const fields = ["years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"];
+
+const instance = Temporal.PlainTime.from({ hour: 12, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321 });
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.subtract({ [field]: -Infinity }, { overflow }));
+ });
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return -Infinity;
+ }
+};
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => instance.subtract({ [field]: obj }, { overflow }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/non-integer-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/non-integer-throws-rangeerror.js
new file mode 100644
index 0000000000..fd5246180e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/non-integer-throws-rangeerror.js
@@ -0,0 +1,29 @@
+// |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.subtract
+description: A non-integer value for any recognized property in the property bag, throws a RangeError
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(15, 30, 45, 987, 654, 321);
+const fields = [
+ "years",
+ "months",
+ "weeks",
+ "days",
+ "hours",
+ "minutes",
+ "seconds",
+ "milliseconds",
+ "microseconds",
+ "nanoseconds",
+];
+fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.subtract({ [field]: 1.5 }));
+ assert.throws(RangeError, () => instance.subtract({ [field]: -1.5 }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/not-a-constructor.js
new file mode 100644
index 0000000000..c382bb0251
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/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.subtract
+description: >
+ Temporal.PlainTime.prototype.subtract 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.subtract();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainTime.prototype.subtract), false,
+ "isConstructor(Temporal.PlainTime.prototype.subtract)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/options-ignored.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/options-ignored.js
new file mode 100644
index 0000000000..49d07b34ef
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/options-ignored.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.subtract
+description: Options argument is ignored.
+includes: [temporalHelpers.js]
+features: [Symbol, Temporal]
+---*/
+
+const values = [
+ undefined,
+ null,
+ true,
+ "hello",
+ Symbol("foo"),
+ 1,
+ 1n,
+ {},
+ () => {},
+ { get overflow() { throw new Test262Error("should not get overflow") } },
+];
+
+const time = Temporal.PlainTime.from("15:23:30.123456789");
+for (const options of values) {
+ TemporalHelpers.assertPlainTime(time.subtract({ hours: 1 }, options),
+ 14, 23, 30, 123, 456, 789);
+}
+
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/order-of-operations.js
new file mode 100644
index 0000000000..c8c81fcc3b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/order-of-operations.js
@@ -0,0 +1,62 @@
+// |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.subtract
+description: Properties on an object passed to subtract() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+const expected = [
+ "get fields.days",
+ "get fields.days.valueOf",
+ "call fields.days.valueOf",
+ "get fields.hours",
+ "get fields.hours.valueOf",
+ "call fields.hours.valueOf",
+ "get fields.microseconds",
+ "get fields.microseconds.valueOf",
+ "call fields.microseconds.valueOf",
+ "get fields.milliseconds",
+ "get fields.milliseconds.valueOf",
+ "call fields.milliseconds.valueOf",
+ "get fields.minutes",
+ "get fields.minutes.valueOf",
+ "call fields.minutes.valueOf",
+ "get fields.months",
+ "get fields.months.valueOf",
+ "call fields.months.valueOf",
+ "get fields.nanoseconds",
+ "get fields.nanoseconds.valueOf",
+ "call fields.nanoseconds.valueOf",
+ "get fields.seconds",
+ "get fields.seconds.valueOf",
+ "call fields.seconds.valueOf",
+ "get fields.weeks",
+ "get fields.weeks.valueOf",
+ "call fields.weeks.valueOf",
+ "get fields.years",
+ "get fields.years.valueOf",
+ "call fields.years.valueOf",
+];
+const actual = [];
+const fields = TemporalHelpers.propertyBagObserver(actual, {
+ years: 1,
+ months: 1,
+ weeks: 1,
+ days: 1,
+ hours: 1,
+ minutes: 1,
+ seconds: 1,
+ milliseconds: 1,
+ microseconds: 1,
+ nanoseconds: 1,
+}, "fields");
+const result = instance.subtract(fields);
+TemporalHelpers.assertPlainTime(result, 11, 33, 55, 986, 653, 320);
+assert.compareArray(actual, expected, "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/precision-exact-mathematical-values-1.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/precision-exact-mathematical-values-1.js
new file mode 100644
index 0000000000..3b9836b044
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/precision-exact-mathematical-values-1.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.subtract
+description: >
+ Duration components are precise mathematical integers.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+let duration = Temporal.Duration.from({
+ microseconds: Number.MIN_SAFE_INTEGER,
+ nanoseconds: -1000,
+});
+
+let time = Temporal.PlainTime.from({
+ microsecond: 1,
+});
+
+let result = time.subtract(duration);
+
+TemporalHelpers.assertPlainTime(result, 23, 47, 34, 740, 993, 0);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/precision-exact-mathematical-values-2.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/precision-exact-mathematical-values-2.js
new file mode 100644
index 0000000000..c216bd6231
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/precision-exact-mathematical-values-2.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.subtract
+description: >
+ Duration components are precise mathematical integers.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+let duration = Temporal.Duration.from({
+ seconds: -Number.MAX_SAFE_INTEGER,
+ nanoseconds: -999_999_999,
+});
+
+let time = new Temporal.PlainTime(0, 0, 0, 0, 0, 0);
+
+let result = time.subtract(duration);
+
+TemporalHelpers.assertPlainTime(result, 7, 36, 31, 999, 999, 999);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/prop-desc.js
new file mode 100644
index 0000000000..7d4e360a6e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/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.subtract
+description: The "subtract" property of Temporal.PlainTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainTime.prototype.subtract,
+ "function",
+ "`typeof PlainTime.prototype.subtract` is `function`"
+);
+
+verifyProperty(Temporal.PlainTime.prototype, "subtract", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/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/subtract/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/subclassing-ignored.js
new file mode 100644
index 0000000000..e9c47a0666
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/subtract/subclassing-ignored.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.subtract
+description: Objects of a subclass are never created as return values for subtract()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.PlainTime,
+ [12, 34, 56, 987, 654, 321],
+ "subtract",
+ [{ nanoseconds: 1 }],
+ (result) => TemporalHelpers.assertPlainTime(result, 12, 34, 56, 987, 654, 320),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/basic.js
new file mode 100644
index 0000000000..e3cbd04d1e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/basic.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.tojson
+description: Basic behavior for toJSON
+features: [Temporal]
+---*/
+
+const tests = [
+ [new Temporal.PlainTime(5, 3, 1), "05:03:01"],
+ [new Temporal.PlainTime(15, 23), "15:23:00"],
+ [new Temporal.PlainTime(15, 23, 30), "15:23:30"],
+ [new Temporal.PlainTime(15, 23, 30, 123, 400), "15:23:30.1234"],
+];
+
+const options = new Proxy({}, {
+ get() { throw new Test262Error("should not get properties off argument") }
+});
+for (const [time, expected] of tests) {
+ assert.sameValue(time.toJSON(), expected, "toJSON without argument");
+ assert.sameValue(time.toJSON(options), expected, "toJSON with argument");
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/branding.js
new file mode 100644
index 0000000000..21624123db
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/branding.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.tojson
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toJSON = Temporal.PlainTime.prototype.toJSON;
+
+assert.sameValue(typeof toJSON, "function");
+
+assert.throws(TypeError, () => toJSON.call(undefined), "undefined");
+assert.throws(TypeError, () => toJSON.call(null), "null");
+assert.throws(TypeError, () => toJSON.call(true), "true");
+assert.throws(TypeError, () => toJSON.call(""), "empty string");
+assert.throws(TypeError, () => toJSON.call(Symbol()), "symbol");
+assert.throws(TypeError, () => toJSON.call(1), "1");
+assert.throws(TypeError, () => toJSON.call({}), "plain object");
+assert.throws(TypeError, () => toJSON.call(Temporal.PlainTime), "Temporal.PlainTime");
+assert.throws(TypeError, () => toJSON.call(Temporal.PlainTime.prototype), "Temporal.PlainTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/builtin.js
new file mode 100644
index 0000000000..c2a661a303
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/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.tojson
+description: >
+ Tests that Temporal.PlainTime.prototype.toJSON
+ 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.toJSON),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainTime.prototype.toJSON),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainTime.prototype.toJSON),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainTime.prototype.toJSON.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/length.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/length.js
new file mode 100644
index 0000000000..5c4ca090f3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/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.tojson
+description: Temporal.PlainTime.prototype.toJSON.length is 0
+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.toJSON, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/name.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/name.js
new file mode 100644
index 0000000000..60560473d5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/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.tojson
+description: Temporal.PlainTime.prototype.toJSON.name is "toJSON".
+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.toJSON, "name", {
+ value: "toJSON",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/not-a-constructor.js
new file mode 100644
index 0000000000..ef77906631
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/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.tojson
+description: >
+ Temporal.PlainTime.prototype.toJSON 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.toJSON();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainTime.prototype.toJSON), false,
+ "isConstructor(Temporal.PlainTime.prototype.toJSON)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/prop-desc.js
new file mode 100644
index 0000000000..56d7e40457
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/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.tojson
+description: The "toJSON" property of Temporal.PlainTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainTime.prototype.toJSON,
+ "function",
+ "`typeof PlainTime.prototype.toJSON` is `function`"
+);
+
+verifyProperty(Temporal.PlainTime.prototype, "toJSON", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toJSON/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/toLocaleString/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/branding.js
new file mode 100644
index 0000000000..2401d31297
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/branding.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.tolocalestring
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toLocaleString = Temporal.PlainTime.prototype.toLocaleString;
+
+assert.sameValue(typeof toLocaleString, "function");
+
+assert.throws(TypeError, () => toLocaleString.call(undefined), "undefined");
+assert.throws(TypeError, () => toLocaleString.call(null), "null");
+assert.throws(TypeError, () => toLocaleString.call(true), "true");
+assert.throws(TypeError, () => toLocaleString.call(""), "empty string");
+assert.throws(TypeError, () => toLocaleString.call(Symbol()), "symbol");
+assert.throws(TypeError, () => toLocaleString.call(1), "1");
+assert.throws(TypeError, () => toLocaleString.call({}), "plain object");
+assert.throws(TypeError, () => toLocaleString.call(Temporal.PlainTime), "Temporal.PlainTime");
+assert.throws(TypeError, () => toLocaleString.call(Temporal.PlainTime.prototype), "Temporal.PlainTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/builtin.js
new file mode 100644
index 0000000000..5cf924222c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/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.tolocalestring
+description: >
+ Tests that Temporal.PlainTime.prototype.toLocaleString
+ 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.toLocaleString),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainTime.prototype.toLocaleString),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainTime.prototype.toLocaleString),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainTime.prototype.toLocaleString.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/length.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/length.js
new file mode 100644
index 0000000000..2da8dbc59d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/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.tolocalestring
+description: Temporal.PlainTime.prototype.toLocaleString.length is 0
+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.toLocaleString, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/name.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/name.js
new file mode 100644
index 0000000000..c736160c0c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/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.tolocalestring
+description: Temporal.PlainTime.prototype.toLocaleString.name is "toLocaleString".
+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.toLocaleString, "name", {
+ value: "toLocaleString",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/not-a-constructor.js
new file mode 100644
index 0000000000..0e7938a0fc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/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.tolocalestring
+description: >
+ Temporal.PlainTime.prototype.toLocaleString 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.toLocaleString();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainTime.prototype.toLocaleString), false,
+ "isConstructor(Temporal.PlainTime.prototype.toLocaleString)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/prop-desc.js
new file mode 100644
index 0000000000..8ed43ab577
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/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.tolocalestring
+description: The "toLocaleString" property of Temporal.PlainTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainTime.prototype.toLocaleString,
+ "function",
+ "`typeof PlainTime.prototype.toLocaleString` is `function`"
+);
+
+verifyProperty(Temporal.PlainTime.prototype, "toLocaleString", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/return-string.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/return-string.js
new file mode 100644
index 0000000000..59c032d396
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/return-string.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Kate Miháliková. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.tolocalestring
+description: >
+ toLocaleString return a string.
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+assert.sameValue(typeof time.toLocaleString("en", { timeStyle: "short" }), "string");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toLocaleString/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/toPlainDateTime/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..aaf98aa359
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-builtin-calendar-no-array-iteration.js
@@ -0,0 +1,24 @@
+// |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.toplaindatetime
+description: >
+ Calling the method with a property bag argument with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+const arg = { year: 2000, month: 5, day: 2, calendar: "iso8601" };
+instance.toPlainDateTime(arg);
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-calendar-datefromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..fb8706493b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-calendar-datefromfields-called-with-null-prototype-fields.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.toplaindatetime
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+const arg = { year: 2000, month: 5, day: 2, calendar };
+instance.toPlainDateTime(arg);
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..ba733a7bd9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-constructor-in-calendar-fields.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.toplaindatetime
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+assert.throws(RangeError, () => instance.toPlainDateTime(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-duplicate-calendar-fields.js
new file mode 100644
index 0000000000..671a32ba83
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-duplicate-calendar-fields.js
@@ -0,0 +1,19 @@
+// |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.toplaindatetime
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['day'], ['month'], ['monthCode'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar };
+ const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+ assert.throws(RangeError, () => instance.toPlainDateTime(arg));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-leap-second.js
new file mode 100644
index 0000000000..df8483e7b9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-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.toplaindatetime
+description: Leap second is a valid ISO string for PlainDate
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+let arg = "2016-12-31T23:59:60";
+const result1 = instance.toPlainDateTime(arg);
+TemporalHelpers.assertPlainDateTime(
+ result1,
+ 2016, 12, "M12", 31, 12, 34, 56, 987, 654, 321,
+ "leap second is a valid ISO string for PlainDate"
+);
+
+arg = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+const result2 = instance.toPlainDateTime(arg);
+TemporalHelpers.assertPlainDateTime(
+ result2,
+ 2016, 12, "M12", 31, 12, 34, 56, 987, 654, 321,
+ "second: 60 is ignored in property bag for PlainDate"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-number.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-number.js
new file mode 100644
index 0000000000..aee435340f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/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.toplaindatetime
+description: A number cannot be used in place of a Temporal.PlainDate
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+const numbers = [
+ 1,
+ 19761118,
+ -19761118,
+ 1234567890,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => instance.toPlainDateTime(arg),
+ 'Numbers cannot be used in place of an ISO string for PlainDate'
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-plaindatetime.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-plaindatetime.js
new file mode 100644
index 0000000000..2d5bdfb37e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-plaindatetime.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.toplaindatetime
+description: Fast path for converting Temporal.PlainDateTime to Temporal.PlainDate by reading internal slots
+info: |
+ sec-temporal.plaintime.prototype.toplaindatetime step 3:
+ 3. Set _temporalDate_ to ? ToTemporalDate(_temporalDate_).
+ sec-temporal-totemporaldate step 2.b:
+ b. If _item_ has an [[InitializedTemporalDateTime]] internal slot, then
+ i. Return ! CreateTemporalDate(_item_.[[ISOYear]], _item_.[[ISOMonth]], _item_.[[ISODay]], _item_.[[Calendar]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkPlainDateTimeConversionFastPath((datetime) => {
+ const time = new Temporal.PlainTime(6, 54, 32, 123, 456, 789);
+ const result = time.toPlainDateTime(datetime);
+ TemporalHelpers.assertPlainDateTime(result, 2000, 5, "M05", 2, 6, 54, 32, 123, 456, 789);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-propertybag-calendar-case-insensitive.js
new file mode 100644
index 0000000000..34dd11bf93
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-propertybag-calendar-case-insensitive.js
@@ -0,0 +1,20 @@
+// |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.toplaindatetime
+description: The calendar name is case-insensitive
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+const calendar = "IsO8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.toPlainDateTime(arg);
+TemporalHelpers.assertPlainDateTime(result, 1976, 11, "M11", 18, 12, 34, 56, 987, 654, 321, "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-propertybag-calendar-leap-second.js
new file mode 100644
index 0000000000..9beb64271e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-propertybag-calendar-leap-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.toplaindatetime
+description: Leap second is a valid ISO string for a calendar in a property bag
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+const calendar = "2016-12-31T23:59:60";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.toPlainDateTime(arg);
+TemporalHelpers.assertPlainDateTime(
+ result,
+ 1976, 11, "M11", 18, 12, 34, 56, 987, 654, 321,
+ "leap second is a valid ISO string for calendar"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-propertybag-calendar-number.js
new file mode 100644
index 0000000000..407773adb4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-propertybag-calendar-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.toplaindatetime
+description: A number as calendar in a property bag is not accepted
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(1976, 11, 18);
+
+const numbers = [
+ 1,
+ 19970327,
+ -19970327,
+ 1234567890,
+];
+for (const calendar of numbers) {
+ const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+ assert.throws(
+ TypeError,
+ () => instance.toPlainDateTime(arg),
+ "Numbers cannot be used as a calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-propertybag-calendar-string.js
new file mode 100644
index 0000000000..6edf8a1789
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-propertybag-calendar-string.js
@@ -0,0 +1,20 @@
+// |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.toplaindatetime
+description: A calendar ID is valid input for Calendar
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+const calendar = "iso8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.toPlainDateTime(arg);
+TemporalHelpers.assertPlainDateTime(result, 1976, 11, "M11", 18, 12, 34, 56, 987, 654, 321, `Calendar created from string "${calendar}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-propertybag-calendar-wrong-type.js
new file mode 100644
index 0000000000..7924805b00
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-propertybag-calendar-wrong-type.js
@@ -0,0 +1,46 @@
+// |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.toplaindatetime
+description: >
+ Appropriate error thrown when a calendar property from a property bag cannot
+ be converted to a calendar object or string
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [calendar, description] of primitiveTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ typeof calendar === 'string' ? RangeError : TypeError,
+ () => instance.toPlainDateTime(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+ [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields()
+];
+
+for (const [calendar, description] of typeErrorTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(TypeError, () => instance.toPlainDateTime(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/toPlainDateTime/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-propertybag-calendar-year-zero.js
new file mode 100644
index 0000000000..f9ba398d5f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-propertybag-calendar-year-zero.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.toplaindatetime
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T17:45",
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+01:00",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.toPlainDateTime(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-proto-in-calendar-fields.js
new file mode 100644
index 0000000000..fd70a10ff5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-proto-in-calendar-fields.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.toplaindatetime
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+assert.throws(RangeError, () => instance.toPlainDateTime(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..b7ab3b08c0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-calendar-annotation.js
@@ -0,0 +1,34 @@
+// |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.toplaindatetime
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2000-05-02[u-ca=iso8601]", "without time or time zone"],
+ ["2000-05-02[UTC][u-ca=iso8601]", "with time zone and no time"],
+ ["2000-05-02T15:23[u-ca=iso8601]", "without time zone"],
+ ["2000-05-02T15:23[UTC][u-ca=iso8601]", "with time zone"],
+ ["2000-05-02T15:23[!u-ca=iso8601]", "with ! and no time zone"],
+ ["2000-05-02T15:23[UTC][!u-ca=iso8601]", "with ! and time zone"],
+ ["2000-05-02T15:23[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.toPlainDateTime(arg);
+
+ TemporalHelpers.assertPlainDateTime(
+ result,
+ 2000, 5, "M05", 2, 12, 34, 56, 987, 654, 321,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..88e313cf0f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-critical-unknown-annotation.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.toplaindatetime
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[!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.toPlainDateTime(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..6e64c39c15
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-date-with-utc-offset.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.toplaindatetime
+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 = [
+ "2000-05-02T00+00:00",
+ "2000-05-02T00+00:00[UTC]",
+ "2000-05-02T00+00:00[!UTC]",
+ "2000-05-02T00-02:30[America/St_Johns]",
+];
+
+for (const arg of validStrings) {
+ const result = instance.toPlainDateTime(arg);
+
+ TemporalHelpers.assertPlainDateTime(
+ result,
+ 2000, 5, "M05", 2, 12, 34, 56, 987, 654, 321,
+ `"${arg}" is a valid UTC offset with time for PlainDate`
+ );
+}
+
+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.toPlainDateTime(arg),
+ `"${arg}" UTC offset without time is not valid for PlainDate`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-invalid.js
new file mode 100644
index 0000000000..519b9ff9be
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-invalid.js
@@ -0,0 +1,64 @@
+// |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.toplaindatetime
+description: >
+ RangeError thrown if an invalid ISO string (or syntactically valid ISO string
+ that is not supported) is used as a PlainDate
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ // invalid ISO strings:
+ "",
+ "invalid iso8601",
+ "2020-01-00",
+ "2020-01-32",
+ "2020-02-30",
+ "2021-02-29",
+ "2020-00-01",
+ "2020-13-01",
+ "2020-01-01T",
+ "2020-01-01T25:00:00",
+ "2020-01-01T01:60:00",
+ "2020-01-01T01:60:61",
+ "2020-01-01junk",
+ "2020-01-01T00:00:00junk",
+ "2020-01-01T00:00:00+00:00junk",
+ "2020-01-01T00:00:00+00:00[UTC]junk",
+ "2020-01-01T00:00:00+00:00[UTC][u-ca=iso8601]junk",
+ "02020-01-01",
+ "2020-001-01",
+ "2020-01-001",
+ "2020-01-01T001",
+ "2020-01-01T01:001",
+ "2020-01-01T01:01:001",
+ // valid, but forms not supported in Temporal:
+ "2020-W01-1",
+ "2020-001",
+ "+0002020-01-01",
+ // valid, but this calendar must not exist:
+ "2020-01-01[u-ca=notexist]",
+ // may be valid in other contexts, but insufficient information for PlainDate:
+ "2020-01",
+ "+002020-01",
+ "01-01",
+ "2020-W01",
+ "P1Y",
+ "-P12Y",
+ // valid, but outside the supported range:
+ "-999999-01-01",
+ "+999999-01-01",
+];
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => instance.toPlainDateTime(arg),
+ `"${arg}" should not be a valid ISO string for a PlainDate`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..2208411476
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/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.toplaindatetime
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[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.toPlainDateTime(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/toPlainDateTime/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..4f47d2457f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-multiple-time-zone.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.toplaindatetime
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[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.toPlainDateTime(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-time-separators.js
new file mode 100644
index 0000000000..8325711849
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-time-separators.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.toplaindatetime
+description: Time separator in string argument can vary
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2000-05-02T15:23", "uppercase T"],
+ ["2000-05-02t15:23", "lowercase T"],
+ ["2000-05-02 15:23", "space between date and time"],
+];
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.toPlainDateTime(arg);
+
+ TemporalHelpers.assertPlainDateTime(
+ result,
+ 2000, 5, "M05", 2, 12, 34, 56, 987, 654, 321,
+ `variant time separators (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..9af036f12c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-time-zone-annotation.js
@@ -0,0 +1,39 @@
+// |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.toplaindatetime
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2000-05-02[Asia/Kolkata]", "named, with no time"],
+ ["2000-05-02[!Europe/Vienna]", "named, with ! and no time"],
+ ["2000-05-02[+00:00]", "numeric, with no time"],
+ ["2000-05-02[!-02:30]", "numeric, with ! and no time"],
+ ["2000-05-02T15:23[America/Sao_Paulo]", "named, with no offset"],
+ ["2000-05-02T15:23[!Asia/Tokyo]", "named, with ! and no offset"],
+ ["2000-05-02T15:23[-02:30]", "numeric, with no offset"],
+ ["2000-05-02T15:23[!+00:00]", "numeric, with ! and no offset"],
+ ["2000-05-02T15:23+00:00[America/New_York]", "named, with offset"],
+ ["2000-05-02T15:23+00:00[!UTC]", "named, with offset and !"],
+ ["2000-05-02T15:23+00:00[+01:00]", "numeric, with offset"],
+ ["2000-05-02T15:23+00:00[!-08:00]", "numeric, with offset and !"],
+];
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.toPlainDateTime(arg);
+
+ TemporalHelpers.assertPlainDateTime(
+ result,
+ 2000, 5, "M05", 2, 12, 34, 56, 987, 654, 321,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..0fb9958f8a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-unknown-annotation.js
@@ -0,0 +1,33 @@
+// |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.toplaindatetime
+description: Various forms of unknown annotation
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2000-05-02[foo=bar]", "without time"],
+ ["2000-05-02T15:23[foo=bar]", "alone"],
+ ["2000-05-02T15:23[UTC][foo=bar]", "with time zone"],
+ ["2000-05-02T15:23[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["2000-05-02T15:23[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["2000-05-02T15:23[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.toPlainDateTime(arg);
+
+ TemporalHelpers.assertPlainDateTime(
+ result,
+ 2000, 5, "M05", 2, 12, 34, 56, 987, 654, 321,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..4e47c1b5cd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-string-with-utc-designator.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.toplaindatetime
+description: RangeError thrown if a string with UTC designator is used as a PlainDate
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "2019-10-01T09:00:00Z",
+ "2019-10-01T09:00:00Z[UTC]",
+];
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.toPlainDateTime(arg),
+ "String with UTC designator should not be valid as a PlainDate"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-wrong-type.js
new file mode 100644
index 0000000000..95387bf637
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/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.toplaindatetime
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainDate
+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.toPlainDateTime(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.PlainDate, "Temporal.PlainDate, object"],
+ [Temporal.PlainDate.prototype, "Temporal.PlainDate.prototype, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.toPlainDateTime(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/toPlainDateTime/argument-zoneddatetime-convert.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-zoneddatetime-convert.js
new file mode 100644
index 0000000000..39f6296bda
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-zoneddatetime-convert.js
@@ -0,0 +1,22 @@
+// |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.toplaindatetime
+description: An exception from TimeZone#getOffsetNanosecondsFor() is propagated.
+features: [Temporal]
+---*/
+
+class TZ extends Temporal.TimeZone {
+ constructor() { super("UTC") }
+ getOffsetNanosecondsFor() { throw new Test262Error() }
+}
+
+const tz = new TZ();
+const arg = new Temporal.ZonedDateTime(0n, tz);
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+assert.throws(Test262Error, () => instance.toPlainDateTime(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-zoneddatetime-slots.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-zoneddatetime-slots.js
new file mode 100644
index 0000000000..a7ea2c308e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-zoneddatetime-slots.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.toplaindatetime
+description: Getters are not called when converting a ZonedDateTime to a PlainDate.
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.ZonedDateTime.prototype);
+const getters = ["year", "month", "monthCode", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond", "calendar"];
+
+for (const property of getters) {
+ Object.defineProperty(Temporal.ZonedDateTime.prototype, property, {
+ get() {
+ actual.push(`get ${property}`);
+ const value = prototypeDescrs[property].get.call(this);
+ return {
+ toString() {
+ actual.push(`toString ${property}`);
+ return value.toString();
+ },
+ valueOf() {
+ actual.push(`valueOf ${property}`);
+ return value;
+ },
+ };
+ },
+ });
+}
+
+const arg = new Temporal.ZonedDateTime(0n, "UTC");
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+instance.toPlainDateTime(arg);
+assert.compareArray(actual, []);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..996ac97964
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/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.toplaindatetime
+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.toPlainDateTime(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..5757a1d253
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/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.toplaindatetime
+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.toPlainDateTime(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/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..92bff3ca6d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/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.toplaindatetime
+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.toPlainDateTime(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..b1b91f30fa
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/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.toplaindatetime
+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.toPlainDateTime(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/basic.js
new file mode 100644
index 0000000000..995467100a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/basic.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.toplaindatetime
+description: Basic tests for toPlainDateTime().
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainTime = Temporal.PlainTime.from("11:30:23.123456789");
+
+const plainDate = plainTime.toPlainDateTime(Temporal.PlainDate.from("1976-11-18"));
+TemporalHelpers.assertPlainDateTime(plainDate, 1976, 11, "M11", 18, 11, 30, 23, 123, 456, 789, "PlainDate");
+
+const optionBag = plainTime.toPlainDateTime({ year: 1976, month: 11, day: 18 });
+TemporalHelpers.assertPlainDateTime(optionBag, 1976, 11, "M11", 18, 11, 30, 23, 123, 456, 789, "option bag");
+
+const string = plainTime.toPlainDateTime("1976-11-18");
+TemporalHelpers.assertPlainDateTime(string, 1976, 11, "M11", 18, 11, 30, 23, 123, 456, 789, "string");
+
+assert.throws(TypeError, () => plainTime.toPlainDateTime({ year: 1976 }), "missing properties");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/branding.js
new file mode 100644
index 0000000000..724c37070f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/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.toplaindatetime
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toPlainDateTime = Temporal.PlainTime.prototype.toPlainDateTime;
+
+assert.sameValue(typeof toPlainDateTime, "function");
+
+const args = [new Temporal.PlainDate(2022, 6, 22)];
+
+assert.throws(TypeError, () => toPlainDateTime.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => toPlainDateTime.apply(null, args), "null");
+assert.throws(TypeError, () => toPlainDateTime.apply(true, args), "true");
+assert.throws(TypeError, () => toPlainDateTime.apply("", args), "empty string");
+assert.throws(TypeError, () => toPlainDateTime.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => toPlainDateTime.apply(1, args), "1");
+assert.throws(TypeError, () => toPlainDateTime.apply({}, args), "plain object");
+assert.throws(TypeError, () => toPlainDateTime.apply(Temporal.PlainTime, args), "Temporal.PlainTime");
+assert.throws(TypeError, () => toPlainDateTime.apply(Temporal.PlainTime.prototype, args), "Temporal.PlainTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/builtin.js
new file mode 100644
index 0000000000..9242d0b4a0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/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.toplaindatetime
+description: >
+ Tests that Temporal.PlainTime.prototype.toPlainDateTime
+ 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.toPlainDateTime),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainTime.prototype.toPlainDateTime),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainTime.prototype.toPlainDateTime),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainTime.prototype.toPlainDateTime.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/calendar-datefromfields-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/calendar-datefromfields-called-with-options-undefined.js
new file mode 100644
index 0000000000..cbca1c086f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/calendar-datefromfields-called-with-options-undefined.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.toplaindatetime
+description: >
+ Calendar.dateFromFields method is called with undefined as the options value
+ when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321, calendar);
+instance.toPlainDateTime({ year: 2000, month: 5, day: 3, calendar });
+assert.sameValue(calendar.dateFromFieldsCallCount, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/calendar-fields-iterable.js
new file mode 100644
index 0000000000..93ae2c6dc0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/calendar-fields-iterable.js
@@ -0,0 +1,34 @@
+// |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.toplaindatetime
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plaintime.prototype.toplaindatetime step 3:
+ 3. Set _temporalDate_ to ? ToTemporalDate(_temporalDate_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const time = new Temporal.PlainTime(13, 3);
+const calendar = TemporalHelpers.calendarFieldsIterable();
+time.toPlainDateTime({ year: 2000, month: 5, day: 3, calendar });
+
+assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/calendar-temporal-object.js
new file mode 100644
index 0000000000..9ccbd4fc03
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/calendar-temporal-object.js
@@ -0,0 +1,30 @@
+// |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.toplaindatetime
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plaintime.prototype.toplaindatetime step 3:
+ 3. Set _temporalDate_ to ? ToTemporalDate(_temporalDate_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject, calendar) => {
+ const time = new Temporal.PlainTime(13, 3);
+ const result = time.toPlainDateTime({ year: 2000, month: 5, day: 3, calendar: temporalObject });
+ assert.sameValue(result.getCalendar(), calendar, "Temporal object coerced to calendar");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..84a2e311a0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/infinity-throws-rangeerror.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.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plaintime.prototype.toplaindatetime
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(15);
+const base = { year: 2000, month: 5, day: 2 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.toPlainDateTime({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.toPlainDateTime({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/length.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/length.js
new file mode 100644
index 0000000000..a10aff33cb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/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.toplaindatetime
+description: Temporal.PlainTime.prototype.toPlainDateTime.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.toPlainDateTime, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/limits.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/limits.js
new file mode 100644
index 0000000000..d2561e05c6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/limits.js
@@ -0,0 +1,42 @@
+// |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.toplaindatetime
+description: Checking limits of representable PlainDateTime
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const midnight = new Temporal.PlainTime(0, 0);
+const firstNs = new Temporal.PlainTime(0, 0, 0, 0, 0, 1);
+const lastNs = new Temporal.PlainTime(23, 59, 59, 999, 999, 999);
+const min = new Temporal.PlainDate(-271821, 4, 19);
+const max = new Temporal.PlainDate(275760, 9, 13);
+
+assert.throws(
+ RangeError,
+ () => midnight.toPlainDateTime(min),
+ "Cannot go below representable limit"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ midnight.toPlainDateTime(max),
+ 275760, 9, "M09", 13, 0, 0, 0, 0, 0, 0,
+ "Midnight of maximum representable PlainDate"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ firstNs.toPlainDateTime(min),
+ -271821, 4, "M04", 19, 0, 0, 0, 0, 0, 1,
+ "Computing the minimum (earliest) representable PlainDateTime"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ lastNs.toPlainDateTime(max),
+ 275760, 9, "M09", 13, 23, 59, 59, 999, 999, 999,
+ "Computing the maximum (latest) representable PlainDateTime"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/name.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/name.js
new file mode 100644
index 0000000000..830ca10999
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/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.toplaindatetime
+description: Temporal.PlainTime.prototype.toPlainDateTime.name is "toPlainDateTime".
+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.toPlainDateTime, "name", {
+ value: "toPlainDateTime",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/not-a-constructor.js
new file mode 100644
index 0000000000..38b8e598ef
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/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.toplaindatetime
+description: >
+ Temporal.PlainTime.prototype.toPlainDateTime 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.toPlainDateTime();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainTime.prototype.toPlainDateTime), false,
+ "isConstructor(Temporal.PlainTime.prototype.toPlainDateTime)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/prop-desc.js
new file mode 100644
index 0000000000..ecc3c8e55a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/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.toplaindatetime
+description: The "toPlainDateTime" property of Temporal.PlainTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainTime.prototype.toPlainDateTime,
+ "function",
+ "`typeof PlainTime.prototype.toPlainDateTime` is `function`"
+);
+
+verifyProperty(Temporal.PlainTime.prototype, "toPlainDateTime", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/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/toPlainDateTime/year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/year-zero.js
new file mode 100644
index 0000000000..090c19d1eb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/year-zero.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.toplaindatetime
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T00:45",
+ "-000000-10-31T00:45+01:00",
+ "-000000-10-31T00:45+00:00[UTC]",
+];
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.toPlainDateTime(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/basic.js
new file mode 100644
index 0000000000..d43372e50f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/basic.js
@@ -0,0 +1,18 @@
+// |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.tostring
+description: Basic tests for toString()
+features: [Temporal]
+---*/
+
+assert.sameValue(new Temporal.PlainTime(15, 23).toString(), "15:23:00");
+assert.sameValue(new Temporal.PlainTime(15, 23, 30).toString(), "15:23:30");
+assert.sameValue(new Temporal.PlainTime(15, 23, 30, 123).toString(), "15:23:30.123");
+assert.sameValue(new Temporal.PlainTime(15, 23, 30, 123, 400).toString(), "15:23:30.1234");
+assert.sameValue(new Temporal.PlainTime(15, 23, 30, 123, 456).toString(), "15:23:30.123456");
+assert.sameValue(new Temporal.PlainTime(15, 23, 30, 123, 456, 789).toString(), "15:23:30.123456789");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/branding.js
new file mode 100644
index 0000000000..cd2d2e6171
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/branding.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.tostring
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toString = Temporal.PlainTime.prototype.toString;
+
+assert.sameValue(typeof toString, "function");
+
+assert.throws(TypeError, () => toString.call(undefined), "undefined");
+assert.throws(TypeError, () => toString.call(null), "null");
+assert.throws(TypeError, () => toString.call(true), "true");
+assert.throws(TypeError, () => toString.call(""), "empty string");
+assert.throws(TypeError, () => toString.call(Symbol()), "symbol");
+assert.throws(TypeError, () => toString.call(1), "1");
+assert.throws(TypeError, () => toString.call({}), "plain object");
+assert.throws(TypeError, () => toString.call(Temporal.PlainTime), "Temporal.PlainTime");
+assert.throws(TypeError, () => toString.call(Temporal.PlainTime.prototype), "Temporal.PlainTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/builtin.js
new file mode 100644
index 0000000000..c540a97479
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/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.tostring
+description: >
+ Tests that Temporal.PlainTime.prototype.toString
+ 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.toString),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainTime.prototype.toString),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainTime.prototype.toString),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainTime.prototype.toString.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-auto.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-auto.js
new file mode 100644
index 0000000000..4468ac3f39
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-auto.js
@@ -0,0 +1,23 @@
+// |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.tostring
+description: auto value for fractionalSecondDigits option
+features: [Temporal]
+---*/
+
+const tests = [
+ [new Temporal.PlainTime(5, 3, 1), "05:03:01"],
+ [new Temporal.PlainTime(15, 23), "15:23:00"],
+ [new Temporal.PlainTime(15, 23, 30), "15:23:30"],
+ [new Temporal.PlainTime(15, 23, 30, 123, 400), "15:23:30.1234"],
+];
+
+for (const [time, expected] of tests) {
+ assert.sameValue(time.toString(), expected, "default is to emit seconds and drop trailing zeroes");
+ assert.sameValue(time.toString({ fractionalSecondDigits: "auto" }), expected, "auto is the default");
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-invalid-string.js
new file mode 100644
index 0000000000..9f6d4b7454
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-invalid-string.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.tostring
+description: RangeError thrown when fractionalSecondDigits option not one of the allowed string values
+info: |
+ sec-getstringornumberoption step 4:
+ 4. If _stringValues_ is not *undefined* and _stringValues_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-tosecondsstringprecision step 9:
+ 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
+ sec-temporal.plaintime.prototype.tostring step 4:
+ 4. Let _precision_ be ? ToSecondsStringPrecision(_options_).
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 987, 650, 0);
+
+for (const fractionalSecondDigits of ["other string", "AUTO", "not-auto", "autos", "auto\0"]) {
+ assert.throws(RangeError, () => time.toString({ fractionalSecondDigits }),
+ `"${fractionalSecondDigits}" is not a valid value for fractionalSecondDigits`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-nan.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-nan.js
new file mode 100644
index 0000000000..ec1a8a2903
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-nan.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.tostring
+description: RangeError thrown when fractionalSecondDigits option is NaN
+info: |
+ sec-getoption step 8.b:
+ b. If _value_ is *NaN*, throw a *RangeError* exception.
+ sec-getstringornumberoption step 2:
+ 2. Let _value_ be ? GetOption(_options_, _property_, « Number, String », *undefined*, _fallback_).
+ sec-temporal-tosecondsstringprecision step 9:
+ 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
+ sec-temporal.plaintime.prototype.tostring step 4:
+ 4. Let _precision_ be ? ToSecondsStringPrecision(_options_).
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 987, 650, 0);
+assert.throws(RangeError, () => time.toString({ fractionalSecondDigits: NaN }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-non-integer.js
new file mode 100644
index 0000000000..2a0eefadbf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-non-integer.js
@@ -0,0 +1,32 @@
+// |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.tostring
+description: Rounding for fractionalSecondDigits option
+info: |
+ sec-getstringornumberoption step 3.b:
+ b. Return floor(ℝ(_value_)).
+ sec-temporal-tosecondsstringprecision step 9:
+ 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
+ sec-temporal.plaintime.prototype.tostring step 4:
+ 4. Let _precision_ be ? ToSecondsStringPrecision(_options_).
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 987, 650, 0);
+
+let string = time.toString({ fractionalSecondDigits: 2.5 });
+assert.sameValue(string, "12:34:56.98", "fractionalSecondDigits 2.5 floors to 2");
+
+string = time.toString({ fractionalSecondDigits: 9.7 });
+assert.sameValue(string, "12:34:56.987650000", "fractionalSecondDigits 9.7 floors to 9 and is not out of range");
+
+assert.throws(
+ RangeError,
+ () => time.toString({ fractionalSecondDigits: -0.6 }),
+ "fractionalSecondDigits -0.6 floors to -1 and is out of range"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-number.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-number.js
new file mode 100644
index 0000000000..068e608617
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-number.js
@@ -0,0 +1,39 @@
+// |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.tostring
+description: Number for fractionalSecondDigits option
+features: [Temporal]
+---*/
+
+const fewSeconds = new Temporal.PlainTime(5, 3, 1);
+const zeroSeconds = new Temporal.PlainTime(15, 23);
+const wholeSeconds = new Temporal.PlainTime(15, 23, 30);
+const subSeconds = new Temporal.PlainTime(15, 23, 30, 123, 400);
+
+assert.sameValue(fewSeconds.toString({ fractionalSecondDigits: 0 }), "05:03:01",
+ "pads parts with 0");
+assert.sameValue(subSeconds.toString({ fractionalSecondDigits: 0 }), "15:23:30",
+ "truncates 4 decimal places to 0");
+assert.sameValue(zeroSeconds.toString({ fractionalSecondDigits: 2 }), "15:23:00.00",
+ "pads zero seconds to 2 decimal places");
+assert.sameValue(wholeSeconds.toString({ fractionalSecondDigits: 2 }), "15:23:30.00",
+ "pads whole seconds to 2 decimal places");
+assert.sameValue(subSeconds.toString({ fractionalSecondDigits: 2 }), "15:23:30.12",
+ "truncates 4 decimal places to 2");
+assert.sameValue(subSeconds.toString({ fractionalSecondDigits: 3 }), "15:23:30.123",
+ "truncates 4 decimal places to 3");
+assert.sameValue(subSeconds.toString({ fractionalSecondDigits: 6 }), "15:23:30.123400",
+ "pads 4 decimal places to 6");
+assert.sameValue(zeroSeconds.toString({ fractionalSecondDigits: 7 }), "15:23:00.0000000",
+ "pads zero seconds to 7 decimal places");
+assert.sameValue(wholeSeconds.toString({ fractionalSecondDigits: 7 }), "15:23:30.0000000",
+ "pads whole seconds to 7 decimal places");
+assert.sameValue(subSeconds.toString({ fractionalSecondDigits: 7 }), "15:23:30.1234000",
+ "pads 4 decimal places to 7");
+assert.sameValue(subSeconds.toString({ fractionalSecondDigits: 9 }), "15:23:30.123400000",
+ "pads 4 decimal places to 9");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-out-of-range.js
new file mode 100644
index 0000000000..ca1a79ee64
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-out-of-range.js
@@ -0,0 +1,29 @@
+// |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.tostring
+description: RangeError thrown when fractionalSecondDigits option out of range
+info: |
+ sec-getstringornumberoption step 3.a:
+ a. If _value_ < _minimum_ or _value_ > _maximum_, throw a *RangeError* exception.
+ sec-temporal-tosecondsstringprecision step 9:
+ 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
+ sec-temporal.plaintime.prototype.tostring step 4:
+ 4. Let _precision_ be ? ToSecondsStringPrecision(_options_).
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 987, 650, 0);
+
+assert.throws(RangeError, () => time.toString({ fractionalSecondDigits: -Infinity }),
+ "−∞ is out of range for fractionalSecondDigits");
+assert.throws(RangeError, () => time.toString({ fractionalSecondDigits: -1 }),
+ "−1 is out of range for fractionalSecondDigits");
+assert.throws(RangeError, () => time.toString({ fractionalSecondDigits: 10 }),
+ "10 is out of range for fractionalSecondDigits");
+assert.throws(RangeError, () => time.toString({ fractionalSecondDigits: Infinity }),
+ "∞ is out of range for fractionalSecondDigits");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-undefined.js
new file mode 100644
index 0000000000..c7b073dfac
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-undefined.js
@@ -0,0 +1,38 @@
+// |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.tostring
+description: Fallback value for fractionalSecondDigits option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-getstringornumberoption step 2:
+ 2. Let _value_ be ? GetOption(_options_, _property_, « Number, String », *undefined*, _fallback_).
+ sec-temporal-tosecondsstringprecision step 9:
+ 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
+ sec-temporal.plaintime.prototype.tostring step 4:
+ 4. Let _precision_ be ? ToSecondsStringPrecision(_options_).
+features: [Temporal]
+---*/
+
+const tests = [
+ [new Temporal.PlainTime(5, 3, 1), "05:03:01"],
+ [new Temporal.PlainTime(15, 23), "15:23:00"],
+ [new Temporal.PlainTime(15, 23, 30), "15:23:30"],
+ [new Temporal.PlainTime(15, 23, 30, 123, 400), "15:23:30.1234"],
+];
+
+for (const [time, expected] of tests) {
+ const explicit = time.toString({ fractionalSecondDigits: undefined });
+ assert.sameValue(explicit, expected, "default fractionalSecondDigits is auto (property present but undefined)");
+
+ const implicit = time.toString({});
+ assert.sameValue(implicit, expected, "default fractionalSecondDigits is auto (property not present)");
+
+ const lambda = time.toString(() => {});
+ assert.sameValue(lambda, expected, "default fractionalSecondDigits is auto (property not present, function object)");
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-wrong-type.js
new file mode 100644
index 0000000000..ff611b4652
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-wrong-type.js
@@ -0,0 +1,50 @@
+// |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.tostring
+description: Type conversions for fractionalSecondDigits option
+info: |
+ sec-getoption steps 8–9:
+ 8. Else if _type_ is Number, then
+ a. Set _value_ to ? ToNumber(value).
+ b. ...
+ 9. Else,
+ a. Set _value_ to ? ToString(value).
+ sec-getstringornumberoption step 2:
+ 2. Let _value_ be ? GetOption(_options_, _property_, « Number, String », *undefined*, _fallback_).
+ sec-temporal-tosecondsstringprecision step 9:
+ 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
+ sec-temporal.plaintime.prototype.tostring step 4:
+ 4. Let _precision_ be ? ToSecondsStringPrecision(_options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 987, 650, 0);
+
+assert.throws(RangeError, () => time.toString({ fractionalSecondDigits: null }),
+ "null is not a number and converts to the string 'null' which is not valid for fractionalSecondDigits");
+assert.throws(RangeError, () => time.toString({ fractionalSecondDigits: true }),
+ "true is not a number and converts to the string 'true' which is not valid for fractionalSecondDigits");
+assert.throws(RangeError, () => time.toString({ fractionalSecondDigits: false }),
+ "false is not a number and converts to the string 'false' which is not valid for fractionalSecondDigits");
+assert.throws(TypeError, () => time.toString({ fractionalSecondDigits: Symbol() }),
+ "symbols are not numbers and cannot convert to strings");
+assert.throws(RangeError, () => time.toString({ fractionalSecondDigits: 2n }),
+ "bigints are not numbers and convert to strings which are not valid for fractionalSecondDigits");
+assert.throws(RangeError, () => time.toString({ fractionalSecondDigits: {} }),
+ "plain objects are not numbers and convert to strings which are not valid for fractionalSecondDigits");
+
+const expected = [
+ "get fractionalSecondDigits.toString",
+ "call fractionalSecondDigits.toString",
+];
+const actual = [];
+const observer = TemporalHelpers.toPrimitiveObserver(actual, "auto", "fractionalSecondDigits");
+const result = time.toString({ fractionalSecondDigits: observer });
+assert.sameValue(result, "12:34:56.98765", "object with toString uses toString return value");
+assert.compareArray(actual, expected, "object with toString calls toString and not valueOf");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/length.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/length.js
new file mode 100644
index 0000000000..d52b57ed79
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/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.tostring
+description: Temporal.PlainTime.prototype.toString.length is 0
+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.toString, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/name.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/name.js
new file mode 100644
index 0000000000..7e418da574
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/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.tostring
+description: Temporal.PlainTime.prototype.toString.name is "toString".
+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.toString, "name", {
+ value: "toString",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/not-a-constructor.js
new file mode 100644
index 0000000000..0dc5dd1963
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/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.tostring
+description: >
+ Temporal.PlainTime.prototype.toString 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.toString();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainTime.prototype.toString), false,
+ "isConstructor(Temporal.PlainTime.prototype.toString)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/options-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/options-invalid.js
new file mode 100644
index 0000000000..5b488efb27
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/options-invalid.js
@@ -0,0 +1,18 @@
+// |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.tostring
+description: TypeError thrown when a primitive is passed as the options argument
+features: [Temporal]
+---*/
+
+const instance = Temporal.PlainTime.from("12:56:32");
+const values = [null, true, "hello", Symbol("foo"), 1, 1n];
+
+for (const badOptions of values) {
+ assert.throws(TypeError, () => instance.toString(badOptions));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/options-object.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/options-object.js
new file mode 100644
index 0000000000..ff4fd01393
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/options-object.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.tostring
+description: Empty or a function object may be used as options
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime();
+
+const result1 = instance.toString({});
+assert.sameValue(
+ result1, "00:00:00",
+ "options may be an empty plain object"
+);
+
+const result2 = instance.toString(() => {});
+assert.sameValue(
+ result2, "00:00:00",
+ "options may be a function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/options-undefined.js
new file mode 100644
index 0000000000..c2f24bac5f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/options-undefined.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.tostring
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const tests = [
+ ["15:23", "15:23:00"],
+ ["15:23:30", "15:23:30"],
+ ["15:23:30.1234", "15:23:30.1234"],
+];
+
+for (const [input, expected] of tests) {
+ const time = Temporal.PlainTime.from(input);
+
+ const explicit = time.toString(undefined);
+ assert.sameValue(explicit, expected, "default precision is auto and no rounding");
+
+ const implicit = time.toString();
+ assert.sameValue(implicit, expected, "default precision is auto and no rounding");
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/options-wrong-type.js
new file mode 100644
index 0000000000..130b55c581
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/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.tostring
+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.toString(value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/order-of-operations.js
new file mode 100644
index 0000000000..d417bc719d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/order-of-operations.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.tostring
+description: Properties on objects passed to toString() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "get options.fractionalSecondDigits",
+ "get options.fractionalSecondDigits.toString",
+ "call options.fractionalSecondDigits.toString",
+ "get options.roundingMode",
+ "get options.roundingMode.toString",
+ "call options.roundingMode.toString",
+ "get options.smallestUnit",
+];
+const actual = [];
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+const expectedForSmallestUnit = expected.concat([
+ "get options.smallestUnit.toString",
+ "call options.smallestUnit.toString",
+]);
+
+instance.toString(
+ TemporalHelpers.propertyBagObserver(actual, {
+ fractionalSecondDigits: "auto",
+ roundingMode: "halfExpand",
+ smallestUnit: "millisecond",
+ }, "options"),
+);
+assert.compareArray(actual, expectedForSmallestUnit, "order of operations");
+actual.splice(0); // clear
+
+instance.toString(
+ TemporalHelpers.propertyBagObserver(actual, {
+ fractionalSecondDigits: "auto",
+ roundingMode: "halfExpand",
+ smallestUnit: undefined,
+ }, "options"),
+);
+assert.compareArray(actual, expected, "order of operations with smallestUnit undefined");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/prop-desc.js
new file mode 100644
index 0000000000..10428d6fc5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/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.tostring
+description: The "toString" property of Temporal.PlainTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainTime.prototype.toString,
+ "function",
+ "`typeof PlainTime.prototype.toString` is `function`"
+);
+
+verifyProperty(Temporal.PlainTime.prototype, "toString", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/rounding-cross-midnight.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/rounding-cross-midnight.js
new file mode 100644
index 0000000000..8b5b2cb4ca
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/rounding-cross-midnight.js
@@ -0,0 +1,16 @@
+// |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.tostring
+description: Rounding can cross midnight
+features: [Temporal]
+---*/
+
+const plainTime = new Temporal.PlainTime(23, 59, 59, 999, 999, 999); // one nanosecond before 00:00:00
+for (const roundingMode of ["ceil", "halfExpand"]) {
+ assert.sameValue(plainTime.toString({ fractionalSecondDigits: 8, roundingMode }), "00:00:00.00000000");
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-ceil.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-ceil.js
new file mode 100644
index 0000000000..5d72280641
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-ceil.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.tostring
+description: ceil value for roundingMode option
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 123, 987, 500);
+
+const result1 = time.toString({ smallestUnit: "microsecond", roundingMode: "ceil" });
+assert.sameValue(result1, "12:34:56.123988",
+ "roundingMode is ceil (with 6 digits from smallestUnit)");
+
+const result2 = time.toString({ fractionalSecondDigits: 6, roundingMode: "ceil" });
+assert.sameValue(result2, "12:34:56.123988",
+ "roundingMode is ceil (with 6 digits from fractionalSecondDigits)");
+
+const result3 = time.toString({ smallestUnit: "millisecond", roundingMode: "ceil" });
+assert.sameValue(result3, "12:34:56.124",
+ "roundingMode is ceil (with 3 digits from smallestUnit)");
+
+const result4 = time.toString({ fractionalSecondDigits: 3, roundingMode: "ceil" });
+assert.sameValue(result4, "12:34:56.124",
+ "roundingMode is ceil (with 3 digits from fractionalSecondDigits)");
+
+const result5 = time.toString({ smallestUnit: "second", roundingMode: "ceil" });
+assert.sameValue(result5, "12:34:57",
+ "roundingMode is ceil (with 0 digits from smallestUnit)");
+
+const result6 = time.toString({ fractionalSecondDigits: 0, roundingMode: "ceil" });
+assert.sameValue(result6, "12:34:57",
+ "roundingMode is ceil (with 0 digits from fractionalSecondDigits)");
+
+const result7 = time.toString({ smallestUnit: "minute", roundingMode: "ceil" });
+assert.sameValue(result7, "12:35", "roundingMode is ceil (round to minute)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-expand.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-expand.js
new file mode 100644
index 0000000000..29a4b20c23
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-expand.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.tostring
+description: expand value for roundingMode option
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 123, 987, 500);
+
+const result1 = time.toString({ smallestUnit: "microsecond", roundingMode: "expand" });
+assert.sameValue(result1, "12:34:56.123988",
+ "roundingMode is expand (with 6 digits from smallestUnit)");
+
+const result2 = time.toString({ fractionalSecondDigits: 6, roundingMode: "expand" });
+assert.sameValue(result2, "12:34:56.123988",
+ "roundingMode is expand (with 6 digits from fractionalSecondDigits)");
+
+const result3 = time.toString({ smallestUnit: "millisecond", roundingMode: "expand" });
+assert.sameValue(result3, "12:34:56.124",
+ "roundingMode is expand (with 3 digits from smallestUnit)");
+
+const result4 = time.toString({ fractionalSecondDigits: 3, roundingMode: "expand" });
+assert.sameValue(result4, "12:34:56.124",
+ "roundingMode is expand (with 3 digits from fractionalSecondDigits)");
+
+const result5 = time.toString({ smallestUnit: "second", roundingMode: "expand" });
+assert.sameValue(result5, "12:34:57",
+ "roundingMode is expand (with 0 digits from smallestUnit)");
+
+const result6 = time.toString({ fractionalSecondDigits: 0, roundingMode: "expand" });
+assert.sameValue(result6, "12:34:57",
+ "roundingMode is expand (with 0 digits from fractionalSecondDigits)");
+
+const result7 = time.toString({ smallestUnit: "minute", roundingMode: "expand" });
+assert.sameValue(result7, "12:35", "roundingMode is expand (round to minute)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-floor.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-floor.js
new file mode 100644
index 0000000000..e169a3d814
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-floor.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.tostring
+description: floor value for roundingMode option
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 123, 987, 500);
+
+const result1 = time.toString({ smallestUnit: "microsecond", roundingMode: "floor" });
+assert.sameValue(result1, "12:34:56.123987",
+ "roundingMode is floor (with 6 digits from smallestUnit)");
+
+const result2 = time.toString({ fractionalSecondDigits: 6, roundingMode: "floor" });
+assert.sameValue(result2, "12:34:56.123987",
+ "roundingMode is floor (with 6 digits from fractionalSecondDigits)");
+
+const result3 = time.toString({ smallestUnit: "millisecond", roundingMode: "floor" });
+assert.sameValue(result3, "12:34:56.123",
+ "roundingMode is floor (with 3 digits from smallestUnit)");
+
+const result4 = time.toString({ fractionalSecondDigits: 3, roundingMode: "floor" });
+assert.sameValue(result4, "12:34:56.123",
+ "roundingMode is floor (with 3 digits from fractionalSecondDigits)");
+
+const result5 = time.toString({ smallestUnit: "second", roundingMode: "floor" });
+assert.sameValue(result5, "12:34:56",
+ "roundingMode is floor (with 0 digits from smallestUnit)");
+
+const result6 = time.toString({ fractionalSecondDigits: 0, roundingMode: "floor" });
+assert.sameValue(result6, "12:34:56",
+ "roundingMode is floor (with 0 digits from fractionalSecondDigits)");
+
+const result7 = time.toString({ smallestUnit: "minute", roundingMode: "floor" });
+assert.sameValue(result7, "12:34", "roundingMode is floor (round to minute)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-halfCeil.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-halfCeil.js
new file mode 100644
index 0000000000..4fa20cfcd4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-halfCeil.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.tostring
+description: halfCeil value for roundingMode option
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 123, 987, 500);
+
+const result1 = time.toString({ smallestUnit: "microsecond", roundingMode: "halfCeil" });
+assert.sameValue(result1, "12:34:56.123988",
+ "roundingMode is halfCeil (with 6 digits from smallestUnit)");
+
+const result2 = time.toString({ fractionalSecondDigits: 6, roundingMode: "halfCeil" });
+assert.sameValue(result2, "12:34:56.123988",
+ "roundingMode is halfCeil (with 6 digits from fractionalSecondDigits)");
+
+const result3 = time.toString({ smallestUnit: "millisecond", roundingMode: "halfCeil" });
+assert.sameValue(result3, "12:34:56.124",
+ "roundingMode is halfCeil (with 3 digits from smallestUnit)");
+
+const result4 = time.toString({ fractionalSecondDigits: 3, roundingMode: "halfCeil" });
+assert.sameValue(result4, "12:34:56.124",
+ "roundingMode is halfCeil (with 3 digits from fractionalSecondDigits)");
+
+const result5 = time.toString({ smallestUnit: "second", roundingMode: "halfCeil" });
+assert.sameValue(result5, "12:34:56",
+ "roundingMode is halfCeil (with 0 digits from smallestUnit)");
+
+const result6 = time.toString({ fractionalSecondDigits: 0, roundingMode: "halfCeil" });
+assert.sameValue(result6, "12:34:56",
+ "roundingMode is halfCeil (with 0 digits from fractionalSecondDigits)");
+
+const result7 = time.toString({ smallestUnit: "minute", roundingMode: "halfCeil" });
+assert.sameValue(result7, "12:35", "roundingMode is halfCeil (round to minute)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-halfEven.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-halfEven.js
new file mode 100644
index 0000000000..4740838f6e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-halfEven.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.tostring
+description: halfEven value for roundingMode option
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 123, 987, 500);
+
+const result1 = time.toString({ smallestUnit: "microsecond", roundingMode: "halfEven" });
+assert.sameValue(result1, "12:34:56.123988",
+ "roundingMode is halfEven (with 6 digits from smallestUnit)");
+
+const result2 = time.toString({ fractionalSecondDigits: 6, roundingMode: "halfEven" });
+assert.sameValue(result2, "12:34:56.123988",
+ "roundingMode is halfEven (with 6 digits from fractionalSecondDigits)");
+
+const result3 = time.toString({ smallestUnit: "millisecond", roundingMode: "halfEven" });
+assert.sameValue(result3, "12:34:56.124",
+ "roundingMode is halfEven (with 3 digits from smallestUnit)");
+
+const result4 = time.toString({ fractionalSecondDigits: 3, roundingMode: "halfEven" });
+assert.sameValue(result4, "12:34:56.124",
+ "roundingMode is halfEven (with 3 digits from fractionalSecondDigits)");
+
+const result5 = time.toString({ smallestUnit: "second", roundingMode: "halfEven" });
+assert.sameValue(result5, "12:34:56",
+ "roundingMode is halfEven (with 0 digits from smallestUnit)");
+
+const result6 = time.toString({ fractionalSecondDigits: 0, roundingMode: "halfEven" });
+assert.sameValue(result6, "12:34:56",
+ "roundingMode is halfEven (with 0 digits from fractionalSecondDigits)");
+
+const result7 = time.toString({ smallestUnit: "minute", roundingMode: "halfEven" });
+assert.sameValue(result7, "12:35", "roundingMode is halfEven (round to minute)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-halfExpand.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-halfExpand.js
new file mode 100644
index 0000000000..0cfb80098a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-halfExpand.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.tostring
+description: halfExpand value for roundingMode option
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 123, 987, 500);
+
+const result1 = time.toString({ smallestUnit: "microsecond", roundingMode: "halfExpand" });
+assert.sameValue(result1, "12:34:56.123988",
+ "roundingMode is halfExpand (with 6 digits from smallestUnit)");
+
+const result2 = time.toString({ fractionalSecondDigits: 6, roundingMode: "halfExpand" });
+assert.sameValue(result2, "12:34:56.123988",
+ "roundingMode is halfExpand (with 6 digits from fractionalSecondDigits)");
+
+const result3 = time.toString({ smallestUnit: "millisecond", roundingMode: "halfExpand" });
+assert.sameValue(result3, "12:34:56.124",
+ "roundingMode is halfExpand (with 3 digits from smallestUnit)");
+
+const result4 = time.toString({ fractionalSecondDigits: 3, roundingMode: "halfExpand" });
+assert.sameValue(result4, "12:34:56.124",
+ "roundingMode is halfExpand (with 3 digits from fractionalSecondDigits)");
+
+const result5 = time.toString({ smallestUnit: "second", roundingMode: "halfExpand" });
+assert.sameValue(result5, "12:34:56",
+ "roundingMode is halfExpand (with 0 digits from smallestUnit)");
+
+const result6 = time.toString({ fractionalSecondDigits: 0, roundingMode: "halfExpand" });
+assert.sameValue(result6, "12:34:56",
+ "roundingMode is halfExpand (with 0 digits from fractionalSecondDigits)");
+
+const result7 = time.toString({ smallestUnit: "minute", roundingMode: "halfExpand" });
+assert.sameValue(result7, "12:35", "roundingMode is halfExpand (round to minute)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-halfFloor.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-halfFloor.js
new file mode 100644
index 0000000000..e5edab5e8f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-halfFloor.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.tostring
+description: halfFloor value for roundingMode option
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 123, 987, 500);
+
+const result1 = time.toString({ smallestUnit: "microsecond", roundingMode: "halfFloor" });
+assert.sameValue(result1, "12:34:56.123987",
+ "roundingMode is halfFloor (with 6 digits from smallestUnit)");
+
+const result2 = time.toString({ fractionalSecondDigits: 6, roundingMode: "halfFloor" });
+assert.sameValue(result2, "12:34:56.123987",
+ "roundingMode is halfFloor (with 6 digits from fractionalSecondDigits)");
+
+const result3 = time.toString({ smallestUnit: "millisecond", roundingMode: "halfFloor" });
+assert.sameValue(result3, "12:34:56.124",
+ "roundingMode is halfFloor (with 3 digits from smallestUnit)");
+
+const result4 = time.toString({ fractionalSecondDigits: 3, roundingMode: "halfFloor" });
+assert.sameValue(result4, "12:34:56.124",
+ "roundingMode is halfFloor (with 3 digits from fractionalSecondDigits)");
+
+const result5 = time.toString({ smallestUnit: "second", roundingMode: "halfFloor" });
+assert.sameValue(result5, "12:34:56",
+ "roundingMode is halfFloor (with 0 digits from smallestUnit)");
+
+const result6 = time.toString({ fractionalSecondDigits: 0, roundingMode: "halfFloor" });
+assert.sameValue(result6, "12:34:56",
+ "roundingMode is halfFloor (with 0 digits from fractionalSecondDigits)");
+
+const result7 = time.toString({ smallestUnit: "minute", roundingMode: "halfFloor" });
+assert.sameValue(result7, "12:35", "roundingMode is halfFloor (round to minute)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-halfTrunc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-halfTrunc.js
new file mode 100644
index 0000000000..9c94a5d011
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-halfTrunc.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.tostring
+description: halfTrunc value for roundingMode option
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 123, 987, 500);
+
+const result1 = time.toString({ smallestUnit: "microsecond", roundingMode: "halfTrunc" });
+assert.sameValue(result1, "12:34:56.123987",
+ "roundingMode is halfTrunc (with 6 digits from smallestUnit)");
+
+const result2 = time.toString({ fractionalSecondDigits: 6, roundingMode: "halfTrunc" });
+assert.sameValue(result2, "12:34:56.123987",
+ "roundingMode is halfTrunc (with 6 digits from fractionalSecondDigits)");
+
+const result3 = time.toString({ smallestUnit: "millisecond", roundingMode: "halfTrunc" });
+assert.sameValue(result3, "12:34:56.124",
+ "roundingMode is halfTrunc (with 3 digits from smallestUnit)");
+
+const result4 = time.toString({ fractionalSecondDigits: 3, roundingMode: "halfTrunc" });
+assert.sameValue(result4, "12:34:56.124",
+ "roundingMode is halfTrunc (with 3 digits from fractionalSecondDigits)");
+
+const result5 = time.toString({ smallestUnit: "second", roundingMode: "halfTrunc" });
+assert.sameValue(result5, "12:34:56",
+ "roundingMode is halfTrunc (with 0 digits from smallestUnit)");
+
+const result6 = time.toString({ fractionalSecondDigits: 0, roundingMode: "halfTrunc" });
+assert.sameValue(result6, "12:34:56",
+ "roundingMode is halfTrunc (with 0 digits from fractionalSecondDigits)");
+
+const result7 = time.toString({ smallestUnit: "minute", roundingMode: "halfTrunc" });
+assert.sameValue(result7, "12:35", "roundingMode is halfTrunc (round to minute)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-invalid-string.js
new file mode 100644
index 0000000000..e59438a859
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-invalid-string.js
@@ -0,0 +1,16 @@
+// |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.tostring
+description: RangeError thrown when roundingMode option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 123, 987, 500);
+for (const roundingMode of ["other string", "cile", "CEIL", "ce\u0131l", "auto", "halfexpand", "floor\0"]) {
+ assert.throws(RangeError, () => time.toString({ smallestUnit: "microsecond", roundingMode }));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-trunc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-trunc.js
new file mode 100644
index 0000000000..349ec756fc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-trunc.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.tostring
+description: trunc value for roundingMode option
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 123, 987, 500);
+
+const result1 = time.toString({ smallestUnit: "microsecond", roundingMode: "trunc" });
+assert.sameValue(result1, "12:34:56.123987",
+ "roundingMode is trunc (with 6 digits from smallestUnit)");
+
+const result2 = time.toString({ fractionalSecondDigits: 6, roundingMode: "trunc" });
+assert.sameValue(result2, "12:34:56.123987",
+ "roundingMode is trunc (with 6 digits from fractionalSecondDigits)");
+
+const result3 = time.toString({ smallestUnit: "millisecond", roundingMode: "trunc" });
+assert.sameValue(result3, "12:34:56.123",
+ "roundingMode is trunc (with 3 digits from smallestUnit)");
+
+const result4 = time.toString({ fractionalSecondDigits: 3, roundingMode: "trunc" });
+assert.sameValue(result4, "12:34:56.123",
+ "roundingMode is trunc (with 3 digits from fractionalSecondDigits)");
+
+const result5 = time.toString({ smallestUnit: "second", roundingMode: "trunc" });
+assert.sameValue(result5, "12:34:56",
+ "roundingMode is trunc (with 0 digits from smallestUnit)");
+
+const result6 = time.toString({ fractionalSecondDigits: 0, roundingMode: "trunc" });
+assert.sameValue(result6, "12:34:56",
+ "roundingMode is trunc (with 0 digits from fractionalSecondDigits)");
+
+const result7 = time.toString({ smallestUnit: "minute", roundingMode: "trunc" });
+assert.sameValue(result7, "12:34", "roundingMode is trunc (round to minute)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-undefined.js
new file mode 100644
index 0000000000..836cbb0f68
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-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.tostring
+description: Fallback value for roundingMode option
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 123, 987, 500);
+
+const explicit1 = time.toString({ smallestUnit: "microsecond", roundingMode: undefined });
+assert.sameValue(explicit1, "12:34:56.123987", "default roundingMode is trunc");
+const implicit1 = time.toString({ smallestUnit: "microsecond" });
+assert.sameValue(implicit1, "12:34:56.123987", "default roundingMode is trunc");
+
+const explicit2 = time.toString({ smallestUnit: "millisecond", roundingMode: undefined });
+assert.sameValue(explicit2, "12:34:56.123", "default roundingMode is trunc");
+const implicit2 = time.toString({ smallestUnit: "millisecond" });
+assert.sameValue(implicit2, "12:34:56.123", "default roundingMode is trunc");
+
+const explicit3 = time.toString({ smallestUnit: "second", roundingMode: undefined });
+assert.sameValue(explicit3, "12:34:56", "default roundingMode is trunc");
+const implicit3 = time.toString({ smallestUnit: "second" });
+assert.sameValue(implicit3, "12:34:56", "default roundingMode is trunc");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-wrong-type.js
new file mode 100644
index 0000000000..2d7a86a9c0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-wrong-type.js
@@ -0,0 +1,18 @@
+// |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.tostring
+description: Type conversions for roundingMode option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 123, 987, 500);
+TemporalHelpers.checkStringOptionWrongType("roundingMode", "trunc",
+ (roundingMode) => time.toString({ smallestUnit: "microsecond", roundingMode }),
+ (result, descr) => assert.sameValue(result, "12:34:56.123987", descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/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/toString/smallestunit-fractionalseconddigits.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/smallestunit-fractionalseconddigits.js
new file mode 100644
index 0000000000..9a9b87c3d1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/smallestunit-fractionalseconddigits.js
@@ -0,0 +1,33 @@
+// |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.tostring
+description: fractionalSecondDigits option is not used with smallestUnit present
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 789, 999, 999);
+const tests = [
+ ["minute", "12:34"],
+ ["second", "12:34:56"],
+ ["millisecond", "12:34:56.789"],
+ ["microsecond", "12:34:56.789999"],
+ ["nanosecond", "12:34:56.789999999"],
+];
+
+for (const [smallestUnit, expected] of tests) {
+ const string = time.toString({
+ smallestUnit,
+ fractionalSecondDigits: 5,
+ });
+ assert.sameValue(string, expected, `smallestUnit: "${smallestUnit}" overrides fractionalSecondDigits`);
+}
+
+assert.throws(RangeError, () => time.toString({
+ smallestUnit: "hour",
+ fractionalSecondDigits: 5,
+}), "hour is an invalid smallestUnit but still overrides fractionalSecondDigits");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/smallestunit-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/smallestunit-invalid-string.js
new file mode 100644
index 0000000000..7b31d12b17
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/smallestunit-invalid-string.js
@@ -0,0 +1,40 @@
+// |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.tostring
+description: RangeError thrown when smallestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 123, 987, 500);
+const badValues = [
+ "era",
+ "eraYear",
+ "year",
+ "month",
+ "week",
+ "day",
+ "hour",
+ "millisecond\0",
+ "mill\u0131second",
+ "SECOND",
+ "eras",
+ "eraYears",
+ "years",
+ "months",
+ "weeks",
+ "days",
+ "hours",
+ "milliseconds\0",
+ "mill\u0131seconds",
+ "SECONDS",
+ "other string",
+];
+for (const smallestUnit of badValues) {
+ assert.throws(RangeError, () => time.toString({ 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/toString/smallestunit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/smallestunit-plurals-accepted.js
new file mode 100644
index 0000000000..9fde77c2c4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/smallestunit-plurals-accepted.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.tostring
+description: Plural units are accepted as well for the smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 789, 999, 999);
+const validUnits = [
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+];
+TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => time.toString({ smallestUnit }), validUnits);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/smallestunit-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/smallestunit-undefined.js
new file mode 100644
index 0000000000..fb45251a66
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/smallestunit-undefined.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.tostring
+description: Fallback value for smallestUnit option
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 123, 987, 500);
+
+const explicit1 = time.toString({ smallestUnit: undefined, fractionalSecondDigits: 6 });
+assert.sameValue(explicit1, "12:34:56.123987", "default smallestUnit defers to fractionalSecondDigits");
+const implicit1 = time.toString({ fractionalSecondDigits: 6 });
+assert.sameValue(implicit1, "12:34:56.123987", "default smallestUnit defers to fractionalSecondDigits");
+
+const explicit2 = time.toString({ smallestUnit: undefined, fractionalSecondDigits: 3 });
+assert.sameValue(explicit2, "12:34:56.123", "default smallestUnit defers to fractionalSecondDigits");
+const implicit2 = time.toString({ fractionalSecondDigits: 3 });
+assert.sameValue(implicit2, "12:34:56.123", "default smallestUnit defers to fractionalSecondDigits");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/smallestunit-valid-units.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/smallestunit-valid-units.js
new file mode 100644
index 0000000000..6d44d85080
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/smallestunit-valid-units.js
@@ -0,0 +1,58 @@
+// |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.tostring
+description: Valid units for the smallestUnit option
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 123, 456, 789);
+
+function test(instance, expectations, description) {
+ for (const [smallestUnit, expectedResult] of expectations) {
+ assert.sameValue(instance.toString({ smallestUnit }), expectedResult,
+ `${description} with smallestUnit "${smallestUnit}"`);
+ }
+}
+
+test(
+ time,
+ [
+ ["minute", "12:34"],
+ ["second", "12:34:56"],
+ ["millisecond", "12:34:56.123"],
+ ["microsecond", "12:34:56.123456"],
+ ["nanosecond", "12:34:56.123456789"],
+ ],
+ "subseconds toString"
+);
+
+test(
+ new Temporal.PlainTime(12, 34),
+ [
+ ["minute", "12:34"],
+ ["second", "12:34:00"],
+ ["millisecond", "12:34:00.000"],
+ ["microsecond", "12:34:00.000000"],
+ ["nanosecond", "12:34:00.000000000"],
+ ],
+ "whole minutes toString"
+);
+
+const notValid = [
+ "era",
+ "year",
+ "month",
+ "week",
+ "day",
+ "hour",
+];
+
+notValid.forEach((smallestUnit) => {
+ assert.throws(RangeError, () => time.toString({ smallestUnit }),
+ `"${smallestUnit}" is not a valid unit for the smallestUnit option`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/smallestunit-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/smallestunit-wrong-type.js
new file mode 100644
index 0000000000..33715d5b90
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toString/smallestunit-wrong-type.js
@@ -0,0 +1,18 @@
+// |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.tostring
+description: Type conversions for smallestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 123, 987, 500);
+TemporalHelpers.checkStringOptionWrongType("smallestUnit", "microsecond",
+ (smallestUnit) => time.toString({ smallestUnit }),
+ (result, descr) => assert.sameValue(result, "12:34:56.123987", descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toStringTag/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toStringTag/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toStringTag/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toStringTag/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toStringTag/prop-desc.js
new file mode 100644
index 0000000000..355fee5ac9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toStringTag/prop-desc.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-@@tostringtag
+description: The @@toStringTag property of Temporal.PlainTime
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainTime.prototype, Symbol.toStringTag, {
+ value: "Temporal.PlainTime",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toStringTag/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toStringTag/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toStringTag/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..4aab6d8fcd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-builtin-calendar-no-array-iteration.js
@@ -0,0 +1,24 @@
+// |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.tozoneddatetime
+description: >
+ Calling the method with a property bag argument with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+const arg = { year: 2000, month: 5, day: 2, calendar: "iso8601" };
+instance.toZonedDateTime({ plainDate: arg, timeZone: "UTC" });
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-calendar-datefromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..b24fe89cee
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-calendar-datefromfields-called-with-null-prototype-fields.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.tozoneddatetime
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+const arg = { year: 2000, month: 5, day: 2, calendar };
+instance.toZonedDateTime({ plainDate: arg, timeZone: "UTC" });
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..df8e557c95
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-constructor-in-calendar-fields.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.tozoneddatetime
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+assert.throws(RangeError, () => instance.toZonedDateTime({ plainDate: arg, timeZone: "UTC" }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-duplicate-calendar-fields.js
new file mode 100644
index 0000000000..c6b11b1c5e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-duplicate-calendar-fields.js
@@ -0,0 +1,19 @@
+// |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.tozoneddatetime
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['day'], ['month'], ['monthCode'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar };
+ const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+ assert.throws(RangeError, () => instance.toZonedDateTime({ plainDate: arg, timeZone: "UTC" }));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-leap-second.js
new file mode 100644
index 0000000000..4adfadacb5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-leap-second.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.tozoneddatetime
+description: Leap second is a valid ISO string for PlainDate
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+let arg = "2016-12-31T23:59:60";
+const result1 = instance.toZonedDateTime({ plainDate: arg, timeZone: "UTC" });
+assert.sameValue(
+ result1.epochNanoseconds,
+ 1_483_187_696_987_654_321n,
+ "leap second is a valid ISO string for PlainDate"
+);
+
+arg = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+const result2 = instance.toZonedDateTime({ plainDate: arg, timeZone: "UTC" });
+assert.sameValue(
+ result2.epochNanoseconds,
+ 1_483_187_696_987_654_321n,
+ "second: 60 is ignored in property bag for PlainDate"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-number.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-number.js
new file mode 100644
index 0000000000..a171ac85d7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/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.tozoneddatetime
+description: A number cannot be used in place of a Temporal.PlainDate
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+const numbers = [
+ 1,
+ 19761118,
+ -19761118,
+ 1234567890,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => instance.toZonedDateTime({ plainDate: arg, timeZone: "UTC" }),
+ 'Numbers cannot be used in place of an ISO string for PlainDate'
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-plaindatetime.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-plaindatetime.js
new file mode 100644
index 0000000000..db71724782
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-plaindatetime.js
@@ -0,0 +1,32 @@
+// |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.tozoneddatetime
+description: Fast path for converting Temporal.PlainDateTime to Temporal.PlainDate by reading internal slots
+info: |
+ sec-temporal.plaintime.prototype.tozoneddatetime step 5:
+ 5. Let _temporalDate_ be ? ToTemporalDate(_temporalDateLike_).
+ sec-temporal-totemporaldate step 2.b:
+ b. If _item_ has an [[InitializedTemporalDateTime]] internal slot, then
+ i. Return ! CreateTemporalDate(_item_.[[ISOYear]], _item_.[[ISOMonth]], _item_.[[ISODay]], _item_.[[Calendar]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkPlainDateTimeConversionFastPath((datetime) => {
+ const time = new Temporal.PlainTime(6, 54, 32, 123, 456, 789);
+ const result = time.toZonedDateTime({ plainDate: datetime, timeZone: "UTC" });
+ assert.sameValue(result.year, 2000, "year result");
+ assert.sameValue(result.month, 5, "month result");
+ assert.sameValue(result.day, 2, "day result");
+ assert.sameValue(result.hour, 6, "hour result");
+ assert.sameValue(result.minute, 54, "minute result");
+ assert.sameValue(result.second, 32, "second result");
+ assert.sameValue(result.millisecond, 123, "millisecond result");
+ assert.sameValue(result.microsecond, 456, "microsecond result");
+ assert.sameValue(result.nanosecond, 789, "nanosecond result");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-primitive.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-primitive.js
new file mode 100644
index 0000000000..71011392de
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-primitive.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.tozoneddatetime
+description: TypeError thrown if a primitive is passed as the argument
+info: |
+ Temporal.PlainTime.prototype.toZonedDateTime ( item )
+
+ 3. If Type(item) is not Object, then
+ a. Throw a TypeError exception.
+features: [Symbol, Temporal]
+---*/
+
+const instance = Temporal.PlainTime.from("00:00");
+
+assert.throws(TypeError, () => instance.toZonedDateTime(undefined), "undefined");
+assert.throws(TypeError, () => instance.toZonedDateTime(null), "null");
+assert.throws(TypeError, () => instance.toZonedDateTime(true), "true");
+assert.throws(TypeError, () => instance.toZonedDateTime(""), "empty string");
+assert.throws(TypeError, () => instance.toZonedDateTime(Symbol()), "symbol");
+assert.throws(TypeError, () => instance.toZonedDateTime(1), "1");
+assert.throws(TypeError, () => instance.toZonedDateTime(1n), "1n");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-propertybag-calendar-case-insensitive.js
new file mode 100644
index 0000000000..476177705e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-propertybag-calendar-case-insensitive.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.tozoneddatetime
+description: The calendar name is case-insensitive
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+const calendar = "IsO8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.toZonedDateTime({ plainDate: arg, timeZone: "UTC" });
+assert.sameValue(result.epochNanoseconds, 217_168_496_987_654_321n, "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-propertybag-calendar-leap-second.js
new file mode 100644
index 0000000000..126ebf0d1c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-propertybag-calendar-leap-second.js
@@ -0,0 +1,23 @@
+// |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.tozoneddatetime
+description: Leap second is a valid ISO string for a calendar in a property bag
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+const calendar = "2016-12-31T23:59:60";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.toZonedDateTime({ plainDate: arg, timeZone: "UTC" });
+assert.sameValue(
+ result.epochNanoseconds,
+ 217_168_496_987_654_321n,
+ "leap second is a valid ISO string for calendar"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-propertybag-calendar-number.js
new file mode 100644
index 0000000000..d1ec94a514
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-propertybag-calendar-number.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.tozoneddatetime
+description: A number as calendar in a property bag is not accepted
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+const numbers = [
+ 1,
+ 19970327,
+ -19970327,
+ 1234567890,
+];
+
+for (const calendar of numbers) {
+ const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+ assert.throws(
+ TypeError,
+ () => instance.toZonedDateTime({ plainDate: arg, timeZone: "UTC" }),
+ "Numbers cannot be used as a calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-propertybag-calendar-string.js
new file mode 100644
index 0000000000..11f185579a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-propertybag-calendar-string.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.tozoneddatetime
+description: A calendar ID is valid input for Calendar
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+const calendar = "iso8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.toZonedDateTime({ plainDate: arg, timeZone: "UTC" });
+assert.sameValue(result.epochNanoseconds, 217_168_496_987_654_321n, `Calendar created from string "${calendar}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-propertybag-calendar-wrong-type.js
new file mode 100644
index 0000000000..fca5483d19
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-propertybag-calendar-wrong-type.js
@@ -0,0 +1,46 @@
+// |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.tozoneddatetime
+description: >
+ Appropriate error thrown when a calendar property from a property bag cannot
+ be converted to a calendar object or string
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [calendar, description] of primitiveTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ typeof calendar === 'string' ? RangeError : TypeError,
+ () => instance.toZonedDateTime({ plainDate: arg, timeZone: "UTC" }),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+ [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields()
+];
+
+for (const [calendar, description] of typeErrorTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(TypeError, () => instance.toZonedDateTime({ plainDate: arg, timeZone: "UTC" }), `${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/toZonedDateTime/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-propertybag-calendar-year-zero.js
new file mode 100644
index 0000000000..2acf24b30d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-propertybag-calendar-year-zero.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.tozoneddatetime
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T17:45",
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+01:00",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.toZonedDateTime({ plainDate: arg, timeZone: "UTC" }),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-propertybag-missing-properties.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-propertybag-missing-properties.js
new file mode 100644
index 0000000000..e2aaab1a86
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-propertybag-missing-properties.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.tozoneddatetime
+description: Both plainDate and timeZone properties need to not be undefined.
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime();
+const plainDate = new Temporal.PlainDate(2022, 5, 19);
+const timeZone = new Temporal.TimeZone("UTC");
+assert.throws(TypeError, () => instance.toZonedDateTime({}),
+ "no properties");
+assert.throws(TypeError, () => instance.toZonedDateTime({ plainDate }),
+ "only plainDate");
+assert.throws(TypeError, () => instance.toZonedDateTime({ plainDate, timeZone: undefined }),
+ "timeZone explicitly undefined");
+assert.throws(TypeError, () => instance.toZonedDateTime({ timeZone }),
+ "only timeZone");
+assert.throws(TypeError, () => instance.toZonedDateTime({ plainDate: undefined, timeZone }),
+ "plainDate explicitly undefined");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-proto-in-calendar-fields.js
new file mode 100644
index 0000000000..ace48b7cb0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-proto-in-calendar-fields.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.tozoneddatetime
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+assert.throws(RangeError, () => instance.toZonedDateTime({ plainDate: arg, timeZone: "UTC" }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..c48022d265
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-calendar-annotation.js
@@ -0,0 +1,33 @@
+// |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.tozoneddatetime
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02[u-ca=iso8601]", "without time or time zone"],
+ ["2000-05-02[UTC][u-ca=iso8601]", "with time zone and no time"],
+ ["2000-05-02T15:23[u-ca=iso8601]", "without time zone"],
+ ["2000-05-02T15:23[UTC][u-ca=iso8601]", "with time zone"],
+ ["2000-05-02T15:23[!u-ca=iso8601]", "with ! and no time zone"],
+ ["2000-05-02T15:23[UTC][!u-ca=iso8601]", "with ! and time zone"],
+ ["2000-05-02T15:23[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.toZonedDateTime({ plainDate: arg, timeZone: "UTC" });
+
+ assert.sameValue(
+ result.epochNanoseconds,
+ 957_270_896_987_654_321n,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..dc53b6ebfe
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-critical-unknown-annotation.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.tozoneddatetime
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[!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.toZonedDateTime({ plainDate: arg, timeZone: "UTC" }),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..fcc2b3f072
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-date-with-utc-offset.js
@@ -0,0 +1,48 @@
+// |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.tozoneddatetime
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+const validStrings = [
+ "2000-05-02T00+00:00",
+ "2000-05-02T00+00:00[UTC]",
+ "2000-05-02T00+00:00[!UTC]",
+ "2000-05-02T00-02:30[America/St_Johns]",
+];
+
+for (const arg of validStrings) {
+ const result = instance.toZonedDateTime({ plainDate: arg, timeZone: "UTC" });
+
+ assert.sameValue(
+ result.epochNanoseconds,
+ 957_270_896_987_654_321n,
+ `"${arg}" is a valid UTC offset with time for PlainDate`
+ );
+}
+
+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.toZonedDateTime({ plainDate: arg, timeZone: "UTC" }),
+ `"${arg}" UTC offset without time is not valid for PlainDate`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-invalid.js
new file mode 100644
index 0000000000..1757339404
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-invalid.js
@@ -0,0 +1,64 @@
+// |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.tozoneddatetime
+description: >
+ RangeError thrown if an invalid ISO string (or syntactically valid ISO string
+ that is not supported) is used as a PlainDate
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ // invalid ISO strings:
+ "",
+ "invalid iso8601",
+ "2020-01-00",
+ "2020-01-32",
+ "2020-02-30",
+ "2021-02-29",
+ "2020-00-01",
+ "2020-13-01",
+ "2020-01-01T",
+ "2020-01-01T25:00:00",
+ "2020-01-01T01:60:00",
+ "2020-01-01T01:60:61",
+ "2020-01-01junk",
+ "2020-01-01T00:00:00junk",
+ "2020-01-01T00:00:00+00:00junk",
+ "2020-01-01T00:00:00+00:00[UTC]junk",
+ "2020-01-01T00:00:00+00:00[UTC][u-ca=iso8601]junk",
+ "02020-01-01",
+ "2020-001-01",
+ "2020-01-001",
+ "2020-01-01T001",
+ "2020-01-01T01:001",
+ "2020-01-01T01:01:001",
+ // valid, but forms not supported in Temporal:
+ "2020-W01-1",
+ "2020-001",
+ "+0002020-01-01",
+ // valid, but this calendar must not exist:
+ "2020-01-01[u-ca=notexist]",
+ // may be valid in other contexts, but insufficient information for PlainDate:
+ "2020-01",
+ "+002020-01",
+ "01-01",
+ "2020-W01",
+ "P1Y",
+ "-P12Y",
+ // valid, but outside the supported range:
+ "-999999-01-01",
+ "+999999-01-01",
+];
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => instance.toZonedDateTime({ plainDate: arg, timeZone: "UTC" }),
+ `"${arg}" should not be a valid ISO string for a PlainDate`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..d381bc0212
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/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.tozoneddatetime
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[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.toZonedDateTime({ plainDate: arg, timeZone: "UTC" }),
+ `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/toZonedDateTime/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..a95338db3d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-multiple-time-zone.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.tozoneddatetime
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[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.toZonedDateTime({ plainDate: arg, timeZone: "UTC" }),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-time-separators.js
new file mode 100644
index 0000000000..653debbd30
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-time-separators.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.tozoneddatetime
+description: Time separator in string argument can vary
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02T15:23", "uppercase T"],
+ ["2000-05-02t15:23", "lowercase T"],
+ ["2000-05-02 15:23", "space between date and time"],
+];
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.toZonedDateTime({ plainDate: arg, timeZone: "UTC" });
+
+ assert.sameValue(
+ result.epochNanoseconds,
+ 957_270_896_987_654_321n,
+ `variant time separators (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..c49c5b85d7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-time-zone-annotation.js
@@ -0,0 +1,38 @@
+// |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.tozoneddatetime
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02[Asia/Kolkata]", "named, with no time"],
+ ["2000-05-02[!Europe/Vienna]", "named, with ! and no time"],
+ ["2000-05-02[+00:00]", "numeric, with no time"],
+ ["2000-05-02[!-02:30]", "numeric, with ! and no time"],
+ ["2000-05-02T15:23[America/Sao_Paulo]", "named, with no offset"],
+ ["2000-05-02T15:23[!Asia/Tokyo]", "named, with ! and no offset"],
+ ["2000-05-02T15:23[-02:30]", "numeric, with no offset"],
+ ["2000-05-02T15:23[!+00:00]", "numeric, with ! and no offset"],
+ ["2000-05-02T15:23+00:00[America/New_York]", "named, with offset"],
+ ["2000-05-02T15:23+00:00[!UTC]", "named, with offset and !"],
+ ["2000-05-02T15:23+00:00[+01:00]", "numeric, with offset"],
+ ["2000-05-02T15:23+00:00[!-08:00]", "numeric, with offset and !"],
+];
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.toZonedDateTime({ plainDate: arg, timeZone: "UTC" });
+
+ assert.sameValue(
+ result.epochNanoseconds,
+ 957_270_896_987_654_321n,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..b570aa57c5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-unknown-annotation.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.tozoneddatetime
+description: Various forms of unknown annotation
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02[foo=bar]", "without time"],
+ ["2000-05-02T15:23[foo=bar]", "alone"],
+ ["2000-05-02T15:23[UTC][foo=bar]", "with time zone"],
+ ["2000-05-02T15:23[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["2000-05-02T15:23[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["2000-05-02T15:23[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.toZonedDateTime({ plainDate: arg, timeZone: "UTC" });
+
+ assert.sameValue(
+ result.epochNanoseconds,
+ 957_270_896_987_654_321n,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..c36daff5df
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-string-with-utc-designator.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.tozoneddatetime
+description: RangeError thrown if a string with UTC designator is used as a PlainDate
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "2019-10-01T09:00:00Z",
+ "2019-10-01T09:00:00Z[UTC]",
+];
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.toZonedDateTime({ plainDate: arg, timeZone: "UTC" }),
+ "String with UTC designator should not be valid as a PlainDate"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-wrong-type.js
new file mode 100644
index 0000000000..762fa6e9b9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/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.tozoneddatetime
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainDate
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+const primitiveTests = [
+ [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.toZonedDateTime({ plainDate: arg, timeZone: "UTC" }),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [undefined, "undefined"], // plainDate property is required
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.PlainDate, "Temporal.PlainDate, object"],
+ [Temporal.PlainDate.prototype, "Temporal.PlainDate.prototype, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.toZonedDateTime({ plainDate: arg, timeZone: "UTC" }), `${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/toZonedDateTime/argument-zoneddatetime-convert.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-zoneddatetime-convert.js
new file mode 100644
index 0000000000..43ccf45aec
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-zoneddatetime-convert.js
@@ -0,0 +1,22 @@
+// |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.tozoneddatetime
+description: An exception from TimeZone#getOffsetNanosecondsFor() is propagated.
+features: [Temporal]
+---*/
+
+class TZ extends Temporal.TimeZone {
+ constructor() { super("UTC") }
+ getOffsetNanosecondsFor() { throw new Test262Error() }
+}
+
+const tz = new TZ();
+const arg = new Temporal.ZonedDateTime(0n, tz);
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+assert.throws(Test262Error, () => instance.toZonedDateTime({ plainDate: arg, timeZone: "UTC" }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-zoneddatetime-slots.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-zoneddatetime-slots.js
new file mode 100644
index 0000000000..a8dea3776c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-zoneddatetime-slots.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.tozoneddatetime
+description: Getters are not called when converting a ZonedDateTime to a PlainDate.
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.ZonedDateTime.prototype);
+const getters = ["year", "month", "monthCode", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond", "calendar"];
+
+for (const property of getters) {
+ Object.defineProperty(Temporal.ZonedDateTime.prototype, property, {
+ get() {
+ actual.push(`get ${property}`);
+ const value = prototypeDescrs[property].get.call(this);
+ return {
+ toString() {
+ actual.push(`toString ${property}`);
+ return value.toString();
+ },
+ valueOf() {
+ actual.push(`valueOf ${property}`);
+ return value;
+ },
+ };
+ },
+ });
+}
+
+const arg = new Temporal.ZonedDateTime(0n, "UTC");
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+instance.toZonedDateTime({ plainDate: arg, timeZone: "UTC" });
+assert.compareArray(actual, []);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..39ca85488e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/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.tozoneddatetime
+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.toZonedDateTime({ plainDate: datetime, timeZone: "UTC" }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..79284d6948
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/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.tozoneddatetime
+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.toZonedDateTime({ plainDate: datetime, timeZone: "UTC" }),
+ `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/toZonedDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..359fcb371c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/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.tozoneddatetime
+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.toZonedDateTime({ plainDate: datetime, timeZone: "UTC" }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..b1e0abaa25
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/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.tozoneddatetime
+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.toZonedDateTime({ plainDate: datetime, timeZone: "UTC" }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/basic.js
new file mode 100644
index 0000000000..8dd138b0eb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/basic.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.tozoneddatetime
+description: Basic tests for toZonedDateTime().
+features: [Temporal]
+---*/
+
+const plainTime = Temporal.PlainTime.from('12:00');
+const plainDate = Temporal.PlainDate.from('2020-07-08');
+const timeZone = Temporal.TimeZone.from('-07:00');
+
+const objects = plainTime.toZonedDateTime({ timeZone, plainDate });
+assert.sameValue(objects.epochNanoseconds, 1594234800000000000n, "objects: epochNanoseconds");
+assert.sameValue(objects.getTimeZone(), timeZone, "objects: timeZone");
+
+const timeZoneString = plainTime.toZonedDateTime({ timeZone: "-07:00", plainDate });
+assert.sameValue(timeZoneString.epochNanoseconds, 1594234800000000000n, "timeZone string: epochNanoseconds");
+assert.sameValue(timeZoneString.timeZoneId, "-07:00", "timeZone string: timeZone");
+
+const plainDateString = plainTime.toZonedDateTime({ timeZone, plainDate: "2020-07-08" });
+assert.sameValue(plainDateString.epochNanoseconds, 1594234800000000000n, "plainDate string: epochNanoseconds");
+assert.sameValue(plainDateString.getTimeZone(), timeZone, "plainDate string: timeZone");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/branding.js
new file mode 100644
index 0000000000..9df89a0dd4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/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.tozoneddatetime
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toZonedDateTime = Temporal.PlainTime.prototype.toZonedDateTime;
+
+assert.sameValue(typeof toZonedDateTime, "function");
+
+const args = [{ plainDate: "2022-05-19", timeZone: "UTC" }];
+
+assert.throws(TypeError, () => toZonedDateTime.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => toZonedDateTime.apply(null, args), "null");
+assert.throws(TypeError, () => toZonedDateTime.apply(true, args), "true");
+assert.throws(TypeError, () => toZonedDateTime.apply("", args), "empty string");
+assert.throws(TypeError, () => toZonedDateTime.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => toZonedDateTime.apply(1, args), "1");
+assert.throws(TypeError, () => toZonedDateTime.apply({}, args), "plain object");
+assert.throws(TypeError, () => toZonedDateTime.apply(Temporal.PlainTime, args), "Temporal.PlainTime");
+assert.throws(TypeError, () => toZonedDateTime.apply(Temporal.PlainTime.prototype, args), "Temporal.PlainTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/builtin.js
new file mode 100644
index 0000000000..e9271270ce
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/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.tozoneddatetime
+description: >
+ Tests that Temporal.PlainTime.prototype.toZonedDateTime
+ 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.toZonedDateTime),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainTime.prototype.toZonedDateTime),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainTime.prototype.toZonedDateTime),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainTime.prototype.toZonedDateTime.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/calendar-datefromfields-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/calendar-datefromfields-called-with-options-undefined.js
new file mode 100644
index 0000000000..a94747cf81
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/calendar-datefromfields-called-with-options-undefined.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.tozoneddatetime
+description: >
+ Calendar.dateFromFields method is called with undefined as the options value
+ when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321, calendar);
+instance.toZonedDateTime({ plainDate: { year: 2000, month: 5, day: 3, calendar }, timeZone: new Temporal.TimeZone("UTC") });
+assert.sameValue(calendar.dateFromFieldsCallCount, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/calendar-fields-iterable.js
new file mode 100644
index 0000000000..b8c58af599
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/calendar-fields-iterable.js
@@ -0,0 +1,34 @@
+// |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.tozoneddatetime
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plaintime.prototype.tozoneddatetime step 5:
+ 3. Let _temporalDate_ be ? ToTemporalDate(_temporalDateLike_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const time = new Temporal.PlainTime(13, 3);
+const calendar = TemporalHelpers.calendarFieldsIterable();
+time.toZonedDateTime({ plainDate: { year: 2000, month: 5, day: 3, calendar }, timeZone: "UTC" });
+
+assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/calendar-temporal-object.js
new file mode 100644
index 0000000000..a9a98f20fd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/calendar-temporal-object.js
@@ -0,0 +1,30 @@
+// |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.tozoneddatetime
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plaintime.prototype.tozoneddatetime step 5:
+ 5. Let _temporalDate_ be ? ToTemporalDate(_temporalDateLike_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject, calendar) => {
+ const time = new Temporal.PlainTime(13, 3);
+ const result = time.toZonedDateTime({ timeZone: "UTC", plainDate: { year: 2000, month: 5, day: 3, calendar: temporalObject } });
+ assert.sameValue(result.getCalendar(), calendar, "Temporal object coerced to calendar");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/getpossibleinstantsfor-called-with-iso8601-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/getpossibleinstantsfor-called-with-iso8601-calendar.js
new file mode 100644
index 0000000000..729c58da0f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/getpossibleinstantsfor-called-with-iso8601-calendar.js
@@ -0,0 +1,58 @@
+// |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.tozoneddatetime
+description: >
+ Time zone's getPossibleInstantsFor is called with a PlainDateTime with the
+ built-in ISO 8601 calendar
+features: [Temporal]
+info: |
+ DisambiguatePossibleInstants:
+ 2. Let _n_ be _possibleInstants_'s length.
+ ...
+ 5. Assert: _n_ = 0.
+ ...
+ 19. If _disambiguation_ is *"earlier"*, then
+ ...
+ c. Let _earlierDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*).
+ d. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _earlierDateTime_).
+ ...
+ 20. Assert: _disambiguation_ is *"compatible"* or *"later"*.
+ ...
+ 23. Let _laterDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*).
+ 24. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _laterDateTime_).
+---*/
+
+class SkippedDateTime extends Temporal.TimeZone {
+ constructor() {
+ super("UTC");
+ this.calls = 0;
+ }
+
+ getPossibleInstantsFor(dateTime) {
+ // Calls occur in pairs. For the first one return no possible instants so
+ // that DisambiguatePossibleInstants will call it again
+ if (this.calls++ % 2 == 0) {
+ return [];
+ }
+
+ assert.sameValue(
+ dateTime.getISOFields().calendar,
+ "iso8601",
+ "getPossibleInstantsFor called with dateTime with built-in ISO 8601 calendar"
+ );
+ return super.getPossibleInstantsFor(dateTime);
+ }
+}
+
+const nonBuiltinISOCalendar = new Temporal.Calendar("iso8601");
+const timeZone = new SkippedDateTime();
+
+const instance = new Temporal.PlainTime(12, 34, 56);
+instance.toZonedDateTime({ timeZone, plainDate: new Temporal.PlainDate(2000, 5, 2, nonBuiltinISOCalendar) });
+
+assert.sameValue(timeZone.calls, 2, "getPossibleInstantsFor should have been called 2 times");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..56340351a6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/infinity-throws-rangeerror.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.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plaintime.prototype.tozoneddatetime
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(15);
+const base = { year: 2000, month: 5, day: 2 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.toZonedDateTime({ timeZone: "UTC", plainDate: { ...base, [prop]: inf } }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.toZonedDateTime({ timeZone: "UTC", plainDate: { ...base, [prop]: obj } }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/length.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/length.js
new file mode 100644
index 0000000000..73dda4c229
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/length.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.tozoneddatetime
+description: Temporal.PlainTime.prototype.toZonedDateTime.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.toZonedDateTime, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/name.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/name.js
new file mode 100644
index 0000000000..c6520773db
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/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.tozoneddatetime
+description: Temporal.PlainTime.prototype.toZonedDateTime.name is "toZonedDateTime".
+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.toZonedDateTime, "name", {
+ value: "toZonedDateTime",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/not-a-constructor.js
new file mode 100644
index 0000000000..5e5068b867
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/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.tozoneddatetime
+description: >
+ Temporal.PlainTime.prototype.toZonedDateTime 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.toZonedDateTime();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainTime.prototype.toZonedDateTime), false,
+ "isConstructor(Temporal.PlainTime.prototype.toZonedDateTime)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/order-of-operations.js
new file mode 100644
index 0000000000..168f4cf078
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/order-of-operations.js
@@ -0,0 +1,118 @@
+// |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.tozoneddatetime
+description: User code calls happen in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const expected = [
+ "get item.plainDate",
+ "get item.plainDate.calendar",
+ "has item.plainDate.calendar.dateAdd",
+ "has item.plainDate.calendar.dateFromFields",
+ "has item.plainDate.calendar.dateUntil",
+ "has item.plainDate.calendar.day",
+ "has item.plainDate.calendar.dayOfWeek",
+ "has item.plainDate.calendar.dayOfYear",
+ "has item.plainDate.calendar.daysInMonth",
+ "has item.plainDate.calendar.daysInWeek",
+ "has item.plainDate.calendar.daysInYear",
+ "has item.plainDate.calendar.fields",
+ "has item.plainDate.calendar.id",
+ "has item.plainDate.calendar.inLeapYear",
+ "has item.plainDate.calendar.mergeFields",
+ "has item.plainDate.calendar.month",
+ "has item.plainDate.calendar.monthCode",
+ "has item.plainDate.calendar.monthDayFromFields",
+ "has item.plainDate.calendar.monthsInYear",
+ "has item.plainDate.calendar.weekOfYear",
+ "has item.plainDate.calendar.year",
+ "has item.plainDate.calendar.yearMonthFromFields",
+ "has item.plainDate.calendar.yearOfWeek",
+ "get item.plainDate.calendar.dateFromFields",
+ "get item.plainDate.calendar.fields",
+ "call item.plainDate.calendar.fields",
+ "get item.plainDate.day",
+ "get item.plainDate.day.valueOf",
+ "call item.plainDate.day.valueOf",
+ "get item.plainDate.month",
+ "get item.plainDate.month.valueOf",
+ "call item.plainDate.month.valueOf",
+ "get item.plainDate.monthCode",
+ "get item.plainDate.monthCode.toString",
+ "call item.plainDate.monthCode.toString",
+ "get item.plainDate.year",
+ "get item.plainDate.year.valueOf",
+ "call item.plainDate.year.valueOf",
+ "call item.plainDate.calendar.dateFromFields",
+ "get item.timeZone",
+ "has item.timeZone.getOffsetNanosecondsFor",
+ "has item.timeZone.getPossibleInstantsFor",
+ "has item.timeZone.id",
+ "get item.timeZone.getOffsetNanosecondsFor",
+ "get item.timeZone.getPossibleInstantsFor",
+ "call item.timeZone.getPossibleInstantsFor",
+];
+
+const calendar = TemporalHelpers.calendarObserver(actual, "item.plainDate.calendar");
+const instance = new Temporal.PlainTime(2, 30);
+
+const dstTimeZone = TemporalHelpers.springForwardFallBackTimeZone();
+const timeZone = TemporalHelpers.timeZoneObserver(actual, "item.timeZone", {
+ getOffsetNanosecondsFor: dstTimeZone.getOffsetNanosecondsFor,
+ getPossibleInstantsFor: dstTimeZone.getPossibleInstantsFor,
+});
+
+const plainDate = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2000,
+ month: 1,
+ monthCode: "M01",
+ day: 1,
+ calendar,
+}, "item.plainDate");
+instance.toZonedDateTime(TemporalHelpers.propertyBagObserver(actual, {
+ plainDate,
+ timeZone,
+}, "item"));
+assert.compareArray(actual, expected, "order of operations at normal wall-clock time");
+actual.splice(0); // clear
+
+const fallBackPlainDate = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2000,
+ month: 10,
+ monthCode: "M10",
+ day: 29,
+ calendar,
+}, "item.plainDate");
+const fallBackInstance = new Temporal.PlainTime(1, 30);
+fallBackInstance.toZonedDateTime(TemporalHelpers.propertyBagObserver(actual, {
+ plainDate: fallBackPlainDate,
+ timeZone,
+}, "item"));
+assert.compareArray(actual, expected, "order of operations at repeated wall-clock time");
+actual.splice(0); // clear
+
+const springForwardPlainDate = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2000,
+ month: 4,
+ monthCode: "M04",
+ day: 2,
+ calendar,
+}, "item.plainDate");
+instance.toZonedDateTime(TemporalHelpers.propertyBagObserver(actual, {
+ plainDate: springForwardPlainDate,
+ timeZone,
+}, "item"));
+assert.compareArray(actual, expected.concat([
+ "call item.timeZone.getOffsetNanosecondsFor",
+ "call item.timeZone.getOffsetNanosecondsFor",
+ "call item.timeZone.getPossibleInstantsFor",
+]), "order of operations at skipped wall-clock time");
+actual.splice(0); // clear
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/plaindate-infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/plaindate-infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..6df15bc12c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/plaindate-infinity-throws-rangeerror.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.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plaintime.prototype.tozoneddatetime
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(15);
+const base = { year: 2000, month: 5, day: 2 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.toZonedDateTime({ timeZone: "UTC", plainDate: { ...base, [prop]: inf } }), `${prop} property cannot be ${inf} in plainDate`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.toZonedDateTime({ timeZone: "UTC", plainDate: { ...base, [prop]: obj } }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/prop-desc.js
new file mode 100644
index 0000000000..354de10328
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/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.tozoneddatetime
+description: The "toZonedDateTime" property of Temporal.PlainTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainTime.prototype.toZonedDateTime,
+ "function",
+ "`typeof PlainTime.prototype.toZonedDateTime` is `function`"
+);
+
+verifyProperty(Temporal.PlainTime.prototype, "toZonedDateTime", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/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/toZonedDateTime/timezone-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-case-insensitive.js
new file mode 100644
index 0000000000..1c276cdc60
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-case-insensitive.js
@@ -0,0 +1,17 @@
+// |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.tozoneddatetime
+description: Time zone names are case insensitive
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime();
+
+const timeZone = 'uTc';
+const result = instance.toZonedDateTime({ plainDate: new Temporal.PlainDate(2000, 5, 2), timeZone });
+assert.sameValue(result.timeZoneId, 'UTC', `Time zone created from string "${timeZone}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..ef8f207249
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-non-integer.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.tozoneddatetime
+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 plainDate = new Temporal.PlainDate(2000, 5, 2);
+ timeZone.getPossibleInstantsFor = function () {
+ return [];
+ };
+ assert.throws(RangeError, () => time.toZonedDateTime({ plainDate, timeZone }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..224ce6a5ed
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-not-callable.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.tozoneddatetime
+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 plainDate = new Temporal.PlainDate(2000, 5, 2);
+ timeZone.getPossibleInstantsFor = function () {
+ return [];
+ };
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => time.toZonedDateTime({ plainDate, timeZone }),
+ `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/toZonedDateTime/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..522e2c7b0b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-out-of-range.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.tozoneddatetime
+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 plainDate = new Temporal.PlainDate(2000, 5, 2);
+ timeZone.getPossibleInstantsFor = function () {
+ return [];
+ };
+ assert.throws(RangeError, () => time.toZonedDateTime({ plainDate, timeZone }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..aafa053af7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,31 @@
+// |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.tozoneddatetime
+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 plainDate = new Temporal.PlainDate(2000, 5, 2);
+ timeZone.getPossibleInstantsFor = function () {
+ return [];
+ };
+ assert.throws(TypeError, () => time.toZonedDateTime({ plainDate, timeZone }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-getpossibleinstantsfor-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-getpossibleinstantsfor-iterable.js
new file mode 100644
index 0000000000..78c729ac0f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-getpossibleinstantsfor-iterable.js
@@ -0,0 +1,45 @@
+// |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.tozoneddatetime
+description: An iterable returned from timeZone.getPossibleInstantsFor is consumed after each call
+info: |
+ sec-temporal.plaintime.prototype.tozoneddatetime step 10:
+ 10. Let _instant_ be ? BuiltinTimeZoneGetInstantFor(_timeZone_, _temporalDateTime_, *"compatible"*).
+ sec-temporal-builtintimezonegetinstantfor step 1:
+ 1. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_).
+ sec-temporal-builtintimezonegetinstantfor step 14:
+ 14. Assert: _disambiguation_ is *"compatible"* or *"later"*.
+ sec-temporal-builtintimezonegetinstantfor step 16:
+ 16. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _later_).
+ sec-temporal-getpossibleinstantsfor step 2:
+ 2. Let _list_ be ? IterableToList(_possibleInstants_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected1 = [
+ "2000-05-02T12:34:56.987654321",
+];
+
+TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => {
+ const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+ time.toZonedDateTime({ plainDate: new Temporal.PlainDate(2000, 5, 2), timeZone });
+}, expected1);
+
+// Same, but test the other path where the time doesn't exist and
+// GetPossibleInstantsFor is called again on a later time
+
+const expected2 = [
+ "2030-01-01T00:30:00",
+ "2030-01-01T01:30:00",
+];
+
+TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => {
+ const time = new Temporal.PlainTime(0, 30);
+ time.toZonedDateTime({ plainDate: new Temporal.PlainDate(2030, 1, 1), timeZone });
+}, expected2);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-string-datetime.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-string-datetime.js
new file mode 100644
index 0000000000..cce8691a13
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-string-datetime.js
@@ -0,0 +1,65 @@
+// |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.tozoneddatetime
+description: Conversion of ISO date-time strings to Temporal.TimeZone instances
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime();
+
+let timeZone = "2021-08-19T17:30";
+assert.throws(RangeError, () => instance.toZonedDateTime({ plainDate: new Temporal.PlainDate(2000, 5, 2), timeZone }), "bare date-time string is not a time zone");
+
+[
+ "2021-08-19T17:30-07:00:01",
+ "2021-08-19T17:30-07:00:00",
+ "2021-08-19T17:30-07:00:00.1",
+ "2021-08-19T17:30-07:00:00.0",
+ "2021-08-19T17:30-07:00:00.01",
+ "2021-08-19T17:30-07:00:00.00",
+ "2021-08-19T17:30-07:00:00.001",
+ "2021-08-19T17:30-07:00:00.000",
+ "2021-08-19T17:30-07:00:00.0001",
+ "2021-08-19T17:30-07:00:00.0000",
+ "2021-08-19T17:30-07:00:00.00001",
+ "2021-08-19T17:30-07:00:00.00000",
+ "2021-08-19T17:30-07:00:00.000001",
+ "2021-08-19T17:30-07:00:00.000000",
+ "2021-08-19T17:30-07:00:00.0000001",
+ "2021-08-19T17:30-07:00:00.0000000",
+ "2021-08-19T17:30-07:00:00.00000001",
+ "2021-08-19T17:30-07:00:00.00000000",
+ "2021-08-19T17:30-07:00:00.000000001",
+ "2021-08-19T17:30-07:00:00.000000000",
+].forEach((timeZone) => {
+ assert.throws(
+ RangeError,
+ () => instance.toZonedDateTime({ plainDate: new Temporal.PlainDate(2000, 5, 2), timeZone }),
+ `ISO string ${timeZone} with a sub-minute offset is not a valid time zone`
+ );
+});
+
+timeZone = "2021-08-19T17:30Z";
+const result1 = instance.toZonedDateTime({ plainDate: new Temporal.PlainDate(2000, 5, 2), timeZone });
+assert.sameValue(result1.timeZoneId, "UTC", "date-time + Z is UTC time zone");
+
+timeZone = "2021-08-19T17:30-07:00";
+const result2 = instance.toZonedDateTime({ plainDate: new Temporal.PlainDate(2000, 5, 2), timeZone });
+assert.sameValue(result2.timeZoneId, "-07:00", "date-time + offset is the offset time zone");
+
+timeZone = "2021-08-19T17:30[UTC]";
+const result3 = instance.toZonedDateTime({ plainDate: new Temporal.PlainDate(2000, 5, 2), timeZone });
+assert.sameValue(result3.timeZoneId, "UTC", "date-time + IANA annotation is the IANA time zone");
+
+timeZone = "2021-08-19T17:30Z[UTC]";
+const result4 = instance.toZonedDateTime({ plainDate: new Temporal.PlainDate(2000, 5, 2), timeZone });
+assert.sameValue(result4.timeZoneId, "UTC", "date-time + Z + IANA annotation is the IANA time zone");
+
+timeZone = "2021-08-19T17:30-07:00[UTC]";
+const result5 = instance.toZonedDateTime({ plainDate: new Temporal.PlainDate(2000, 5, 2), timeZone });
+assert.sameValue(result5.timeZoneId, "UTC", "date-time + offset + IANA annotation is the IANA time zone");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-string-leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-string-leap-second.js
new file mode 100644
index 0000000000..b9ffeaf896
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-string-leap-second.js
@@ -0,0 +1,20 @@
+// |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.tozoneddatetime
+description: Leap second is a valid ISO string for TimeZone
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime();
+let timeZone = "2016-12-31T23:59:60+00:00[UTC]";
+
+const result = instance.toZonedDateTime({ plainDate: new Temporal.PlainDate(2000, 5, 2), timeZone });
+assert.sameValue(result.timeZoneId, "UTC", "leap second is a valid ISO string for TimeZone");
+
+timeZone = "2021-08-19T17:30:45.123456789+23:59[+23:59:60]";
+assert.throws(RangeError, () => instance.toZonedDateTime({ plainDate: new Temporal.PlainDate(2000, 5, 2), timeZone }), "leap second in time zone name not valid");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-string-multiple-offsets.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-string-multiple-offsets.js
new file mode 100644
index 0000000000..7891dfd8b0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-string-multiple-offsets.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.tozoneddatetime
+description: Time zone parsing from ISO strings uses the bracketed offset, not the ISO string offset
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime();
+const timeZone = "2021-08-19T17:30:45.123456789-12:12[+01:46]";
+
+const result = instance.toZonedDateTime({ plainDate: new Temporal.PlainDate(2000, 5, 2), timeZone });
+assert.sameValue(result.timeZoneId, "+01:46", "Time zone string determined from bracket name");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-string-year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-string-year-zero.js
new file mode 100644
index 0000000000..e89a158fab
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-string-year-zero.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.tozoneddatetime
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+const instance = new Temporal.PlainTime();
+invalidStrings.forEach((timeZone) => {
+ assert.throws(
+ RangeError,
+ () => instance.toZonedDateTime({ plainDate: new Temporal.PlainDate(2000, 5, 2), timeZone }),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-string.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-string.js
new file mode 100644
index 0000000000..1dfee59178
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-string.js
@@ -0,0 +1,39 @@
+// |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.tozoneddatetime
+description: Time zone IDs are valid input for a time zone
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getPossibleInstantsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getPossibleInstantsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be looked up");
+ },
+});
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainTime();
+
+["UTC", "+01:30"].forEach((timeZone) => {
+ const result = instance.toZonedDateTime({ plainDate: new Temporal.PlainDate(2000, 5, 2), timeZone });
+ assert.sameValue(result.getISOFields().timeZone, timeZone, `time zone slot should store string "${timeZone}"`);
+});
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", getPossibleInstantsForOriginal);
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-wrong-type.js
new file mode 100644
index 0000000000..8dbf4e45dd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-wrong-type.js
@@ -0,0 +1,42 @@
+// |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.tozoneddatetime
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or object for TimeZone
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.PlainTime();
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [19761118, "number that would convert to a valid ISO string in other contexts"],
+ [1n, "bigint"],
+];
+
+for (const [timeZone, description] of primitiveTests) {
+ assert.throws(
+ typeof timeZone === 'string' ? RangeError : TypeError,
+ () => instance.toZonedDateTime({ plainDate: new Temporal.PlainDate(2000, 5, 2), timeZone }),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "object not implementing time zone protocol"],
+ [new Temporal.Calendar("iso8601"), "calendar instance"],
+];
+
+for (const [timeZone, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.toZonedDateTime({ plainDate: new Temporal.PlainDate(2000, 5, 2), timeZone }), `${description} is not a valid object and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/year-zero.js
new file mode 100644
index 0000000000..fd73928276
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/year-zero.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.tozoneddatetime
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T00:45",
+ "-000000-10-31T00:45+01:00",
+ "-000000-10-31T00:45+00:00[UTC]",
+];
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.toZonedDateTime({ plainDate: arg, timeZone: "UTC" }),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-cast.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-cast.js
new file mode 100644
index 0000000000..8fae64634f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+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.until("16:34"),
+ 0, 0, 0, 0, /* hours = */ 1, /* minutes = */ 10, /* seconds = */ 29, 876, 543, 211, "string");
+TemporalHelpers.assertDuration(plainTime.until({ hour: 16, minute: 34 }),
+ 0, 0, 0, 0, /* hours = */ 1, /* minutes = */ 10, /* seconds = */ 29, 876, 543, 211, "object");
+
+assert.throws(TypeError, () => plainTime.until({}), "empty");
+assert.throws(TypeError, () => plainTime.until({ minutes: 30 }), "only plural 'minutes'");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-number.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-number.js
new file mode 100644
index 0000000000..184cc6609e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+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.until(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/until/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..5131007c25
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+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.until(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/until/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..8f5566b2a0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+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.until(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..1fbc1405b8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+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.until(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.until(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/until/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..60669f4cb5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+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.until(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/until/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..d98c81ecf6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+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.until(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-no-implicit-midnight.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-no-implicit-midnight.js
new file mode 100644
index 0000000000..6d0b98e8e5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+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.until(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/until/argument-string-time-designator-required-for-disambiguation.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-time-designator-required-for-disambiguation.js
new file mode 100644
index 0000000000..4c73919116
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+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.until(arg),
+ `'${arg}' is ambiguous and requires T prefix`
+ );
+ // The same string with a T prefix should not throw:
+ arg = `T${string}`;
+ instance.until(arg);
+
+ arg = ` ${string}`;
+ assert.throws(
+ RangeError,
+ () => instance.until(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.until(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-time-separators.js
new file mode 100644
index 0000000000..176b7c51a8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+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.until(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/until/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..7f01564d22
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+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.until(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/until/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..7cac3ea843
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+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.until(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/until/argument-string-with-time-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-with-time-designator.js
new file mode 100644
index 0000000000..1e481da34d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+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.until(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/until/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..562616ef60
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+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.until(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/until/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-wrong-type.js
new file mode 100644
index 0000000000..b585ea57c8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+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.until(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.until(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/until/argument-zoneddatetime-balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-zoneddatetime-balance-negative-time-units.js
new file mode 100644
index 0000000000..efb80edab9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+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.until 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().until(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/until/argument-zoneddatetime-negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-zoneddatetime-negative-epochnanoseconds.js
new file mode 100644
index 0000000000..f9d9b3ad92
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+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.until(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/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..244af3a92d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+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.until(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..14523f2df6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+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.until(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/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..65a1b4997c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+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.until(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..03a61ee229
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+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.until(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/balance-negative-time-units.js
new file mode 100644
index 0000000000..6dc665fbc0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+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.until step 11:
+ 11. Let _result_ be ! DifferenceTime(_temporalTime_.[[ISOHour]], _temporalTime_.[[ISOMinute]], _temporalTime_.[[ISOSecond]], _temporalTime_.[[ISOMillisecond]], _temporalTime_.[[ISOMicrosecond]], _temporalTime_.[[ISONanosecond]], _other_.[[ISOHour]], _other_.[[ISOMinute]], _other_.[[ISOSecond]], _other_.[[ISOMillisecond]], _other_.[[ISOMicrosecond]], _other_.[[ISONanosecond]]).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(1, 1, 1, 1, 1, 1);
+
+const result1 = new Temporal.PlainTime(0, 0, 0, 0, 0, 2).until(time);
+TemporalHelpers.assertDuration(result1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 999, "nanoseconds balance");
+
+const result2 = new Temporal.PlainTime(0, 0, 0, 0, 2).until(time);
+TemporalHelpers.assertDuration(result2, 0, 0, 0, 0, 1, 1, 1, 0, 999, 1, "microseconds balance");
+
+const result3 = new Temporal.PlainTime(0, 0, 0, 2).until(time);
+TemporalHelpers.assertDuration(result3, 0, 0, 0, 0, 1, 1, 0, 999, 1, 1, "milliseconds balance");
+
+const result4 = new Temporal.PlainTime(0, 0, 2).until(time);
+TemporalHelpers.assertDuration(result4, 0, 0, 0, 0, 1, 0, 59, 1, 1, 1, "seconds balance");
+
+const result5 = new Temporal.PlainTime(0, 2).until(time);
+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 = new Temporal.PlainTime(2).until(time);
+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/until/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/basic.js
new file mode 100644
index 0000000000..525652c204
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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(16, 23, 30, 123, 456, 789);
+const three = new Temporal.PlainTime(17, 0, 30, 123, 456, 789);
+
+TemporalHelpers.assertDuration(one.until(two),
+ 0, 0, 0, 0, /* hours = */ 1, 0, 0, 0, 0, 0);
+TemporalHelpers.assertDuration(two.until(one),
+ 0, 0, 0, 0, /* hours = */ -1, 0, 0, 0, 0, 0);
+TemporalHelpers.assertDuration(one.until(three),
+ 0, 0, 0, 0, /* hours = */ 1, 37, 0, 0, 0, 0);
+TemporalHelpers.assertDuration(three.until(one),
+ 0, 0, 0, 0, /* hours = */ -1, -37, 0, 0, 0, 0);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/branding.js
new file mode 100644
index 0000000000..58e1b3fa75
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const until = Temporal.PlainTime.prototype.until;
+
+assert.sameValue(typeof until, "function");
+
+const args = [new Temporal.PlainTime(12)];
+
+assert.throws(TypeError, () => until.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => until.apply(null, args), "null");
+assert.throws(TypeError, () => until.apply(true, args), "true");
+assert.throws(TypeError, () => until.apply("", args), "empty string");
+assert.throws(TypeError, () => until.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => until.apply(1, args), "1");
+assert.throws(TypeError, () => until.apply({}, args), "plain object");
+assert.throws(TypeError, () => until.apply(Temporal.PlainTime, args), "Temporal.PlainTime");
+assert.throws(TypeError, () => until.apply(Temporal.PlainTime.prototype, args), "Temporal.PlainTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/builtin.js
new file mode 100644
index 0000000000..d0e21624e8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+description: >
+ Tests that Temporal.PlainTime.prototype.until
+ 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.until),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainTime.prototype.until),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainTime.prototype.until),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainTime.prototype.until.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/largestunit-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/largestunit-invalid-string.js
new file mode 100644
index 0000000000..0284618463
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+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, () => earlier.until(later, { largestUnit }),
+ `"${largestUnit}" is not a valid value for largestUnit`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/largestunit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/largestunit-plurals-accepted.js
new file mode 100644
index 0000000000..4cd50f0195
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+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) => earlier.until(later, { largestUnit }), validUnits);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/largestunit-smallestunit-mismatch.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/largestunit-smallestunit-mismatch.js
new file mode 100644
index 0000000000..2dd6528a00
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+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, () => earlier.until(later, { largestUnit, smallestUnit }));
+ }
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/largestunit-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/largestunit-undefined.js
new file mode 100644
index 0000000000..07a593980f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+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 = earlier.until(later, { largestUnit: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 0, 1, 1, 1, 987, 654, 321, "default largestUnit is hour");
+const implicit = earlier.until(later, {});
+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/until/largestunit-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/largestunit-wrong-type.js
new file mode 100644
index 0000000000..a685d4cd0a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+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) => earlier.until(later, { 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/until/largestunit.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/largestunit.js
new file mode 100644
index 0000000000..3f84e1be5a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/largestunit.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.until
+description: PlainTime.until 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(fourFortyEight.until(elevenFiftyNine), 0, 0, 0, 0, 7, 11, 3, 0, 0, 0, "does not include higher units than necessary (largest unit unspecified)");
+TemporalHelpers.assertDuration(fourFortyEight.until(elevenFiftyNine, { 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(fourFortyEight.until(elevenFiftyNine, { 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(fourFortyEight.until(elevenFiftyNine, { 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(fourFortyEight.until(elevenFiftyNine, { 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/until/leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/leap-second.js
new file mode 100644
index 0000000000..a3397d4d39
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+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.until(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.until(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/until/length.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/length.js
new file mode 100644
index 0000000000..ebb5736dfa
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/length.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.until
+description: Temporal.PlainTime.prototype.until.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.until, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/name.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/name.js
new file mode 100644
index 0000000000..603d27dcac
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+description: Temporal.PlainTime.prototype.until.name is "until".
+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.until, "name", {
+ value: "until",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/not-a-constructor.js
new file mode 100644
index 0000000000..5b4e7dcc2f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+description: >
+ Temporal.PlainTime.prototype.until 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.until();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainTime.prototype.until), false,
+ "isConstructor(Temporal.PlainTime.prototype.until)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/options-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/options-invalid.js
new file mode 100644
index 0000000000..2340eefee9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+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.until(one, badOptions));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/options-object.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/options-object.js
new file mode 100644
index 0000000000..25cc69bca1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+description: Empty or a function object may be used as options
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime();
+
+const result1 = instance.until(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.until(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/until/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/options-undefined.js
new file mode 100644
index 0000000000..c7f622e77a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+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 = earlier.until(later, 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 = earlier.until(later);
+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/until/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/options-wrong-type.js
new file mode 100644
index 0000000000..6706e49ba3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+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.until(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/until/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/order-of-operations.js
new file mode 100644
index 0000000000..35a10fcd59
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+description: Properties on an object passed to until() 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.until(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.until(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/until/plaintime-propertybag-no-time-units.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/plaintime-propertybag-no-time-units.js
new file mode 100644
index 0000000000..2197e3a558
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+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.until(props), "TypeError if no properties are present");
+
+props.minute = 30;
+const result = instance.until(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/until/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/prop-desc.js
new file mode 100644
index 0000000000..6c8038cdcd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+description: The "until" property of Temporal.PlainTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainTime.prototype.until,
+ "function",
+ "`typeof PlainTime.prototype.until` is `function`"
+);
+
+verifyProperty(Temporal.PlainTime.prototype, "until", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/result-sub-second.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/result-sub-second.js
new file mode 100644
index 0000000000..52fdbde50f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+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(time1.until(time2, { largestUnit: "milliseconds" }),
+ 0, 0, 0, 0, 0, 0, 0, /* milliseconds = */ 24762250, 250, 250, "milliseconds");
+
+TemporalHelpers.assertDuration(time1.until(time2, { largestUnit: "microseconds" }),
+ 0, 0, 0, 0, 0, 0, 0, /* milliseconds = */ 0, 24762250250, 250, "microseconds");
+
+TemporalHelpers.assertDuration(time1.until(time2, { 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/until/round-cross-unit-boundary.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/round-cross-unit-boundary.js
new file mode 100644
index 0000000000..faef88b1dc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+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.until(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/until/roundingincrement-hours.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-hours.js
new file mode 100644
index 0000000000..01ed2f19bd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+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(
+ earlier.until(later, { smallestUnit: "hours", roundingIncrement: 1 }),
+ 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, "hours");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "hours", roundingIncrement: 2 }),
+ 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, "hours");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "hours", roundingIncrement: 3 }),
+ 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, "hours");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "hours", roundingIncrement: 4 }),
+ 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, "hours");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "hours", roundingIncrement: 6 }),
+ 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, "hours");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "hours", roundingIncrement: 8 }),
+ 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, "hours");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { 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/until/roundingincrement-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-invalid.js
new file mode 100644
index 0000000000..4037f566ca
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+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, () => earlier.until(later, { smallestUnit: "hours", roundingIncrement: 11 }));
+assert.throws(RangeError, () => earlier.until(later, { smallestUnit: "minutes", roundingIncrement: 29 }));
+assert.throws(RangeError, () => earlier.until(later, { smallestUnit: "seconds", roundingIncrement: 29 }));
+assert.throws(RangeError, () => earlier.until(later, { smallestUnit: "milliseconds", roundingIncrement: 29 }));
+assert.throws(RangeError, () => earlier.until(later, { smallestUnit: "microseconds", roundingIncrement: 29 }));
+assert.throws(RangeError, () => earlier.until(later, { smallestUnit: "nanoseconds", roundingIncrement: 29 }));
+assert.throws(RangeError, () => earlier.until(later, { smallestUnit: "hours", roundingIncrement: 24 }));
+assert.throws(RangeError, () => earlier.until(later, { smallestUnit: "minutes", roundingIncrement: 60 }));
+assert.throws(RangeError, () => earlier.until(later, { smallestUnit: "seconds", roundingIncrement: 60 }));
+assert.throws(RangeError, () => earlier.until(later, { smallestUnit: "milliseconds", roundingIncrement: 1000 }));
+assert.throws(RangeError, () => earlier.until(later, { smallestUnit: "microseconds", roundingIncrement: 1000 }));
+assert.throws(RangeError, () => earlier.until(later, { smallestUnit: "nanoseconds", roundingIncrement: 1000 }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-microseconds.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-microseconds.js
new file mode 100644
index 0000000000..3ae7e1aea5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+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(
+ earlier.until(later, { smallestUnit: "microseconds", roundingIncrement: 1 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "microseconds", roundingIncrement: 2 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "microseconds", roundingIncrement: 4 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 196, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "microseconds", roundingIncrement: 5 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 195, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "microseconds", roundingIncrement: 8 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 192, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "microseconds", roundingIncrement: 10 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 190, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "microseconds", roundingIncrement: 20 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 180, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "microseconds", roundingIncrement: 25 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 175, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "microseconds", roundingIncrement: 40 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 160, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "microseconds", roundingIncrement: 50 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 150, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "microseconds", roundingIncrement: 100 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 100, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "microseconds", roundingIncrement: 125 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 125, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "microseconds", roundingIncrement: 200 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 0, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "microseconds", roundingIncrement: 250 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 0, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { 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/until/roundingincrement-milliseconds.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-milliseconds.js
new file mode 100644
index 0000000000..d24a0b8acf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+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(
+ earlier.until(later, { smallestUnit: "milliseconds", roundingIncrement: 1 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "milliseconds", roundingIncrement: 2 }),
+ 0, 0, 0, 0, 10, 35, 23, 864, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "milliseconds", roundingIncrement: 4 }),
+ 0, 0, 0, 0, 10, 35, 23, 864, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "milliseconds", roundingIncrement: 5 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "milliseconds", roundingIncrement: 8 }),
+ 0, 0, 0, 0, 10, 35, 23, 864, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "milliseconds", roundingIncrement: 10 }),
+ 0, 0, 0, 0, 10, 35, 23, 860, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "milliseconds", roundingIncrement: 20 }),
+ 0, 0, 0, 0, 10, 35, 23, 860, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "milliseconds", roundingIncrement: 25 }),
+ 0, 0, 0, 0, 10, 35, 23, 850, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "milliseconds", roundingIncrement: 40 }),
+ 0, 0, 0, 0, 10, 35, 23, 840, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "milliseconds", roundingIncrement: 50 }),
+ 0, 0, 0, 0, 10, 35, 23, 850, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "milliseconds", roundingIncrement: 100 }),
+ 0, 0, 0, 0, 10, 35, 23, 800, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "milliseconds", roundingIncrement: 125 }),
+ 0, 0, 0, 0, 10, 35, 23, 750, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "milliseconds", roundingIncrement: 200 }),
+ 0, 0, 0, 0, 10, 35, 23, 800, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "milliseconds", roundingIncrement: 250 }),
+ 0, 0, 0, 0, 10, 35, 23, 750, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { 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/until/roundingincrement-minutes.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-minutes.js
new file mode 100644
index 0000000000..5f98541718
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+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(
+ earlier.until(later, { smallestUnit: "minutes", roundingIncrement: 1 }),
+ 0, 0, 0, 0, 10, 35, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "minutes", roundingIncrement: 2 }),
+ 0, 0, 0, 0, 10, 34, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "minutes", roundingIncrement: 3 }),
+ 0, 0, 0, 0, 10, 33, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "minutes", roundingIncrement: 4 }),
+ 0, 0, 0, 0, 10, 32, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "minutes", roundingIncrement: 5 }),
+ 0, 0, 0, 0, 10, 35, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "minutes", roundingIncrement: 6 }),
+ 0, 0, 0, 0, 10, 30, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "minutes", roundingIncrement: 10 }),
+ 0, 0, 0, 0, 10, 30, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "minutes", roundingIncrement: 12 }),
+ 0, 0, 0, 0, 10, 24, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "minutes", roundingIncrement: 15 }),
+ 0, 0, 0, 0, 10, 30, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "minutes", roundingIncrement: 20 }),
+ 0, 0, 0, 0, 10, 20, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { 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/until/roundingincrement-nan.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-nan.js
new file mode 100644
index 0000000000..5da9205585
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+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.until step 10:
+ 10. 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, () => earlier.until(later, { roundingIncrement: NaN }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-nanoseconds.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-nanoseconds.js
new file mode 100644
index 0000000000..59c55f6b84
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+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(
+ earlier.until(later, { smallestUnit: "nanoseconds", roundingIncrement: 1 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 533, "nanoseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "nanoseconds", roundingIncrement: 2 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 532, "nanoseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "nanoseconds", roundingIncrement: 4 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 532, "nanoseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "nanoseconds", roundingIncrement: 5 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 530, "nanoseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "nanoseconds", roundingIncrement: 8 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 528, "nanoseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "nanoseconds", roundingIncrement: 10 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 530, "nanoseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "nanoseconds", roundingIncrement: 20 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 520, "nanoseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "nanoseconds", roundingIncrement: 25 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 525, "nanoseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "nanoseconds", roundingIncrement: 40 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 520, "nanoseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "nanoseconds", roundingIncrement: 50 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 500, "nanoseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "nanoseconds", roundingIncrement: 100 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 500, "nanoseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "nanoseconds", roundingIncrement: 125 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 500, "nanoseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "nanoseconds", roundingIncrement: 200 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 400, "nanoseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "nanoseconds", roundingIncrement: 250 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 500, "nanoseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { 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/until/roundingincrement-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-non-integer.js
new file mode 100644
index 0000000000..413aa6a02c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+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 = earlier.until(later, { roundingIncrement: 2.5, roundingMode: "trunc" });
+TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, "roundingIncrement 2.5 truncates to 2");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-out-of-range.js
new file mode 100644
index 0000000000..1c901e5176
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+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, () => earlier.until(later, { roundingIncrement: -Infinity }));
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: -1 }));
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: 0 }));
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: 0.9 }));
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: 1e9 + 1 }));
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: Infinity }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-seconds.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-seconds.js
new file mode 100644
index 0000000000..2a26e524cb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+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(
+ earlier.until(later, { smallestUnit: "seconds", roundingIncrement: 1 }),
+ 0, 0, 0, 0, 10, 35, 23, 0, 0, 0, "seconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "seconds", roundingIncrement: 2 }),
+ 0, 0, 0, 0, 10, 35, 22, 0, 0, 0, "seconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "seconds", roundingIncrement: 3 }),
+ 0, 0, 0, 0, 10, 35, 21, 0, 0, 0, "seconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "seconds", roundingIncrement: 4 }),
+ 0, 0, 0, 0, 10, 35, 20, 0, 0, 0, "seconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "seconds", roundingIncrement: 5 }),
+ 0, 0, 0, 0, 10, 35, 20, 0, 0, 0, "seconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "seconds", roundingIncrement: 6 }),
+ 0, 0, 0, 0, 10, 35, 18, 0, 0, 0, "seconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "seconds", roundingIncrement: 10 }),
+ 0, 0, 0, 0, 10, 35, 20, 0, 0, 0, "seconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "seconds", roundingIncrement: 12 }),
+ 0, 0, 0, 0, 10, 35, 12, 0, 0, 0, "seconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "seconds", roundingIncrement: 15 }),
+ 0, 0, 0, 0, 10, 35, 15, 0, 0, 0, "seconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "seconds", roundingIncrement: 20 }),
+ 0, 0, 0, 0, 10, 35, 20, 0, 0, 0, "seconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { 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/until/roundingincrement-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-undefined.js
new file mode 100644
index 0000000000..46a39fa264
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+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.until step 10:
+ 10. 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 = earlier.until(later, { roundingIncrement: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, "default roundingIncrement is 1");
+
+const implicit = earlier.until(later, {});
+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/until/roundingincrement-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-wrong-type.js
new file mode 100644
index 0000000000..d247679031
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+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.until step 10:
+ 10. 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) => earlier.until(later, { 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/until/roundingmode-ceil.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-ceil.js
new file mode 100644
index 0000000000..df3a7ee979
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+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(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { 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/until/roundingmode-expand.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-expand.js
new file mode 100644
index 0000000000..705de9a868
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+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(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { 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/until/roundingmode-floor.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-floor.js
new file mode 100644
index 0000000000..11270082d7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+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(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { 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/until/roundingmode-halfCeil.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-halfCeil.js
new file mode 100644
index 0000000000..d987d5c4a4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+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(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { 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/until/roundingmode-halfEven.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-halfEven.js
new file mode 100644
index 0000000000..239e861107
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+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(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { 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/until/roundingmode-halfExpand.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-halfExpand.js
new file mode 100644
index 0000000000..a8e5f26aef
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+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(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { 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/until/roundingmode-halfFloor.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-halfFloor.js
new file mode 100644
index 0000000000..0908a4bd68
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+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(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { 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/until/roundingmode-halfTrunc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-halfTrunc.js
new file mode 100644
index 0000000000..e5bae72f9d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+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(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { 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/until/roundingmode-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-invalid-string.js
new file mode 100644
index 0000000000..76598d4a3d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+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, () => earlier.until(later, { smallestUnit: "microsecond", roundingMode }));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-trunc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-trunc.js
new file mode 100644
index 0000000000..a1f609d8d1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+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(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { 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/until/roundingmode-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-undefined.js
new file mode 100644
index 0000000000..c6cef9687a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+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(
+ earlier.until(later, { smallestUnit: "hours", roundingMode: undefined }),
+ 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, "hours");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "hours" }),
+ 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, "hours");
+TemporalHelpers.assertDuration(
+ later.until(earlier, { smallestUnit: "hours", roundingMode: undefined }),
+ 0, 0, 0, 0, -4, 0, 0, 0, 0, 0, "hours");
+TemporalHelpers.assertDuration(
+ later.until(earlier, { smallestUnit: "hours" }),
+ 0, 0, 0, 0, -4, 0, 0, 0, 0, 0, "hours");
+
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "minutes", roundingMode: undefined }),
+ 0, 0, 0, 0, 4, 17, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "minutes" }),
+ 0, 0, 0, 0, 4, 17, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertDuration(
+ later.until(earlier, { smallestUnit: "minutes", roundingMode: undefined }),
+ 0, 0, 0, 0, -4, -17, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertDuration(
+ later.until(earlier, { smallestUnit: "minutes" }),
+ 0, 0, 0, 0, -4, -17, 0, 0, 0, 0, "minutes");
+
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "seconds", roundingMode: undefined }),
+ 0, 0, 0, 0, 4, 17, 4, 0, 0, 0, "seconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "seconds" }),
+ 0, 0, 0, 0, 4, 17, 4, 0, 0, 0, "seconds");
+TemporalHelpers.assertDuration(
+ later.until(earlier, { smallestUnit: "seconds", roundingMode: undefined }),
+ 0, 0, 0, 0, -4, -17, -4, 0, 0, 0, "seconds");
+TemporalHelpers.assertDuration(
+ later.until(earlier, { smallestUnit: "seconds" }),
+ 0, 0, 0, 0, -4, -17, -4, 0, 0, 0, "seconds");
+
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "milliseconds", roundingMode: undefined }),
+ 0, 0, 0, 0, 4, 17, 4, 864, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "milliseconds" }),
+ 0, 0, 0, 0, 4, 17, 4, 864, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ later.until(earlier, { smallestUnit: "milliseconds", roundingMode: undefined }),
+ 0, 0, 0, 0, -4, -17, -4, -864, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ later.until(earlier, { smallestUnit: "milliseconds" }),
+ 0, 0, 0, 0, -4, -17, -4, -864, 0, 0, "milliseconds");
+
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "microseconds", roundingMode: undefined }),
+ 0, 0, 0, 0, 4, 17, 4, 864, 197, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "microseconds" }),
+ 0, 0, 0, 0, 4, 17, 4, 864, 197, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ later.until(earlier, { smallestUnit: "microseconds", roundingMode: undefined }),
+ 0, 0, 0, 0, -4, -17, -4, -864, -197, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ later.until(earlier, { smallestUnit: "microseconds" }),
+ 0, 0, 0, 0, -4, -17, -4, -864, -197, 0, "microseconds");
+
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "nanoseconds", roundingMode: undefined }),
+ 0, 0, 0, 0, 4, 17, 4, 864, 197, 532, "nanoseconds");
+TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit: "nanoseconds" }),
+ 0, 0, 0, 0, 4, 17, 4, 864, 197, 532, "nanoseconds");
+TemporalHelpers.assertDuration(
+ later.until(earlier, { smallestUnit: "nanoseconds", roundingMode: undefined }),
+ 0, 0, 0, 0, -4, -17, -4, -864, -197, -532, "nanoseconds");
+TemporalHelpers.assertDuration(
+ later.until(earlier, { 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/until/roundingmode-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/roundingmode-wrong-type.js
new file mode 100644
index 0000000000..80fe5287ae
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+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) => earlier.until(later, { 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/until/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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/until/smallestunit-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/smallestunit-invalid-string.js
new file mode 100644
index 0000000000..64101bd085
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+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, () => earlier.until(later, { 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/until/smallestunit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/smallestunit-plurals-accepted.js
new file mode 100644
index 0000000000..7fa86c19b3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+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) => earlier.until(later, { smallestUnit }), validUnits);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/smallestunit-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/smallestunit-undefined.js
new file mode 100644
index 0000000000..e32c3a81ea
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+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 = earlier.until(later, { smallestUnit: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 0, 1, 1, 1, 987, 654, 321, "default smallestUnit is nanosecond");
+const implicit = earlier.until(later, {});
+TemporalHelpers.assertDuration(implicit, 0, 0, 0, 0, 1, 1, 1, 987, 654, 321, "default smallestUnit is nanosecond");
+const lambda = earlier.until(later, () => {});
+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/until/smallestunit-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/smallestunit-wrong-type.js
new file mode 100644
index 0000000000..a840df2f93
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+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) => earlier.until(later, { 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/until/year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/year-zero.js
new file mode 100644
index 0000000000..2cd3a81649
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/until/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.until
+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.until(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/basic.js
new file mode 100644
index 0000000000..1b9dc24a7d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/basic.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.valueof
+description: Basic tests for valueOf().
+features: [Temporal]
+---*/
+
+const plainTime = Temporal.PlainTime.from("09:36:29.123456789");
+const plainTime2 = Temporal.PlainTime.from("09:36:29.123456789");
+
+assert.throws(TypeError, () => plainTime.valueOf(), "valueOf");
+assert.throws(TypeError, () => plainTime < plainTime, "<");
+assert.throws(TypeError, () => plainTime <= plainTime, "<=");
+assert.throws(TypeError, () => plainTime > plainTime, ">");
+assert.throws(TypeError, () => plainTime >= plainTime, ">=");
+assert.sameValue(plainTime === plainTime, true, "===");
+assert.sameValue(plainTime === plainTime2, false, "===");
+assert.sameValue(plainTime !== plainTime, false, "!==");
+assert.sameValue(plainTime !== plainTime2, true, "!==");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/branding.js
new file mode 100644
index 0000000000..c877532a8a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/branding.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.valueof
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const valueOf = Temporal.PlainTime.prototype.valueOf;
+
+assert.sameValue(typeof valueOf, "function");
+
+assert.throws(TypeError, () => valueOf.call(undefined), "undefined");
+assert.throws(TypeError, () => valueOf.call(null), "null");
+assert.throws(TypeError, () => valueOf.call(true), "true");
+assert.throws(TypeError, () => valueOf.call(""), "empty string");
+assert.throws(TypeError, () => valueOf.call(Symbol()), "symbol");
+assert.throws(TypeError, () => valueOf.call(1), "1");
+assert.throws(TypeError, () => valueOf.call({}), "plain object");
+assert.throws(TypeError, () => valueOf.call(Temporal.PlainTime), "Temporal.PlainTime");
+assert.throws(TypeError, () => valueOf.call(Temporal.PlainTime.prototype), "Temporal.PlainTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/builtin.js
new file mode 100644
index 0000000000..b9cf15bcd4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/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.valueof
+description: >
+ Tests that Temporal.PlainTime.prototype.valueOf
+ 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.valueOf),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainTime.prototype.valueOf),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainTime.prototype.valueOf),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainTime.prototype.valueOf.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/length.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/length.js
new file mode 100644
index 0000000000..d3d22c93cd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/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.valueof
+description: Temporal.PlainTime.prototype.valueOf.length is 0
+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.valueOf, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/name.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/name.js
new file mode 100644
index 0000000000..33264a7a6f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/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.valueof
+description: Temporal.PlainTime.prototype.valueOf.name is "valueOf".
+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.valueOf, "name", {
+ value: "valueOf",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/not-a-constructor.js
new file mode 100644
index 0000000000..46477ced1d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/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.valueof
+description: >
+ Temporal.PlainTime.prototype.valueOf 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.valueOf();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainTime.prototype.valueOf), false,
+ "isConstructor(Temporal.PlainTime.prototype.valueOf)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/prop-desc.js
new file mode 100644
index 0000000000..f1473ac5a0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/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.valueof
+description: The "valueOf" property of Temporal.PlainTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainTime.prototype.valueOf,
+ "function",
+ "`typeof PlainTime.prototype.valueOf` is `function`"
+);
+
+verifyProperty(Temporal.PlainTime.prototype, "valueOf", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/valueOf/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/with/argument-not-object.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/argument-not-object.js
new file mode 100644
index 0000000000..c9eddc8b08
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/argument-not-object.js
@@ -0,0 +1,37 @@
+// |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.with
+description: TypeError is thrown if a primitive is passed, including ISO strings
+info: |
+ Temporal.PlainTime.prototype.with ( temporalTimeLike [ , options ] )
+
+ 3. If Type(temporalTimeLike) is not Object, then
+ a. Throw a TypeError exception.
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+assert.throws(TypeError, () => instance.with(undefined), "undefined");
+assert.throws(TypeError, () => instance.with(null), "null");
+assert.throws(TypeError, () => instance.with(true), "true");
+assert.throws(TypeError, () => instance.with(Symbol()), "symbol");
+assert.throws(TypeError, () => instance.with(1), "1");
+assert.throws(TypeError, () => instance.with(1n), "1n");
+
+const strings = [
+ "",
+ "18:05:42.577",
+ "2019-05-17T18:05:42.577",
+ "2019-05-17T18:05:42.577Z",
+ "2019-05-17",
+ "42",
+];
+for (const s of strings) {
+ assert.throws(TypeError, () => instance.with(s), s);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/basic.js
new file mode 100644
index 0000000000..2fa17fda70
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/basic.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.with
+description: Basic tests for with().
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainTime = new Temporal.PlainTime(15, 23, 30, 123, 456, 789);
+TemporalHelpers.assertPlainTime(plainTime, 15, 23, 30, 123, 456, 789, "initial");
+
+const hour = plainTime.with({ hour: 3 });
+TemporalHelpers.assertPlainTime(hour, 3, 23, 30, 123, 456, 789, "hour");
+
+const minute = plainTime.with({ minute: 3 });
+TemporalHelpers.assertPlainTime(minute, 15, 3, 30, 123, 456, 789, "minute");
+
+const second = plainTime.with({ second: 3 });
+TemporalHelpers.assertPlainTime(second, 15, 23, 3, 123, 456, 789, "second");
+
+const millisecond = plainTime.with({ millisecond: 3 });
+TemporalHelpers.assertPlainTime(millisecond, 15, 23, 30, 3, 456, 789, "millisecond");
+
+const microsecond = plainTime.with({ microsecond: 3 });
+TemporalHelpers.assertPlainTime(microsecond, 15, 23, 30, 123, 3, 789, "microsecond");
+
+const nanosecond = plainTime.with({ nanosecond: 3 });
+TemporalHelpers.assertPlainTime(nanosecond, 15, 23, 30, 123, 456, 3, "nanosecond");
+
+const combined = plainTime.with({ minute: 8, nanosecond: 3 });
+TemporalHelpers.assertPlainTime(combined, 15, 8, 30, 123, 456, 3, "combined");
+
+const plural = plainTime.with({ minutes: 8, nanosecond: 3 });
+TemporalHelpers.assertPlainTime(plural, 15, 23, 30, 123, 456, 3, "plural");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/branding.js
new file mode 100644
index 0000000000..f2d4368c5e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/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.with
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const with_ = Temporal.PlainTime.prototype.with;
+
+assert.sameValue(typeof with_, "function");
+
+const args = [{ hour: 7 }];
+
+assert.throws(TypeError, () => with_.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => with_.apply(null, args), "null");
+assert.throws(TypeError, () => with_.apply(true, args), "true");
+assert.throws(TypeError, () => with_.apply("", args), "empty string");
+assert.throws(TypeError, () => with_.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => with_.apply(1, args), "1");
+assert.throws(TypeError, () => with_.apply({}, args), "plain object");
+assert.throws(TypeError, () => with_.apply(Temporal.PlainTime, args), "Temporal.PlainTime");
+assert.throws(TypeError, () => with_.apply(Temporal.PlainTime.prototype, args), "Temporal.PlainTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/builtin.js
new file mode 100644
index 0000000000..53ffd00651
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/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.with
+description: >
+ Tests that Temporal.PlainTime.prototype.with
+ 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.with),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainTime.prototype.with),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainTime.prototype.with),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainTime.prototype.with.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/copy-properties-not-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/copy-properties-not-undefined.js
new file mode 100644
index 0000000000..4ea654c715
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/copy-properties-not-undefined.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.with
+description: PreparePartialTemporalFields copies only defined properties of source object
+info: |
+ 4. For each value _property_ of _fieldNames_, do
+ a. Let _value_ be ? Get(_fields_, _property_).
+ b. If _value_ is not *undefined*, then
+ ...
+ iii. Perform ! CreateDataPropertyOrThrow(_result_, _property_, _value_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainTime = new Temporal.PlainTime(9, 45, 55);
+
+TemporalHelpers.assertPlainTime(plainTime.with({ hour: 8, second: undefined }),
+ 8, 45, 55, 0, 0, 0,
+ "only the properties that are present and defined in the plain object are copied"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..6827322b34
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/infinity-throws-rangeerror.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.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plaintime.prototype.with
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((prop) => {
+ ["constrain", "reject"].forEach((overflow) => {
+ assert.throws(RangeError, () => instance.with({ [prop]: inf }, { overflow }), `${prop} property cannot be ${inf} (overflow ${overflow}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.with({ [prop]: obj }, { overflow }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/length.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/length.js
new file mode 100644
index 0000000000..b51dcb2cf9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/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.with
+description: Temporal.PlainTime.prototype.with.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.with, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/name.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/name.js
new file mode 100644
index 0000000000..e37f7e45de
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/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.with
+description: Temporal.PlainTime.prototype.with.name is "with".
+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.with, "name", {
+ value: "with",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/not-a-constructor.js
new file mode 100644
index 0000000000..4446eb754b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/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.with
+description: >
+ Temporal.PlainTime.prototype.with 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.with();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainTime.prototype.with), false,
+ "isConstructor(Temporal.PlainTime.prototype.with)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/options-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/options-invalid.js
new file mode 100644
index 0000000000..7cd1abbf1a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/options-invalid.js
@@ -0,0 +1,16 @@
+// |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.with
+description: TypeError thrown when a primitive is passed as the options argument
+features: [Temporal]
+---*/
+
+const plainTime = new Temporal.PlainTime(12);
+for (const badOptions of [null, true, "hello", Symbol("foo"), 1, 1n]) {
+ assert.throws(TypeError, () => plainTime.with({ hour: 3 }, badOptions));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/options-object.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/options-object.js
new file mode 100644
index 0000000000..25a0d37bfa
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/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.with
+description: Empty or a function object may be used as options
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime();
+
+const result1 = instance.with({ minute: 45 }, {});
+TemporalHelpers.assertPlainTime(
+ result1, 0, 45, 0, 0, 0, 0,
+ "options may be an empty plain object"
+);
+
+const result2 = instance.with({ minute: 45 }, () => {});
+TemporalHelpers.assertPlainTime(
+ result2, 0, 45, 0, 0, 0, 0,
+ "options may be a function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/options-undefined.js
new file mode 100644
index 0000000000..02c8659e12
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/options-undefined.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.with
+includes: [temporalHelpers.js]
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+const fields = { minute: 60 };
+
+const explicit = time.with(fields, undefined);
+TemporalHelpers.assertPlainTime(explicit, 12, 59, 56, 987, 654, 321, "explicit");
+
+const implicit = time.with(fields);
+TemporalHelpers.assertPlainTime(implicit, 12, 59, 56, 987, 654, 321, "implicit");
+
+const lambda = time.with(fields, () => {});
+TemporalHelpers.assertPlainTime(lambda, 12, 59, 56, 987, 654, 321, "lambda");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/options-wrong-type.js
new file mode 100644
index 0000000000..f15373d377
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/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.with
+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.with({ minute: 45 }, value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/order-of-operations.js
new file mode 100644
index 0000000000..fdc9c6cb57
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/order-of-operations.js
@@ -0,0 +1,59 @@
+// |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.with
+description: Properties on an object passed to with() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+const expected = [
+ // RejectObjectWithCalendarOrTimeZone
+ "get fields.calendar",
+ "get fields.timeZone",
+ // ToTemporalOverflow
+ "get options.overflow",
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+ // ToTemporalTimeRecord
+ "get fields.hour",
+ "get fields.hour.valueOf",
+ "call fields.hour.valueOf",
+ "get fields.microsecond",
+ "get fields.microsecond.valueOf",
+ "call fields.microsecond.valueOf",
+ "get fields.millisecond",
+ "get fields.millisecond.valueOf",
+ "call fields.millisecond.valueOf",
+ "get fields.minute",
+ "get fields.minute.valueOf",
+ "call fields.minute.valueOf",
+ "get fields.nanosecond",
+ "get fields.nanosecond.valueOf",
+ "call fields.nanosecond.valueOf",
+ "get fields.second",
+ "get fields.second.valueOf",
+ "call fields.second.valueOf",
+];
+const actual = [];
+
+const fields = TemporalHelpers.propertyBagObserver(actual, {
+ hour: 1.7,
+ minute: 1.7,
+ second: 1.7,
+ millisecond: 1.7,
+ microsecond: 1.7,
+ nanosecond: 1.7,
+}, "fields");
+
+const options = TemporalHelpers.propertyBagObserver(actual, {
+ overflow: "constrain",
+}, "options");
+
+const result = instance.with(fields, options);
+assert.compareArray(actual, expected, "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/overflow-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/overflow-invalid-string.js
new file mode 100644
index 0000000000..f5e60db95f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/overflow-invalid-string.js
@@ -0,0 +1,29 @@
+// |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.with
+description: RangeError thrown when overflow option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal.plaintime.prototype.with step 11:
+ 11. Let _overflow_ be ? ToTemporalOverflow(_options_).
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12);
+
+const badOverflows = ["", "CONSTRAIN", "balance", "other string", "constra\u0131n", "reject\0"];
+for (const overflow of badOverflows) {
+ assert.throws(
+ RangeError,
+ () => time.with({ minute: 45 }, { overflow }),
+ `invalid overflow ("${overflow}")`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/overflow-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/overflow-undefined.js
new file mode 100644
index 0000000000..1d44db4ab3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/overflow-undefined.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.with
+description: Fallback value for overflow option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal.plaintime.prototype.with step 11:
+ 11. Let _overflow_ be ? ToTemporalOverflow(_options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12);
+const explicit = time.with({ minute: 67 }, { overflow: undefined });
+TemporalHelpers.assertPlainTime(explicit, 12, 59, 0, 0, 0, 0, "default overflow is constrain");
+const implicit = time.with({ minute: 67 }, {});
+TemporalHelpers.assertPlainTime(implicit, 12, 59, 0, 0, 0, 0, "default overflow is constrain");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/overflow-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/overflow-wrong-type.js
new file mode 100644
index 0000000000..96e3ca15dd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/overflow-wrong-type.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.with
+description: Type conversions for overflow option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal.plaintime.prototype.with step 11:
+ 11. Let _overflow_ be ? ToTemporalOverflow(_options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12);
+TemporalHelpers.checkStringOptionWrongType("overflow", "constrain",
+ (overflow) => time.with({ minute: 45 }, { overflow }),
+ (result, descr) => TemporalHelpers.assertPlainTime(result, 12, 45, 0, 0, 0, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/plaintimelike-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/plaintimelike-invalid.js
new file mode 100644
index 0000000000..4b9f9c0316
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/plaintimelike-invalid.js
@@ -0,0 +1,50 @@
+// |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.with
+description: Throws TypeError on an argument that is not a PlainTime-like property bag
+features: [Temporal]
+---*/
+
+const plainTime = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+const tests = [
+ // Step 3.
+ [undefined],
+ [null],
+ [true],
+ ["2019-05-17"],
+ ["2019-05-17T12:34"],
+ ["2019-05-17T12:34Z"],
+ ["18:05:42.577"],
+ ["42"],
+ [Symbol(), "symbol"],
+ [42, "number"],
+ [42n, "bigint"],
+
+ // Step 4.
+ [Temporal.PlainDate.from("2019-05-17"), "PlainDate"],
+ [Temporal.PlainDateTime.from("2019-05-17T12:34"), "PlainDateTime"],
+ [Temporal.PlainMonthDay.from("2019-05-17"), "PlainMonthDay"],
+ [Temporal.PlainTime.from("12:34"), "PlainTime"],
+ [Temporal.PlainYearMonth.from("2019-05-17"), "PlainYearMonth"],
+ [Temporal.ZonedDateTime.from("2019-05-17T12:34Z[UTC]"), "ZonedDateTime"],
+
+ // Step 5-6.
+ [{ hour: 14, calendar: "iso8601" }, "calendar"],
+
+ // Step 7-8.
+ [{ hour: 14, timeZone: "UTC" }, "timeZone"],
+
+ // Step 9.
+ [{}, "empty object"],
+ [{ hours: 14 }, "only plural property"],
+];
+
+for (const [value, message = String(value)] of tests) {
+ assert.throws(TypeError, () => plainTime.with(value), message);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/prop-desc.js
new file mode 100644
index 0000000000..e4a1c0ca44
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/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.with
+description: The "with" property of Temporal.PlainTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainTime.prototype.with,
+ "function",
+ "`typeof PlainTime.prototype.with` is `function`"
+);
+
+verifyProperty(Temporal.PlainTime.prototype, "with", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/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/with/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/subclassing-ignored.js
new file mode 100644
index 0000000000..a9112670f6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/with/subclassing-ignored.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.with
+description: Objects of a subclass are never created as return values for with()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.PlainTime,
+ [12, 34, 56, 987, 654, 321],
+ "with",
+ [{ nanosecond: 1 }],
+ (result) => TemporalHelpers.assertPlainTime(result, 12, 34, 56, 987, 654, 1),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/second-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/second-undefined.js
new file mode 100644
index 0000000000..5eceb3c5ee
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/second-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
+description: Second argument defaults to 0 if not given
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const args = [12, 34];
+
+const explicit = new Temporal.PlainTime(...args, undefined);
+TemporalHelpers.assertPlainTime(explicit, ...args, 0, 0, 0, 0, "explicit");
+
+const implicit = new Temporal.PlainTime(...args);
+TemporalHelpers.assertPlainTime(implicit, ...args, 0, 0, 0, 0, "implicit");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/subclass.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/subclass.js
new file mode 100644
index 0000000000..4fae03ce38
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/subclass.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
+description: Test for Temporal.PlainTime subclassing.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+class CustomPlainTime extends Temporal.PlainTime {
+}
+
+const instance = new CustomPlainTime(12, 34, 56, 987, 654, 321);
+TemporalHelpers.assertPlainTime(instance, 12, 34, 56, 987, 654, 321);
+assert.sameValue(Object.getPrototypeOf(instance), CustomPlainTime.prototype, "Instance of CustomPlainTime");
+assert(instance instanceof CustomPlainTime, "Instance of CustomPlainTime");
+assert(instance instanceof Temporal.PlainTime, "Instance of Temporal.PlainTime");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/basic.js
new file mode 100644
index 0000000000..89df253b7e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/basic.js
@@ -0,0 +1,16 @@
+// |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.plainyearmonth
+description: PlainYearMonth constructor works
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const ym = new Temporal.PlainYearMonth(1976, 11);
+assert.sameValue(typeof ym, "object");
+TemporalHelpers.assertPlainYearMonth(ym, 1976, 11, "M11");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/builtin.js
new file mode 100644
index 0000000000..ec3397b9a8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/builtin.js
@@ -0,0 +1,30 @@
+// |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.plainyearmonth
+description: Tests that Temporal.PlainYearMonth meets the requirements for built-in objects
+info: |
+ 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.PlainYearMonth),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainYearMonth),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainYearMonth),
+ Function.prototype, "prototype");
+
+assert.sameValue(typeof Temporal.PlainYearMonth.prototype,
+ "object", "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-always.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-always.js
new file mode 100644
index 0000000000..bfc3f7c978
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-always.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.plainyearmonth
+description: If calendar name is to be emitted, include additional reference info
+features: [Temporal]
+---*/
+
+const pym = new Temporal.PlainYearMonth(2019, 10, "iso8601", 31);
+
+assert.sameValue(
+ pym.toString({ calendarName: 'always' }),
+ "2019-10-31[u-ca=iso8601]",
+ "emit year-month-day if calendarName = 'always' (four-argument constructor)"
+);
+
+const anotherPYM = Temporal.PlainYearMonth.from("2019-10-31"); // 31 will get dropped
+
+assert.sameValue(
+ anotherPYM.toString({ calendarName: 'always' }),
+ "2019-10-01[u-ca=iso8601]",
+ "emit fallback day if calendarName = 'always' (static from)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-case-insensitive.js
new file mode 100644
index 0000000000..d3465f7957
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-case-insensitive.js
@@ -0,0 +1,16 @@
+// |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.plainyearmonth
+description: Calendar names are case-insensitive
+features: [Temporal]
+---*/
+
+const arg = "iSo8601";
+
+const result = new Temporal.PlainYearMonth(2000, 5, arg, 1);
+assert.sameValue(result.calendarId, "iso8601", "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-invalid.js
new file mode 100644
index 0000000000..b5c69e0d04
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-invalid.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.
+
+/*---
+description: Temporal.PlainYearMonth throws a RangeError if the calendar argument is invalid
+esid: sec-temporal.plainyearmonth
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = ["get year.valueOf", "call year.valueOf", "get month.valueOf", "call month.valueOf"];
+const actual = [];
+const args = [
+ TemporalHelpers.toPrimitiveObserver(actual, 1970, "year"),
+ TemporalHelpers.toPrimitiveObserver(actual, 1, "month"),
+ "local",
+ TemporalHelpers.toPrimitiveObserver(actual, 1, "day")
+];
+assert.throws(RangeError, () => new Temporal.PlainYearMonth(...args));
+assert.compareArray(actual, expected, "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-number.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-number.js
new file mode 100644
index 0000000000..dc11586ee5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-number.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.plainyearmonth
+description: A number is not allowed to be a calendar
+features: [Temporal]
+---*/
+
+const numbers = [
+ 1,
+ -19761118,
+ 19761118,
+ 1234567890,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => new Temporal.PlainYearMonth(2000, 5, arg, 1),
+ "A number is not a valid ISO string for Calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-string.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-string.js
new file mode 100644
index 0000000000..3d8417dd83
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-string.js
@@ -0,0 +1,16 @@
+// |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.plainyearmonth.constructor
+description: A calendar ID is valid input for Calendar
+features: [Temporal]
+---*/
+
+const arg = "iso8601";
+
+const result = new Temporal.PlainYearMonth(2000, 5, arg, 1);
+assert.sameValue(result.getISOFields().calendar, "iso8601", `Calendar created from string "${arg}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-temporal-object.js
new file mode 100644
index 0000000000..501c0b44a8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-temporal-object.js
@@ -0,0 +1,41 @@
+// |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.plainyearmonth
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal-totemporalcalendar step 1.b:
+ b. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const plainDate = new Temporal.PlainDate(2000, 5, 2);
+const plainDateTime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const plainMonthDay = new Temporal.PlainMonthDay(5, 2);
+const plainYearMonth = new Temporal.PlainYearMonth(2000, 5);
+const zonedDateTime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+
+[plainDate, plainDateTime, plainMonthDay, plainYearMonth, zonedDateTime].forEach((arg) => {
+ const actual = [];
+ const expected = [];
+
+ const calendar = arg.getISOFields().calendar;
+
+ Object.defineProperty(arg, "calendar", {
+ get() {
+ actual.push("get calendar");
+ return calendar;
+ },
+ });
+
+ const result = new Temporal.PlainYearMonth(2000, 5, arg, 1);
+ assert.sameValue(result.getISOFields().calendar, calendar, "Temporal object coerced to calendar");
+
+ assert.compareArray(actual, expected, "calendar getter not called");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-undefined.js
new file mode 100644
index 0000000000..317f57a426
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-undefined.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.plainyearmonth
+description: Calendar argument defaults to the built-in ISO 8601 calendar
+features: [Temporal]
+---*/
+
+const args = [2000, 5];
+
+Object.defineProperty(Temporal.Calendar, "from", {
+ get() {
+ throw new Test262Error("Should not get Calendar.from");
+ },
+});
+
+const dateExplicit = new Temporal.PlainYearMonth(...args, undefined);
+assert.sameValue(dateExplicit.calendarId, "iso8601");
+
+const dateImplicit = new Temporal.PlainYearMonth(...args);
+assert.sameValue(dateImplicit.calendarId, "iso8601");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-wrong-type.js
new file mode 100644
index 0000000000..1bbf456ce8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-wrong-type.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.plainyearmonth
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or object for Calendar
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const primitiveTests = [
+ [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,
+ () => new Temporal.PlainYearMonth(2000, 5, arg, 1),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => new Temporal.PlainYearMonth(2000, 5, arg, 1), `${description} is not a valid object and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..1ac34ed0c5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-builtin-calendar-no-array-iteration.js
@@ -0,0 +1,24 @@
+// |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.plainyearmonth.compare
+description: >
+ Calling the method with a property bag argument with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const arg = { year: 2000, month: 5, calendar: "iso8601" };
+Temporal.PlainYearMonth.compare(arg, new Temporal.PlainYearMonth(2019, 6));
+Temporal.PlainYearMonth.compare(new Temporal.PlainYearMonth(2019, 6), arg);
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-cast.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-cast.js
new file mode 100644
index 0000000000..4f98ccb77c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-cast.js
@@ -0,0 +1,22 @@
+// |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.plainyearmonth.compare
+description: compare() casts its arguments
+features: [Temporal]
+---*/
+
+const nov94 = Temporal.PlainYearMonth.from("1994-11");
+const jun13 = Temporal.PlainYearMonth.from("2013-06");
+
+assert.sameValue(Temporal.PlainYearMonth.compare({ year: 1994, month: 11 }, jun13), -1, "one object");
+assert.sameValue(Temporal.PlainYearMonth.compare("1994-11", jun13), -1, "one string");
+assert.throws(TypeError, () => Temporal.PlainYearMonth.compare({ year: 1994 }, jun13), "one missing property");
+
+assert.sameValue(Temporal.PlainYearMonth.compare(nov94, { year: 2013, month: 6 }), -1, "two object");
+assert.sameValue(Temporal.PlainYearMonth.compare(nov94, "2013-06"), -1, "two string");
+assert.throws(TypeError, () => Temporal.PlainYearMonth.compare(nov94, { year: 2013 }), "two missing property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-number.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-number.js
new file mode 100644
index 0000000000..6381187105
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-number.js
@@ -0,0 +1,33 @@
+// |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.plainyearmonth.compare
+description: A number is invalid in place of an ISO string for Temporal.PlainYearMonth
+features: [Temporal]
+---*/
+
+const arg = 201906;
+
+const numbers = [
+ 1,
+ 201906,
+ -201906,
+ 1234567,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => Temporal.PlainYearMonth.compare(arg, new Temporal.PlainYearMonth(2019, 6)),
+ `A number (${arg}) is not a valid ISO string for PlainYearMonth (first argument)`
+ );
+ assert.throws(
+ TypeError,
+ () => Temporal.PlainYearMonth.compare(new Temporal.PlainYearMonth(2019, 6), arg),
+ `A number (${arg}) is not a valid ISO string for PlainYearMonth (first argument)`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-propertybag-calendar-case-insensitive.js
new file mode 100644
index 0000000000..c37f433a64
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-propertybag-calendar-case-insensitive.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.plainyearmonth.compare
+description: The calendar name is case-insensitive
+features: [Temporal]
+---*/
+
+const calendar = "IsO8601";
+
+const arg = { year: 2019, monthCode: "M06", calendar };
+const result1 = Temporal.PlainYearMonth.compare(arg, new Temporal.PlainYearMonth(2019, 6));
+assert.sameValue(result1, 0, "Calendar is case-insensitive (first argument)");
+const result2 = Temporal.PlainYearMonth.compare(new Temporal.PlainYearMonth(2019, 6), arg);
+assert.sameValue(result2, 0, "Calendar is case-insensitive (second argument)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-propertybag-calendar-leap-second.js
new file mode 100644
index 0000000000..47e6492289
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-propertybag-calendar-leap-second.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.plainyearmonth.compare
+description: Leap second is a valid ISO string for a calendar in a property bag
+features: [Temporal]
+---*/
+
+const calendar = "2016-12-31T23:59:60";
+
+const arg = { year: 2019, monthCode: "M06", calendar };
+const result1 = Temporal.PlainYearMonth.compare(arg, new Temporal.PlainYearMonth(2019, 6));
+assert.sameValue(
+ result1,
+ 0,
+ "leap second is a valid ISO string for calendar (first argument)"
+);
+const result2 = Temporal.PlainYearMonth.compare(new Temporal.PlainYearMonth(2019, 6), arg);
+assert.sameValue(
+ result2,
+ 0,
+ "leap second is a valid ISO string for calendar (second argument)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-propertybag-calendar-number.js
new file mode 100644
index 0000000000..9db6ff73d4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-propertybag-calendar-number.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.plainyearmonth.compare
+description: A number as calendar in a property bag is not accepted
+features: [Temporal]
+---*/
+
+const numbers = [
+ 1,
+ 19970327,
+ -19970327,
+ 1234567890,
+];
+
+for (const calendar of numbers) {
+ const arg = { year: 2019, monthCode: "M06", calendar };
+ assert.throws(
+ TypeError,
+ () => Temporal.PlainYearMonth.compare(arg, new Temporal.PlainYearMonth(2019, 6)),
+ "A number is not a valid ISO string for calendar (first argument)"
+ );
+ assert.throws(
+ TypeError,
+ () => Temporal.PlainYearMonth.compare(new Temporal.PlainYearMonth(2019, 6), arg),
+ "A number is not a valid ISO string for calendar (second argument)"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-propertybag-calendar-string.js
new file mode 100644
index 0000000000..d757e80b1e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-propertybag-calendar-string.js
@@ -0,0 +1,33 @@
+// |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.plainyearmonth.compare
+description: A calendar ID is valid input for Calendar
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dateFromFieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dateFromFields");
+Object.defineProperty(Temporal.Calendar.prototype, "dateFromFields", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("dateFromFields should not be looked up");
+ },
+});
+
+const calendar = "iso8601";
+
+const arg = { year: 2019, monthCode: "M06", calendar };
+
+const result1 = Temporal.PlainYearMonth.compare(arg, new Temporal.PlainYearMonth(2019, 6));
+assert.sameValue(result1, 0, `Calendar created from string "${arg}" (first argument)`);
+
+const result2 = Temporal.PlainYearMonth.compare(new Temporal.PlainYearMonth(2019, 6), arg);
+assert.sameValue(result2, 0, `Calendar created from string "${arg}" (second argument)`);
+
+Object.defineProperty(Temporal.Calendar.prototype, "dateFromFields", dateFromFieldsOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-propertybag-calendar-wrong-type.js
new file mode 100644
index 0000000000..bed5c45623
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-propertybag-calendar-wrong-type.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.plainyearmonth.compare
+description: >
+ Appropriate error thrown when a calendar property from a property bag cannot
+ be converted to a calendar object or string
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [calendar, description] of primitiveTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ typeof calendar === "string" ? RangeError : TypeError,
+ () => Temporal.PlainYearMonth.compare(arg, new Temporal.PlainYearMonth(2019, 6)),
+ `${description} does not convert to a valid ISO string (first argument)`
+ );
+ assert.throws(
+ typeof calendar === "string" ? RangeError : TypeError,
+ () => Temporal.PlainYearMonth.compare(new Temporal.PlainYearMonth(2019, 6), arg),
+ `${description} does not convert to a valid ISO string (second argument)`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+ [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields()
+];
+
+for (const [calendar, description] of typeErrorTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(TypeError, () => Temporal.PlainYearMonth.compare(arg, new Temporal.PlainYearMonth(2019, 6)), `${description} is not a valid property bag and does not convert to a string (first argument)`);
+ assert.throws(TypeError, () => Temporal.PlainYearMonth.compare(new Temporal.PlainYearMonth(2019, 6), arg), `${description} is not a valid property bag and does not convert to a string (second argument)`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-propertybag-calendar-year-zero.js
new file mode 100644
index 0000000000..6ecb7deff3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-propertybag-calendar-year-zero.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.plainyearmonth.compare
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T17:45",
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+01:00",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainYearMonth.compare(arg, new Temporal.PlainYearMonth(2019, 6)),
+ "reject minus zero as extended year (first argument)"
+ );
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainYearMonth.compare(new Temporal.PlainYearMonth(2019, 6), arg),
+ "reject minus zero as extended year (second argument)"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..8b6f2f790c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-calendar-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.plainyearmonth.compare
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2019-12-15T15:23[u-ca=iso8601]", "without time zone"],
+ ["2019-12-15T15:23[UTC][u-ca=iso8601]", "with time zone"],
+ ["2019-12-15T15:23[!u-ca=iso8601]", "with ! and no time zone"],
+ ["2019-12-15T15:23[UTC][!u-ca=iso8601]", "with ! and time zone"],
+ ["2019-12-15T15:23[u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.PlainYearMonth.compare(arg, arg);
+
+ assert.sameValue(
+ result,
+ 0,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..cf8b20edca
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-critical-unknown-annotation.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.plainyearmonth.compare
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "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]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainYearMonth.compare(arg, new Temporal.PlainYearMonth(2019, 6)),
+ `reject unknown annotation with critical flag: ${arg} (first argument)`
+ );
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainYearMonth.compare(new Temporal.PlainYearMonth(2019, 6), arg),
+ `reject unknown annotation with critical flag: ${arg} (second argument)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..3375a84db3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-date-with-utc-offset.js
@@ -0,0 +1,58 @@
+// |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.plainyearmonth.compare
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+---*/
+
+const validStrings = [
+ "2019-12[Africa/Abidjan]",
+ "2019-12[!Africa/Abidjan]",
+ "2019-12[u-ca=iso8601]",
+ "2019-12[Africa/Abidjan][u-ca=iso8601]",
+ "2019-12-15T00+00:00",
+ "2019-12-15T00+00:00[UTC]",
+ "2019-12-15T00+00:00[!UTC]",
+ "2019-12-15T00-02:30[America/St_Johns]",
+];
+
+for (const arg of validStrings) {
+ const result = Temporal.PlainYearMonth.compare(arg, arg);
+
+ assert.sameValue(
+ result,
+ 0,
+ `"${arg}" is a valid UTC offset with time for PlainYearMonth`
+ );
+}
+
+const invalidStrings = [
+ "2022-09[u-ca=hebrew]",
+ "2022-09Z",
+ "2022-09+01:00",
+ "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,
+ () => Temporal.PlainYearMonth.compare(arg, new Temporal.PlainYearMonth(2019, 6)),
+ `"${arg}" UTC offset without time is not valid for PlainYearMonth (first argument)`
+ );
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainYearMonth.compare(new Temporal.PlainYearMonth(2019, 6), arg),
+ `"${arg}" UTC offset without time is not valid for PlainYearMonth (second argument)`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-invalid.js
new file mode 100644
index 0000000000..26c2f8b2c5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-invalid.js
@@ -0,0 +1,18 @@
+// |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.plainyearmonth.compare
+description: An invalid ISO string is never supported
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const arg2 = new Temporal.PlainYearMonth(1976, 11);
+for (const arg of TemporalHelpers.ISO.plainYearMonthStringsInvalid()) {
+ assert.throws(RangeError, () => Temporal.PlainYearMonth.compare(arg, arg2), `"${arg}" is invalid (first argument)`);
+ assert.throws(RangeError, () => Temporal.PlainYearMonth.compare(arg2, arg), `"${arg}" is invalid (second argument)`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..d29e88d68b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-multiple-calendar.js
@@ -0,0 +1,37 @@
+// |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.plainyearmonth.compare
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[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]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainYearMonth.compare(arg, new Temporal.PlainYearMonth(2019, 6)),
+ `reject more than one calendar annotation if any critical: ${arg} (first argument)`
+ );
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainYearMonth.compare(new Temporal.PlainYearMonth(2019, 6), arg),
+ `reject more than one calendar annotation if any critical: ${arg} (second argument)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..fecfb4bc40
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-multiple-time-zone.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.plainyearmonth.compare
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "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]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainYearMonth.compare(arg, new Temporal.PlainYearMonth(2019, 6)),
+ `reject more than one time zone annotation: ${arg} (first argument)`
+ );
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainYearMonth.compare(new Temporal.PlainYearMonth(2019, 6), arg),
+ `reject more than one time zone annotation: ${arg} (second argument)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-time-separators.js
new file mode 100644
index 0000000000..8307aacff7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/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.plainyearmonth.compare
+description: Time separator in string argument can vary
+features: [Temporal]
+---*/
+
+const yearMonth = new Temporal.PlainYearMonth(2019, 12);
+const tests = [
+ ["2019-12-15T15:23", "uppercase T"],
+ ["2019-12-15t15:23", "lowercase T"],
+ ["2019-12-15 15:23", "space between date and time"],
+];
+
+tests.forEach(([arg, description]) => {
+ assert.sameValue(
+ Temporal.PlainYearMonth.compare(arg, yearMonth),
+ 0,
+ `variant time separators (${description}), first argument`
+ );
+
+ assert.sameValue(
+ Temporal.PlainYearMonth.compare(yearMonth, arg),
+ 0,
+ `variant time separators (${description}), second argument`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..e2af75b112
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-time-zone-annotation.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.plainyearmonth.compare
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2019-12-15T15:23[Asia/Kolkata]", "named, with no offset"],
+ ["2019-12-15T15:23[!Europe/Vienna]", "named, with ! and no offset"],
+ ["2019-12-15T15:23[+00:00]", "numeric, with no offset"],
+ ["2019-12-15T15:23[!-02:30]", "numeric, with ! and no offset"],
+ ["2019-12-15T15:23+00:00[UTC]", "named, with offset"],
+ ["2019-12-15T15:23+00:00[!Africa/Abidjan]", "named, with offset and !"],
+ ["2019-12-15T15:23+00:00[+01:00]", "numeric, with offset"],
+ ["2019-12-15T15:23+00:00[!-08:00]", "numeric, with offset and !"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.PlainYearMonth.compare(arg, arg);
+
+ assert.sameValue(
+ result,
+ 0,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..eb501cfc7b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-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.plainyearmonth.compare
+description: Various forms of unknown annotation
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2019-12-15T15:23[foo=bar]", "alone"],
+ ["2019-12-15T15:23[UTC][foo=bar]", "with time zone"],
+ ["2019-12-15T15:23[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["2019-12-15T15:23[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["2019-12-15T15:23[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.PlainYearMonth.compare(arg, arg);
+
+ assert.sameValue(
+ result,
+ 0,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..2b3687fc43
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-with-utc-designator.js
@@ -0,0 +1,29 @@
+// |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.plainyearmonth.compare
+description: RangeError thrown if a string with UTC designator is used as a PlainYearMonth
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "2019-10-01T09:00:00Z",
+ "2019-10-01T09:00:00Z[UTC]",
+];
+const yearMonth = new Temporal.PlainYearMonth(2000, 5);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainYearMonth.compare(arg, yearMonth),
+ "String with UTC designator should not be valid as a PlainYearMonth (first argument)"
+ );
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainYearMonth.compare(yearMonth, arg),
+ "String with UTC designator should not be valid as a PlainYearMonth (second argument)"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string.js
new file mode 100644
index 0000000000..3cd77be14c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string.js
@@ -0,0 +1,22 @@
+// |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.plainyearmonth.compare
+description: A string is parsed into the correct object when passed as the argument
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const validStrings = TemporalHelpers.ISO.plainYearMonthStringsValid().concat(TemporalHelpers.ISO.plainYearMonthStringsValidNegativeYear());
+
+for (const arg of validStrings) {
+ assert.sameValue(
+ Temporal.PlainYearMonth.compare(arg, arg),
+ 0,
+ `"${arg}" is a valid PlainYearMonth string`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-wrong-type.js
new file mode 100644
index 0000000000..1f6ae998ed
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-wrong-type.js
@@ -0,0 +1,47 @@
+// |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.plainyearmonth.compare
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainYearMonth
+features: [BigInt, Symbol, Temporal]
+---*/
+
+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,
+ () => Temporal.PlainYearMonth.compare(arg, new Temporal.PlainYearMonth(2019, 6)),
+ `${description} does not convert to a valid ISO string (first argument)`
+ );
+ assert.throws(
+ typeof arg === 'string' ? RangeError : TypeError,
+ () => Temporal.PlainYearMonth.compare(new Temporal.PlainYearMonth(2019, 6), arg),
+ `${description} does not convert to a valid ISO string (second argument)`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.PlainYearMonth, "Temporal.PlainYearMonth, object"],
+ [Temporal.PlainYearMonth.prototype, "Temporal.PlainYearMonth.prototype, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => Temporal.PlainYearMonth.compare(arg, new Temporal.PlainYearMonth(2019, 6)), `${description} is not a valid property bag and does not convert to a string (first argument)`);
+ assert.throws(TypeError, () => Temporal.PlainYearMonth.compare(new Temporal.PlainYearMonth(2019, 6), arg), `${description} is not a valid property bag and does not convert to a string (second argument)`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/basic.js
new file mode 100644
index 0000000000..704b0177e7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/basic.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.plainyearmonth.compare
+description: Basic tests for compare()
+features: [Temporal]
+---*/
+
+const nov94 = Temporal.PlainYearMonth.from("1994-11");
+const nov94bis = Temporal.PlainYearMonth.from("1994-11");
+const jun13 = Temporal.PlainYearMonth.from("2013-06");
+assert.sameValue(Temporal.PlainYearMonth.compare(nov94, nov94), 0, "same object");
+assert.sameValue(Temporal.PlainYearMonth.compare(nov94, nov94bis), 0, "different object");
+assert.sameValue(Temporal.PlainYearMonth.compare(nov94, jun13), -1, "before");
+assert.sameValue(Temporal.PlainYearMonth.compare(jun13, nov94), 1, "after");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/builtin.js
new file mode 100644
index 0000000000..bef5d9ad97
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/builtin.js
@@ -0,0 +1,33 @@
+// |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.plainyearmonth.compare
+description: Tests that Temporal.PlainYearMonth.compare meets the requirements for built-in objects
+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.PlainYearMonth.compare),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainYearMonth.compare),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainYearMonth.compare),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainYearMonth.compare.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/calendar-datefromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..4591181208
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/calendar-datefromfields-called-with-null-prototype-fields.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.plainyearmonth.compare
+description: >
+ Calendar.yearMonthFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const arg1 = { year: 2000, month: 5, calendar };
+const arg2 = new Temporal.PlainYearMonth(2019, 6);
+
+Temporal.PlainYearMonth.compare(arg1, arg2);
+assert.sameValue(calendar.yearMonthFromFieldsCallCount, 1, "yearMonthFromFields should be called on the property bag's calendar (first argument)");
+
+calendar.yearMonthFromFieldsCallCount = 0;
+
+Temporal.PlainYearMonth.compare(arg2, arg1);
+assert.sameValue(calendar.yearMonthFromFieldsCallCount, 1, "yearMonthFromFields should be called on the property bag's calendar (second argument)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/calendar-fields-iterable.js
new file mode 100644
index 0000000000..a4e08ca12f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/calendar-fields-iterable.js
@@ -0,0 +1,40 @@
+// |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.plainyearmonth.compare
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plainyearmonth.compare steps 1–2:
+ 1. Set _one_ to ? ToTemporalYearMonth(_one_).
+ 2. Set _two_ to ? ToTemporalYearMonth(_two_).
+ sec-temporal-totemporalyearmonth step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+Temporal.PlainYearMonth.compare(
+ { year: 2000, month: 5, calendar: calendar1 },
+ { year: 2001, month: 6, calendar: calendar2 },
+);
+
+assert.sameValue(calendar1.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar1.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar1.iteratorExhausted[0], "iterated through the whole iterable");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/calendar-temporal-object.js
new file mode 100644
index 0000000000..60b3f7026d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/calendar-temporal-object.js
@@ -0,0 +1,32 @@
+// |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.plainyearmonth.compare
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plainyearmonth.compare steps 1–2:
+ 1. Set _one_ to ? ToTemporalYearMonth(_one_).
+ 2. Set _two_ to ? ToTemporalYearMonth(_two_).
+ sec-temporal-totemporaldate step 2.b:
+ b. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ Temporal.PlainYearMonth.compare(
+ { year: 2000, month: 5, calendar: temporalObject },
+ { year: 2001, month: 6, calendar: temporalObject },
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/calendar-yearmonthfromfields-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/calendar-yearmonthfromfields-called-with-options-undefined.js
new file mode 100644
index 0000000000..ee9fe4aae1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/calendar-yearmonthfromfields-called-with-options-undefined.js
@@ -0,0 +1,18 @@
+// |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.plainyearmonth.compare
+description: >
+ Calendar.yearMonthFromFields method is called with undefined as the options
+ value when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
+Temporal.PlainYearMonth.compare({ year: 2000, month: 5, calendar }, { year: 2000, month: 6, calendar });
+assert.sameValue(calendar.yearMonthFromFieldsCallCount, 2);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/compare-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/compare-calendar.js
new file mode 100644
index 0000000000..46bab4df8c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/compare-calendar.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.plainyearmonth.compare
+description: compare() does not take the calendar into account
+features: [Temporal]
+---*/
+
+class CustomCalendar extends Temporal.Calendar {
+ constructor(id) {
+ super("iso8601");
+ this._id = id;
+ }
+ toString() {
+ return this._id;
+ }
+}
+
+const ym1 = new Temporal.PlainYearMonth(2000, 1, new CustomCalendar("a"), 1);
+const ym2 = new Temporal.PlainYearMonth(2000, 1, new CustomCalendar("b"), 1);
+assert.sameValue(Temporal.PlainYearMonth.compare(ym1, ym2), 0);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/compare-reference-day.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/compare-reference-day.js
new file mode 100644
index 0000000000..91d93770c8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/compare-reference-day.js
@@ -0,0 +1,16 @@
+// |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.plainyearmonth.compare
+description: compare() takes the reference day into account
+features: [Temporal]
+---*/
+
+const iso = Temporal.Calendar.from("iso8601");
+const ym1 = new Temporal.PlainYearMonth(2000, 1, iso, 1);
+const ym2 = new Temporal.PlainYearMonth(2000, 1, iso, 2);
+assert.sameValue(Temporal.PlainYearMonth.compare(ym1, ym2), -1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..ae2f23fafb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/constructor-in-calendar-fields.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.plainyearmonth.prototype.compare
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+
+assert.throws(RangeError, () => Temporal.PlainYearMonth.compare(arg, new Temporal.PlainYearMonth(2019, 6)));
+assert.throws(RangeError, () => Temporal.PlainYearMonth.compare(new Temporal.PlainYearMonth(2019, 6), arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/duplicate-calendar-fields.js
new file mode 100644
index 0000000000..ae032c2fef
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/duplicate-calendar-fields.js
@@ -0,0 +1,20 @@
+// |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.plainyearmonth.prototype.compare
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+
+for (const extra_fields of [['foo', 'foo'], ['month'], ['monthCode'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar };
+
+ assert.throws(RangeError, () => Temporal.PlainYearMonth.compare(arg, new Temporal.PlainYearMonth(2019, 6)));
+ assert.throws(RangeError, () => Temporal.PlainYearMonth.compare(new Temporal.PlainYearMonth(2019, 6), arg));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/exhaustive.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/exhaustive.js
new file mode 100644
index 0000000000..38dd3eb88e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/exhaustive.js
@@ -0,0 +1,50 @@
+// |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.plainyearmonth.compare
+description: Tests for compare() with each possible outcome
+features: [Temporal]
+---*/
+
+const cal1 = "iso8601";
+const cal2 = new (class extends Temporal.Calendar { id = "custom"; })("iso8601");
+
+assert.sameValue(
+ Temporal.PlainYearMonth.compare(new Temporal.PlainYearMonth(2000, 5, cal1), new Temporal.PlainYearMonth(1987, 5, cal2)),
+ 1,
+ "year >"
+);
+assert.sameValue(
+ Temporal.PlainYearMonth.compare(new Temporal.PlainYearMonth(1981, 12, cal1), new Temporal.PlainYearMonth(2048, 12, cal2)),
+ -1,
+ "year <"
+);
+assert.sameValue(
+ Temporal.PlainYearMonth.compare(new Temporal.PlainYearMonth(2000, 5, cal1), new Temporal.PlainYearMonth(2000, 3, cal2)),
+ 1,
+ "month >"
+);
+assert.sameValue(
+ Temporal.PlainYearMonth.compare(new Temporal.PlainYearMonth(1981, 4, cal1), new Temporal.PlainYearMonth(1981, 12, cal2)),
+ -1,
+ "month <"
+);
+assert.sameValue(
+ Temporal.PlainYearMonth.compare(new Temporal.PlainYearMonth(2000, 5, cal1, 30), new Temporal.PlainYearMonth(2000, 5, cal2, 14)),
+ 1,
+ "reference day >"
+);
+assert.sameValue(
+ Temporal.PlainYearMonth.compare(new Temporal.PlainYearMonth(1981, 4, cal1, 1), new Temporal.PlainYearMonth(1981, 4, cal2, 12)),
+ -1,
+ "reference day <"
+);
+assert.sameValue(
+ Temporal.PlainYearMonth.compare(new Temporal.PlainYearMonth(2000, 5, cal1), new Temporal.PlainYearMonth(2000, 5, cal2)),
+ 0,
+ "="
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..6d55f794e6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/infinity-throws-rangeerror.js
@@ -0,0 +1,33 @@
+// |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.
+
+/*---
+description: Throws if any value in a property bag for either argument is Infinity or -Infinity
+esid: sec-temporal.plainyearmonth.compare
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const other = new Temporal.PlainYearMonth(2000, 5);
+const base = { year: 2000, month: 5 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month"].forEach((prop) => {
+ assert.throws(RangeError, () => Temporal.PlainYearMonth.compare({ ...base, [prop]: inf }, other), `${prop} property cannot be ${inf}`);
+
+ assert.throws(RangeError, () => Temporal.PlainYearMonth.compare(other, { ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls1 = [];
+ const obj1 = TemporalHelpers.toPrimitiveObserver(calls1, inf, prop);
+ assert.throws(RangeError, () => Temporal.PlainYearMonth.compare({ ...base, [prop]: obj1 }, other));
+ assert.compareArray(calls1, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+
+ const calls2 = [];
+ const obj2 = TemporalHelpers.toPrimitiveObserver(calls2, inf, prop);
+ assert.throws(RangeError, () => Temporal.PlainYearMonth.compare(other, { ...base, [prop]: obj2 }));
+ assert.compareArray(calls2, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/leap-second.js
new file mode 100644
index 0000000000..ab971deb60
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/leap-second.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.plainyearmonth.compare
+description: Leap second is a valid ISO string for PlainYearMonth
+features: [Temporal]
+---*/
+
+let arg = "2016-12-31T23:59:60";
+
+const result1 = Temporal.PlainYearMonth.compare(arg, new Temporal.PlainYearMonth(2016, 12));
+assert.sameValue(result1, 0, "leap second is a valid ISO string for PlainYearMonth (first argument)");
+const result2 = Temporal.PlainYearMonth.compare(new Temporal.PlainYearMonth(2016, 12), arg);
+assert.sameValue(result2, 0, "leap second is a valid ISO string for PlainYearMonth (second argument)");
+
+arg = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+
+const result3 = Temporal.PlainYearMonth.compare(arg, new Temporal.PlainYearMonth(2016, 12));
+assert.sameValue(result3, 0, "second: 60 is ignored in property bag for PlainYearMonth (first argument)");
+const result4 = Temporal.PlainYearMonth.compare(new Temporal.PlainYearMonth(2016, 12), arg);
+assert.sameValue(result4, 0, "second: 60 is ignored in property bag for PlainYearMonth (second argument)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/length.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/length.js
new file mode 100644
index 0000000000..dedfbae35d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/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.plainyearmonth.compare
+description: Temporal.PlainYearMonth.compare.length is 2
+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.PlainYearMonth.compare, "length", {
+ value: 2,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/name.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/name.js
new file mode 100644
index 0000000000..a76b7a59e8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/name.js
@@ -0,0 +1,26 @@
+// |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.plainyearmonth.compare
+description: Temporal.PlainYearMonth.compare.name is "compare".
+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.PlainYearMonth.compare, "name", {
+ value: "compare",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/not-a-constructor.js
new file mode 100644
index 0000000000..d83397671c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/not-a-constructor.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.plainyearmonth.compare
+description: Temporal.PlainYearMonth.compare 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.PlainYearMonth.compare();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainYearMonth.compare), false,
+ "isConstructor(Temporal.PlainYearMonth.compare)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/prop-desc.js
new file mode 100644
index 0000000000..a8a01f21da
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/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.plainyearmonth.compare
+description: The "compare" property of Temporal.PlainYearMonth
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainYearMonth.compare,
+ "function",
+ "`typeof PlainYearMonth.compare` is `function`"
+);
+
+verifyProperty(Temporal.PlainYearMonth, "compare", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/proto-in-calendar-fields.js
new file mode 100644
index 0000000000..d7757e3a18
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/proto-in-calendar-fields.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.plainyearmonth.prototype.compare
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+
+assert.throws(RangeError, () => Temporal.PlainYearMonth.compare(arg, new Temporal.PlainYearMonth(2019, 6)));
+assert.throws(RangeError, () => Temporal.PlainYearMonth.compare(new Temporal.PlainYearMonth(2019, 6), arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/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/PlainYearMonth/compare/use-internal-slots.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/use-internal-slots.js
new file mode 100644
index 0000000000..95f9927c41
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/use-internal-slots.js
@@ -0,0 +1,26 @@
+// |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.plainyearmonth.compare
+description: compare() ignores the observable properties and uses internal slots
+features: [Temporal]
+---*/
+
+function CustomError() {}
+
+class AvoidGettersYearMonth extends Temporal.PlainYearMonth {
+ get year() {
+ throw new CustomError();
+ }
+ get month() {
+ throw new CustomError();
+ }
+}
+
+const one = new AvoidGettersYearMonth(2000, 5);
+const two = new AvoidGettersYearMonth(2006, 3);
+assert.sameValue(Temporal.PlainYearMonth.compare(one, two), -1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/year-zero.js
new file mode 100644
index 0000000000..50486522de
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/year-zero.js
@@ -0,0 +1,34 @@
+// |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.
+
+/*---
+description: Negative zero, as an extended year, fails
+esid: sec-temporal.plainyearmonth.compare
+features: [Temporal]
+---*/
+
+const ok = new Temporal.PlainYearMonth(2000, 5);
+const invalidStrings = [
+ "-000000-06",
+ "-000000-06-24",
+ "-000000-06-24T15:43:27",
+ "-000000-06-24T15:43:27+01:00",
+ "-000000-06-24T15:43:27+00:00[UTC]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainYearMonth.compare(arg, ok),
+ "Cannot use minus zero as extended year (first argument)"
+ );
+
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainYearMonth.compare(ok, arg),
+ "Cannot use minus zero as extended year (second argument)"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/constructor.js
new file mode 100644
index 0000000000..7b94464b55
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/constructor.js
@@ -0,0 +1,15 @@
+// |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.plainyearmonth
+description: Temporal.PlainYearMonth constructor cannot be called as a function
+info: |
+ 1. If NewTarget is undefined, throw a TypeError exception.
+features: [Temporal]
+---*/
+
+assert.throws(TypeError, () => Temporal.PlainYearMonth(1970, 1));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..84532a349e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-builtin-calendar-no-array-iteration.js
@@ -0,0 +1,23 @@
+// |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.plainyearmonth.from
+description: >
+ Calling the method with a property bag argument with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const arg = { year: 2000, month: 5, calendar: "iso8601" };
+Temporal.PlainYearMonth.from(arg);
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-number.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-number.js
new file mode 100644
index 0000000000..d4e9bb0eae
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-number.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.plainyearmonth.from
+description: A number is invalid in place of an ISO string for Temporal.PlainYearMonth
+features: [Temporal]
+---*/
+
+const numbers = [
+ 1,
+ 201906,
+ -201906,
+ 1234567,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => Temporal.PlainYearMonth.from(arg),
+ `A number (${arg}) is not a valid ISO string for PlainYearMonth`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-object.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-object.js
new file mode 100644
index 0000000000..9c63069bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-object.js
@@ -0,0 +1,50 @@
+// |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.plainyearmonth.from
+description: An object argument
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from({ year: 2019, monthCode: "M11" }),
+ 2019, 11, "M11", "Only monthCode");
+TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from({ year: 2019, month: 11 }),
+ 2019, 11, "M11", "Only month");
+assert.throws(RangeError,
+ () => Temporal.PlainYearMonth.from({ year: 2019, month: 11, monthCode: "M12" }),
+ "Mismatch between month and monthCode");
+
+const monthDayItem = { year: 2019, month: 11, get day() { throw new Test262Error("should not read the day property") } };
+TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from(monthDayItem),
+ 2019, 11, "M11", "month with day");
+
+const monthCodeDayItem = { year: 2019, monthCode: "M11", get day() { throw new Test262Error("should not read the day property") } };
+TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from(monthCodeDayItem),
+ 2019, 11, "M11", "monthCode with day");
+
+assert.throws(TypeError,
+ () => Temporal.PlainYearMonth.from({}),
+ "No properties");
+assert.throws(TypeError,
+ () => Temporal.PlainYearMonth.from({ year: 2019 }),
+ "Only year");
+assert.throws(TypeError,
+ () => Temporal.PlainYearMonth.from({ year: 2019, months: 6 }),
+ "Year and plural 'months'");
+assert.throws(TypeError,
+ () => Temporal.PlainYearMonth.from({ month: 6 }),
+ "Only month");
+assert.throws(TypeError,
+ () => Temporal.PlainYearMonth.from({ monthCode: "M06" }),
+ "Only monthCode");
+assert.throws(TypeError,
+ () => Temporal.PlainYearMonth.from({ year: undefined, month: 6 }),
+ "year explicit undefined");
+
+TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from({ year: 1976, month: 11, months: 12 }),
+ 1976, 11, "M11", "Plural property ignored");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-plaindate.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-plaindate.js
new file mode 100644
index 0000000000..05becd1028
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-plaindate.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.plainyearmonth.from
+description: A PlainYearMonth argument is cloned
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainDate = Temporal.PlainDate.from("1976-11-18");
+const plainYearMonth = Temporal.PlainYearMonth.from(plainDate);
+TemporalHelpers.assertPlainYearMonth(plainYearMonth, 1976, 11, "M11");
+const fields = plainYearMonth.getISOFields();
+assert.sameValue(fields.calendar, "iso8601", "calendar slot should store a string");
+assert.sameValue(fields.isoDay, 1, "isoDay");
+assert.sameValue(fields.isoMonth, 11, "isoMonth");
+assert.sameValue(fields.isoYear, 1976, "isoYear");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-plainyearmonth.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-plainyearmonth.js
new file mode 100644
index 0000000000..23d8832f2b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-plainyearmonth.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.plainyearmonth.from
+description: A PlainYearMonth object is copied, not returned directly
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const orig = new Temporal.PlainYearMonth(2000, 5, undefined, 7);
+const result = Temporal.PlainYearMonth.from(orig);
+
+TemporalHelpers.assertPlainYearMonth(
+ result,
+ 2000, 5, "M05",
+ "PlainYearMonth is copied",
+ /* era = */ undefined, /* eraYear = */ undefined, /* isoDay = */ 7
+);
+
+assert.sameValue(result.getISOFields().calendar, orig.getISOFields().calendar, "Calendar is copied");
+
+assert.notSameValue(
+ result,
+ orig,
+ "When a PlainYearMonth is given, the returned value is not the original PlainYearMonth"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-propertybag-calendar-case-insensitive.js
new file mode 100644
index 0000000000..1b09db947a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-propertybag-calendar-case-insensitive.js
@@ -0,0 +1,18 @@
+// |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.plainyearmonth.from
+description: The calendar name is case-insensitive
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = "IsO8601";
+
+const arg = { year: 2019, monthCode: "M06", calendar };
+const result = Temporal.PlainYearMonth.from(arg);
+TemporalHelpers.assertPlainYearMonth(result, 2019, 6, "M06", "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-propertybag-calendar-leap-second.js
new file mode 100644
index 0000000000..22b71d7e9c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-propertybag-calendar-leap-second.js
@@ -0,0 +1,22 @@
+// |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.plainyearmonth.from
+description: Leap second is a valid ISO string for a calendar in a property bag
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = "2016-12-31T23:59:60";
+
+const arg = { year: 2019, monthCode: "M06", calendar };
+const result = Temporal.PlainYearMonth.from(arg);
+TemporalHelpers.assertPlainYearMonth(
+ result,
+ 2019, 6, "M06",
+ "leap second is a valid ISO string for calendar"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-propertybag-calendar-number.js
new file mode 100644
index 0000000000..83ccb1205b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-propertybag-calendar-number.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.plainyearmonth.from
+description: A number as calendar in a property bag is not accepted
+features: [Temporal]
+---*/
+
+const numbers = [
+ 1,
+ 19970327,
+ -19970327,
+ 1234567890,
+];
+
+for (const calendar of numbers) {
+ const arg = { year: 2019, monthCode: "M06", calendar };
+ assert.throws(
+ TypeError,
+ () => Temporal.PlainYearMonth.from(arg),
+ "Numbers cannot be used as a calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-propertybag-calendar-string.js
new file mode 100644
index 0000000000..89c9e2fd19
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-propertybag-calendar-string.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.plainyearmonth.from
+description: A calendar ID is valid input for Calendar
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = "iso8601";
+
+const arg = { year: 2019, monthCode: "M06", calendar };
+const result = Temporal.PlainYearMonth.from(arg);
+TemporalHelpers.assertPlainYearMonth(result, 2019, 6, "M06", `Calendar created from string "${calendar}"`);
+assert.sameValue(result.getISOFields().calendar, "iso8601", "calendar slot stores a string");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-propertybag-calendar-wrong-type.js
new file mode 100644
index 0000000000..043b3d9d48
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-propertybag-calendar-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.plainyearmonth.from
+description: >
+ Appropriate error thrown when a calendar property from a property bag cannot
+ be converted to a calendar object or string
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [calendar, description] of primitiveTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ typeof calendar === 'string' ? RangeError : TypeError,
+ () => Temporal.PlainYearMonth.from(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+ [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields()
+];
+
+for (const [calendar, description] of typeErrorTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(TypeError, () => Temporal.PlainYearMonth.from(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/PlainYearMonth/from/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-propertybag-calendar-year-zero.js
new file mode 100644
index 0000000000..9d039d2365
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-propertybag-calendar-year-zero.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.plainyearmonth.from
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T17:45",
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+01:00",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainYearMonth.from(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..833cb89b30
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-calendar-annotation.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.plainyearmonth.from
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2019-12-15T15:23[u-ca=iso8601]", "without time zone"],
+ ["2019-12-15T15:23[UTC][u-ca=iso8601]", "with time zone"],
+ ["2019-12-15T15:23[!u-ca=iso8601]", "with ! and no time zone"],
+ ["2019-12-15T15:23[UTC][!u-ca=iso8601]", "with ! and time zone"],
+ ["2019-12-15T15:23[u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.PlainYearMonth.from(arg);
+
+ TemporalHelpers.assertPlainYearMonth(
+ result,
+ 2019, 12, "M12",
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..ff157f3086
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-critical-unknown-annotation.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.plainyearmonth.from
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "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]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainYearMonth.from(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..f1eb91f5f6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-date-with-utc-offset.js
@@ -0,0 +1,54 @@
+// |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.plainyearmonth.from
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const validStrings = [
+ "2019-12[Africa/Abidjan]",
+ "2019-12[!Africa/Abidjan]",
+ "2019-12[u-ca=iso8601]",
+ "2019-12[Africa/Abidjan][u-ca=iso8601]",
+ "2019-12-15T00+00:00",
+ "2019-12-15T00+00:00[UTC]",
+ "2019-12-15T00+00:00[!UTC]",
+ "2019-12-15T00-02:30[America/St_Johns]",
+];
+
+for (const arg of validStrings) {
+ const result = Temporal.PlainYearMonth.from(arg);
+
+ TemporalHelpers.assertPlainYearMonth(
+ result,
+ 2019, 12, "M12",
+ `"${arg}" is a valid UTC offset with time for PlainYearMonth`
+ );
+}
+
+const invalidStrings = [
+ "2022-09[u-ca=hebrew]",
+ "2022-09Z",
+ "2022-09+01:00",
+ "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,
+ () => Temporal.PlainYearMonth.from(arg),
+ `"${arg}" UTC offset without time is not valid for PlainYearMonth`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-invalid.js
new file mode 100644
index 0000000000..4466c79b3d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-invalid.js
@@ -0,0 +1,17 @@
+// |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.plainyearmonth.from
+description: An invalid ISO string is never supported
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const input of TemporalHelpers.ISO.plainYearMonthStringsInvalid()) {
+ assert.throws(RangeError, () => Temporal.PlainYearMonth.from(input, { overflow: "reject" }));
+ assert.throws(RangeError, () => Temporal.PlainYearMonth.from(input, { overflow: "constrain" }));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..1cf41e7de7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/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.plainyearmonth.from
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[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]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainYearMonth.from(arg),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..87c47ab87d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-multiple-time-zone.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.plainyearmonth.from
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "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]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainYearMonth.from(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-time-separators.js
new file mode 100644
index 0000000000..964481e3c4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-time-separators.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.plainyearmonth.from
+description: Time separator in string argument can vary
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2019-12-15T15:23", "uppercase T"],
+ ["2019-12-15t15:23", "lowercase T"],
+ ["2019-12-15 15:23", "space between date and time"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.PlainYearMonth.from(arg);
+
+ TemporalHelpers.assertPlainYearMonth(
+ result,
+ 2019, 12, "M12",
+ `variant time separators (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..cf078a1a26
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-time-zone-annotation.js
@@ -0,0 +1,33 @@
+// |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.plainyearmonth.from
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2019-12-15T15:23[Asia/Kolkata]", "named, with no offset"],
+ ["2019-12-15T15:23[!Europe/Vienna]", "named, with ! and no offset"],
+ ["2019-12-15T15:23[+00:00]", "numeric, with no offset"],
+ ["2019-12-15T15:23[!-02:30]", "numeric, with ! and no offset"],
+ ["2019-12-15T15:23+00:00[UTC]", "named, with offset"],
+ ["2019-12-15T15:23+00:00[!Africa/Abidjan]", "named, with offset and !"],
+ ["2019-12-15T15:23+00:00[+01:00]", "numeric, with offset"],
+ ["2019-12-15T15:23+00:00[!-08:00]", "numeric, with offset and !"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.PlainYearMonth.from(arg);
+
+ TemporalHelpers.assertPlainYearMonth(
+ result,
+ 2019, 12, "M12",
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-trailing-junk.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-trailing-junk.js
new file mode 100644
index 0000000000..248ccdca42
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-trailing-junk.js
@@ -0,0 +1,13 @@
+// |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.plainyearmonth.from
+description: RangeError thrown if a string with trailing junk is used as a PlainYearMonth
+features: [Temporal]
+---*/
+
+assert.throws(RangeError, () => Temporal.PlainYearMonth.from("1976-11junk"));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..982ae9ad9a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-unknown-annotation.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.plainyearmonth.from
+description: Various forms of unknown annotation
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2019-12-15T15:23[foo=bar]", "alone"],
+ ["2019-12-15T15:23[UTC][foo=bar]", "with time zone"],
+ ["2019-12-15T15:23[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["2019-12-15T15:23[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["2019-12-15T15:23[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.PlainYearMonth.from(arg);
+
+ TemporalHelpers.assertPlainYearMonth(
+ result,
+ 2019, 12, "M12",
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..4841b03596
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-with-utc-designator.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.plainyearmonth.from
+description: RangeError thrown if a string with UTC designator is used as a PlainYearMonth
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "2019-10-01T09:00:00Z",
+ "2019-10-01T09:00:00Z[UTC]",
+];
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainYearMonth.from(arg),
+ "String with UTC designator should not be valid as a PlainYearMonth"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string.js
new file mode 100644
index 0000000000..59b9f44fbe
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string.js
@@ -0,0 +1,33 @@
+// |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.plainyearmonth.from
+description: A string is parsed into the correct object when passed as the argument
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const input of TemporalHelpers.ISO.plainYearMonthStringsValid()) {
+ const plainYearMonth = Temporal.PlainYearMonth.from(input);
+ TemporalHelpers.assertPlainYearMonth(plainYearMonth, 1976, 11, "M11");
+ const fields = plainYearMonth.getISOFields();
+ assert.sameValue(fields.calendar, "iso8601", "calendar slot should store a string");
+ assert.sameValue(fields.isoDay, 1, "isoDay");
+ assert.sameValue(fields.isoMonth, 11, "isoMonth");
+ assert.sameValue(fields.isoYear, 1976, "isoYear");
+}
+
+for (const input of TemporalHelpers.ISO.plainYearMonthStringsValidNegativeYear()) {
+ const plainYearMonth = Temporal.PlainYearMonth.from(input);
+ TemporalHelpers.assertPlainYearMonth(plainYearMonth, -9999, 11, "M11");
+ const fields = plainYearMonth.getISOFields();
+ assert.sameValue(fields.calendar, "iso8601", "calendar slot should store a string");
+ assert.sameValue(fields.isoDay, 1, "isoDay");
+ assert.sameValue(fields.isoMonth, 11, "isoMonth");
+ assert.sameValue(fields.isoYear, -9999, "isoYear");
+ assert.sameValue(plainYearMonth.toString(), "-009999-11");
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-wrong-type.js
new file mode 100644
index 0000000000..de5a5590f6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-wrong-type.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.plainyearmonth.from
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainYearMonth
+features: [BigInt, Symbol, Temporal]
+---*/
+
+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,
+ () => Temporal.PlainYearMonth.from(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.PlainYearMonth, "Temporal.PlainYearMonth, object"],
+ [Temporal.PlainYearMonth.prototype, "Temporal.PlainYearMonth.prototype, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => Temporal.PlainYearMonth.from(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/PlainYearMonth/from/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/builtin.js
new file mode 100644
index 0000000000..233cebcb81
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/builtin.js
@@ -0,0 +1,33 @@
+// |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.plainyearmonth.from
+description: Tests that Temporal.PlainYearMonth.from meets the requirements for built-in objects
+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.PlainYearMonth.from),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainYearMonth.from),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainYearMonth.from),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainYearMonth.from.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/calendar-datefromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..e019e44b5c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/calendar-datefromfields-called-with-null-prototype-fields.js
@@ -0,0 +1,18 @@
+// |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.plainyearmonth.from
+description: >
+ Calendar.yearMonthFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const arg = { year: 2000, month: 5, calendar };
+Temporal.PlainYearMonth.from(arg);
+assert.sameValue(calendar.yearMonthFromFieldsCallCount, 1, "yearMonthFromFields should be called on the property bag's calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/calendar-fields-iterable.js
new file mode 100644
index 0000000000..7f20933b28
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/calendar-fields-iterable.js
@@ -0,0 +1,32 @@
+// |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.plainyearmonth.from
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plainyearmonth.from step 3:
+ 3. Return ? ToTemporalYearMonth(_item_, _options_).
+ sec-temporal-totemporalyearmonth step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+Temporal.PlainYearMonth.from({ year: 2000, month: 5, calendar });
+
+assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/calendar-temporal-object.js
new file mode 100644
index 0000000000..eb863f5961
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/calendar-temporal-object.js
@@ -0,0 +1,29 @@
+// |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.plainyearmonth.from
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plainyearmonth.from step 3:
+ 3. Return ? ToTemporalYearMonth(_item_, _options_).
+ sec-temporal-totemporaldate step 2.b:
+ b. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject, calendar) => {
+ const result = Temporal.PlainYearMonth.from({ year: 2000, month: 5, calendar: temporalObject });
+ assert.sameValue(result.getCalendar(), calendar, "Temporal object coerced to calendar");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..83c2be28c8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/constructor-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |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.plainyearmonth.prototype.from
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+
+assert.throws(RangeError, () => Temporal.PlainYearMonth.from(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/duplicate-calendar-fields.js
new file mode 100644
index 0000000000..066993f13a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/duplicate-calendar-fields.js
@@ -0,0 +1,19 @@
+// |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.plainyearmonth.prototype.from
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+
+for (const extra_fields of [['foo', 'foo'], ['month'], ['monthCode'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar };
+
+ assert.throws(RangeError, () => Temporal.PlainYearMonth.from(arg));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..c8e260b3b1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/infinity-throws-rangeerror.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.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plainyearmonth.from
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const base = { year: 2000, month: 5 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month"].forEach((prop) => {
+ ["constrain", "reject"].forEach((overflow) => {
+ assert.throws(RangeError, () => Temporal.PlainYearMonth.from({ ...base, [prop]: inf }, { overflow }), `${prop} property cannot be ${inf} (overflow ${overflow}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => Temporal.PlainYearMonth.from({ ...base, [prop]: obj }, { overflow }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/leap-second.js
new file mode 100644
index 0000000000..cf9eae3c06
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/leap-second.js
@@ -0,0 +1,44 @@
+// |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.plainyearmonth.from
+description: Leap second is a valid ISO string for PlainYearMonth
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+let arg = "2016-12-31T23:59:60";
+
+const result1 = Temporal.PlainYearMonth.from(arg);
+TemporalHelpers.assertPlainYearMonth(
+ result1,
+ 2016, 12, "M12",
+ "leap second is a valid ISO string for PlainYearMonth"
+);
+
+const result2 = Temporal.PlainYearMonth.from(arg, { overflow: "reject" });
+TemporalHelpers.assertPlainYearMonth(
+ result2,
+ 2016, 12, "M12",
+ "leap second is a valid ISO string for PlainYearMonth"
+);
+
+arg = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+
+const result3 = Temporal.PlainYearMonth.from(arg);
+TemporalHelpers.assertPlainYearMonth(
+ result3,
+ 2016, 12, "M12",
+ "second: 60 is ignored in property bag for PlainYearMonth"
+);
+
+const result4 = Temporal.PlainYearMonth.from(arg, { overflow: "reject" });
+TemporalHelpers.assertPlainYearMonth(
+ result4,
+ 2016, 12, "M12",
+ "second: 60 is ignored in property bag for PlainYearMonth even with overflow: reject"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/length.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/length.js
new file mode 100644
index 0000000000..f652f2ef2e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/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.plainyearmonth.from
+description: Temporal.PlainYearMonth.from.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.PlainYearMonth.from, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/limits.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/limits.js
new file mode 100644
index 0000000000..2cc1f868bd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/limits.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.plainyearmonth.from
+description: PlainYearMonth.from enforces the supported range
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+["reject", "constrain"].forEach((overflow) => {
+ [{ year: -271821, month: 3 }, { year: 275760, month: 10 }, "-271821-03", "+275760-10"].forEach((value) => {
+ assert.throws(RangeError, () => Temporal.PlainYearMonth.from(value, { overflow }),
+ `${JSON.stringify(value)} with ${overflow}`);
+ });
+});
+
+TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from({ year: -271821, month: 4 }),
+ -271821, 4, "M04", "min object");
+TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from({ year: 275760, month: 9 }),
+ 275760, 9, "M09", "max object");
+TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from("-271821-04"),
+ -271821, 4, "M04", "min string");
+TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from("+275760-09"),
+ 275760, 9, "M09", "max string");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/name.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/name.js
new file mode 100644
index 0000000000..1442021f14
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/name.js
@@ -0,0 +1,26 @@
+// |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.plainyearmonth.from
+description: Temporal.PlainYearMonth.from.name is "from".
+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.PlainYearMonth.from, "name", {
+ value: "from",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/not-a-constructor.js
new file mode 100644
index 0000000000..df8fdb4b08
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/not-a-constructor.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.plainyearmonth.from
+description: Temporal.PlainYearMonth.from 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.PlainYearMonth.from();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainYearMonth.from), false,
+ "isConstructor(Temporal.PlainYearMonth.from)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/observable-get-overflow-argument-primitive.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/observable-get-overflow-argument-primitive.js
new file mode 100644
index 0000000000..bb5d83b298
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/observable-get-overflow-argument-primitive.js
@@ -0,0 +1,37 @@
+// |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.plainyearmonth.from
+description: overflow property is extracted with string argument.
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "ownKeys options",
+ "getOwnPropertyDescriptor options.overflow",
+ "get options.overflow",
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+];
+
+let actual = [];
+const options = TemporalHelpers.propertyBagObserver(actual, { overflow: "constrain" }, "options");
+
+const result = Temporal.PlainYearMonth.from("2021-05", options);
+assert.compareArray(actual, expected, "Successful call");
+TemporalHelpers.assertPlainYearMonth(result, 2021, 5, "M05");
+
+actual.splice(0); // empty it for the next check
+const failureExpected = [
+ "ownKeys options",
+ "getOwnPropertyDescriptor options.overflow",
+ "get options.overflow",
+];
+
+assert.throws(TypeError, () => Temporal.PlainYearMonth.from(7, options));
+assert.compareArray(actual, failureExpected, "Failing call");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/observable-get-overflow-argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/observable-get-overflow-argument-string-invalid.js
new file mode 100644
index 0000000000..567cc6b653
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/observable-get-overflow-argument-string-invalid.js
@@ -0,0 +1,24 @@
+// |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.plainyearmonth.from
+description: overflow property is extracted with ISO-invalid string argument.
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "ownKeys options",
+ "getOwnPropertyDescriptor options.overflow",
+ "get options.overflow",
+];
+
+let actual = [];
+const options = TemporalHelpers.propertyBagObserver(actual, { overflow: "constrain" }, "options");
+
+assert.throws(RangeError, () => Temporal.PlainYearMonth.from("2020-13", options));
+assert.compareArray(actual, expected);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/options-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/options-invalid.js
new file mode 100644
index 0000000000..71a0c690bf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/options-invalid.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.plainyearmonth.from
+description: TypeError thrown when a primitive is passed as the options argument
+features: [Temporal]
+---*/
+
+const items = [
+ { year: 2000, month: 11 },
+ "2000-11",
+ new Temporal.PlainYearMonth(2000, 11),
+];
+const values = [null, true, "hello", Symbol("foo"), 1, 1n];
+
+for (const item of items) {
+ for (const badOptions of values) {
+ assert.throws(TypeError, () => Temporal.PlainYearMonth.from(item, badOptions));
+ }
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/options-object.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/options-object.js
new file mode 100644
index 0000000000..e836ed81bc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/options-object.js
@@ -0,0 +1,22 @@
+// |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.plainyearmonth.prototype.from
+description: Empty object may be used as options
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.assertPlainYearMonth(
+ Temporal.PlainYearMonth.from({ year: 2021, monthCode: "M01" }, {}), 2021, 1, "M01",
+ "options may be an empty plain object"
+);
+
+TemporalHelpers.assertPlainYearMonth(
+ Temporal.PlainYearMonth.from({ year: 2021, monthCode: "M01" }, () => {}), 2021, 1, "M01",
+ "options may be an empty function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/options-undefined.js
new file mode 100644
index 0000000000..1a4d3c9afa
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/options-undefined.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.plainyearmonth.from
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const fields = { year: 2000, month: 13 };
+
+const explicit = Temporal.PlainYearMonth.from(fields, undefined);
+assert.sameValue(explicit.month, 12, "default overflow is constrain");
+
+const implicit = Temporal.PlainYearMonth.from(fields);
+assert.sameValue(implicit.month, 12, "default overflow is constrain");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/options-wrong-type.js
new file mode 100644
index 0000000000..5b937ec8b7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/options-wrong-type.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.plainyearmonth.from
+description: TypeError thrown when options argument is a primitive
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const badOptions = [
+ null,
+ true,
+ "some string",
+ Symbol(),
+ 1,
+ 2n,
+];
+
+for (const value of badOptions) {
+ assert.throws(TypeError, () => Temporal.PlainYearMonth.from({ year: 2021, monthCode: "M01" }, value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/order-of-operations.js
new file mode 100644
index 0000000000..7d9082ffa1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/order-of-operations.js
@@ -0,0 +1,80 @@
+// |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.plainyearmonth.from
+description: Properties on an object passed to from() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ // CopyDataProperties
+ "ownKeys options",
+ "getOwnPropertyDescriptor options.overflow",
+ "get options.overflow",
+ "getOwnPropertyDescriptor options.extra",
+ "get options.extra",
+ // GetTemporalCalendarSlotValueWithISODefault
+ "get fields.calendar",
+ "has fields.calendar.dateAdd",
+ "has fields.calendar.dateFromFields",
+ "has fields.calendar.dateUntil",
+ "has fields.calendar.day",
+ "has fields.calendar.dayOfWeek",
+ "has fields.calendar.dayOfYear",
+ "has fields.calendar.daysInMonth",
+ "has fields.calendar.daysInWeek",
+ "has fields.calendar.daysInYear",
+ "has fields.calendar.fields",
+ "has fields.calendar.id",
+ "has fields.calendar.inLeapYear",
+ "has fields.calendar.mergeFields",
+ "has fields.calendar.month",
+ "has fields.calendar.monthCode",
+ "has fields.calendar.monthDayFromFields",
+ "has fields.calendar.monthsInYear",
+ "has fields.calendar.weekOfYear",
+ "has fields.calendar.year",
+ "has fields.calendar.yearMonthFromFields",
+ "has fields.calendar.yearOfWeek",
+ // lookup
+ "get fields.calendar.fields",
+ "get fields.calendar.yearMonthFromFields",
+ // CalendarFields
+ "call fields.calendar.fields",
+ // PrepareTemporalFields
+ "get fields.month",
+ "get fields.month.valueOf",
+ "call fields.month.valueOf",
+ "get fields.monthCode",
+ "get fields.monthCode.toString",
+ "call fields.monthCode.toString",
+ "get fields.year",
+ "get fields.year.valueOf",
+ "call fields.year.valueOf",
+ // CalendarYearMonthFromFields
+ "call fields.calendar.yearMonthFromFields",
+ // inside Calendar.p.yearMonthFromFields
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+];
+const actual = [];
+
+const fields = TemporalHelpers.propertyBagObserver(actual, {
+ year: 1.7,
+ month: 1.7,
+ monthCode: "M01",
+ calendar: TemporalHelpers.calendarObserver(actual, "fields.calendar"),
+}, "fields");
+
+const options = TemporalHelpers.propertyBagObserver(actual, {
+ overflow: "constrain",
+ extra: "property",
+}, "options");
+
+Temporal.PlainYearMonth.from(fields, options);
+assert.compareArray(actual, expected, "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/overflow-constrain.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/overflow-constrain.js
new file mode 100644
index 0000000000..7d27364aa0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/overflow-constrain.js
@@ -0,0 +1,17 @@
+// |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.plainyearmonth.from
+description: Reject value for overflow option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const propertyBag = { year: 2000, month: 13 };
+const plainYearMonth = Temporal.PlainYearMonth.from(propertyBag, { overflow: "constrain" });
+TemporalHelpers.assertPlainYearMonth(plainYearMonth, 2000, 12, "M12", "default overflow is constrain");
+
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/overflow-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/overflow-invalid-string.js
new file mode 100644
index 0000000000..b47d26ae14
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/overflow-invalid-string.js
@@ -0,0 +1,43 @@
+// |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.plainyearmonth.from
+description: RangeError thrown when overflow option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-totemporalyearmonth steps 2–3:
+ 2. If Type(_item_) is Object, then
+ ...
+ e. Return ? YearMonthFromFields(_calendar_, _fields_, _options_).
+ 3. Perform ? ToTemporalOverflow(_options_).
+ sec-temporal.plainyearmonth.from steps 2–3:
+ 2. If Type(_item_) is Object and _item_ has an [[InitializedTemporalYearMonth]] internal slot, then
+ a. Perform ? ToTemporalOverflow(_options_).
+ b. Return ...
+ 3. Return ? ToTemporalYearMonth(_item_, _options_).
+features: [Temporal]
+---*/
+
+const validValues = [
+ new Temporal.PlainYearMonth(2000, 5),
+ { year: 2000, month: 5 },
+ "2000-05",
+];
+
+const badOverflows = ["", "CONSTRAIN", "balance", "other string", "constra\u0131n", "reject\0"];
+for (const value of validValues) {
+ for (const overflow of badOverflows) {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainYearMonth.from(value, { overflow }),
+ `invalid overflow ("${overflow}")`
+ );
+ }
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/overflow-reject.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/overflow-reject.js
new file mode 100644
index 0000000000..204f834311
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/overflow-reject.js
@@ -0,0 +1,14 @@
+// |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.plainyearmonth.from
+description: Reject value for overflow option
+features: [Temporal]
+---*/
+
+const bad = { year: 2019, month: 13 };
+assert.throws(RangeError, () => Temporal.PlainYearMonth.from(bad, { overflow: "reject" }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/overflow-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/overflow-undefined.js
new file mode 100644
index 0000000000..1ead004463
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/overflow-undefined.js
@@ -0,0 +1,48 @@
+// |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.plainyearmonth.from
+description: Fallback value for overflow option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-totemporalyearmonth steps 2–3:
+ 2. If Type(_item_) is Object, then
+ ...
+ e. Return ? YearMonthFromFields(_calendar_, _fields_, _options_).
+ 3. Perform ? ToTemporalOverflow(_options_).
+ sec-temporal.plainyearmonth.from steps 2–3:
+ 2. If Type(_item_) is Object and _item_ has an [[InitializedTemporalYearMonth]] internal slot, then
+ a. Perform ? ToTemporalOverflow(_options_).
+ b. Return ...
+ 3. Return ? ToTemporalYearMonth(_item_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const validValues = [
+ new Temporal.PlainYearMonth(2000, 5),
+ "2000-05",
+];
+validValues.forEach((value) => {
+ const explicit = Temporal.PlainYearMonth.from(value, { overflow: undefined });
+ TemporalHelpers.assertPlainYearMonth(explicit, 2000, 5, "M05", "overflow is ignored");
+ const implicit = Temporal.PlainYearMonth.from(value, {});
+ TemporalHelpers.assertPlainYearMonth(implicit, 2000, 5, "M05", "overflow is ignored");
+ const lambda = Temporal.PlainYearMonth.from(value, () => {});
+ TemporalHelpers.assertPlainYearMonth(lambda, 2000, 5, "M05", "overflow is ignored");
+});
+
+const propertyBag = { year: 2000, month: 13 };
+const explicit = Temporal.PlainYearMonth.from(propertyBag, { overflow: undefined });
+TemporalHelpers.assertPlainYearMonth(explicit, 2000, 12, "M12", "default overflow is constrain");
+const implicit = Temporal.PlainYearMonth.from(propertyBag, {});
+TemporalHelpers.assertPlainYearMonth(implicit, 2000, 12, "M12", "default overflow is constrain");
+const lambda = Temporal.PlainYearMonth.from(propertyBag, () => {});
+TemporalHelpers.assertPlainYearMonth(lambda, 2000, 12, "M12", "default overflow is constrain");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/overflow-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/overflow-wrong-type.js
new file mode 100644
index 0000000000..17564481ba
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/overflow-wrong-type.js
@@ -0,0 +1,37 @@
+// |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.plainyearmonth.from
+description: Type conversions for overflow option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-totemporalyearmonth steps 2–3:
+ 2. If Type(_item_) is Object, then
+ ...
+ e. Return ? YearMonthFromFields(_calendar_, _fields_, _options_).
+ 3. Perform ? ToTemporalOverflow(_options_).
+ sec-temporal.plainyearmonth.from steps 2–3:
+ 2. If Type(_item_) is Object and _item_ has an [[InitializedTemporalYearMonth]] internal slot, then
+ a. Perform ? ToTemporalOverflow(_options_).
+ b. Return ...
+ 3. Return ? ToTemporalYearMonth(_item_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const validValues = [
+ new Temporal.PlainYearMonth(2000, 5),
+ { year: 2000, month: 5 },
+ "2000-05",
+];
+validValues.forEach((value) => TemporalHelpers.checkStringOptionWrongType("overflow", "constrain",
+ (overflow) => Temporal.PlainYearMonth.from(value, { overflow }),
+ (result, descr) => TemporalHelpers.assertPlainYearMonth(result, 2000, 5, "M05", descr),
+));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/prop-desc.js
new file mode 100644
index 0000000000..1ca65fe582
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/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.plainyearmonth.from
+description: The "from" property of Temporal.PlainYearMonth
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainYearMonth.from,
+ "function",
+ "`typeof PlainYearMonth.from` is `function`"
+);
+
+verifyProperty(Temporal.PlainYearMonth, "from", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/proto-in-calendar-fields.js
new file mode 100644
index 0000000000..eedf95b18b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/proto-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |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.plainyearmonth.prototype.from
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+
+assert.throws(RangeError, () => Temporal.PlainYearMonth.from(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/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/PlainYearMonth/from/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/subclassing-ignored.js
new file mode 100644
index 0000000000..807269128f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/subclassing-ignored.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.plainyearmonth.from
+description: The receiver is never called when calling from()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnoredStatic(
+ Temporal.PlainYearMonth,
+ "from",
+ ["2000-05"],
+ (result) => TemporalHelpers.assertPlainYearMonth(result, 2000, 5, "M05"),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/year-zero.js
new file mode 100644
index 0000000000..07ffa27808
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/year-zero.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.plainyearmonth.from
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-06",
+ "-000000-06-24",
+ "-000000-06-24T15:43:27",
+ "-000000-06-24T15:43:27+01:00",
+ "-000000-06-24T15:43:27+00:00[UTC]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainYearMonth.from(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..ee276ac931
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/infinity-throws-rangeerror.js
@@ -0,0 +1,44 @@
+// |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.
+
+/*---
+description: Temporal.PlainYearMonth throws a RangeError if any numerical value is Infinity
+esid: sec-temporal.plainyearmonth
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const isoCalendar = Temporal.Calendar.from('iso8601');
+
+assert.throws(RangeError, () => new Temporal.PlainYearMonth(Infinity, 1));
+assert.throws(RangeError, () => new Temporal.PlainYearMonth(1970, Infinity));
+assert.throws(RangeError, () => new Temporal.PlainYearMonth(1970, 1, isoCalendar, Infinity));
+
+const O = (primitiveValue, propertyName) => (calls) => TemporalHelpers.toPrimitiveObserver(calls, primitiveValue, propertyName);
+const tests = [
+ [
+ "infinite year",
+ [O(Infinity, "year"), O(1, "month"), () => "iso8601", O(1, "day")],
+ ["get year.valueOf", "call year.valueOf"]
+ ],
+ [
+ "infinite month",
+ [O(1970, "year"), O(Infinity, "month"), () => "iso8601", O(1, "day")],
+ ["get year.valueOf", "call year.valueOf", "get month.valueOf", "call month.valueOf"]
+ ],
+ [
+ "infinite day",
+ [O(1970, "year"), O(1, "month"), () => "iso8601", O(Infinity, "day")],
+ ["get year.valueOf", "call year.valueOf", "get month.valueOf", "call month.valueOf", "get day.valueOf", "call day.valueOf"]
+ ],
+];
+
+for (const [description, args, expected] of tests) {
+ const actual = [];
+ const args_ = args.map((o) => o(actual));
+ assert.throws(RangeError, () => new Temporal.PlainYearMonth(...args_), description);
+ assert.compareArray(actual, expected, `${description} order of operations`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/length.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/length.js
new file mode 100644
index 0000000000..06d322c288
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/length.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.plainyearmonth
+description: Temporal.PlainYearMonth.length is 2
+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.PlainYearMonth, "length", {
+ value: 2,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/limits.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/limits.js
new file mode 100644
index 0000000000..92f711a442
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/limits.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.plainyearmonth
+description: Limits for the PlainYearMonth constructor.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+assert.throws(RangeError, () => new Temporal.PlainYearMonth(-271821, 3), "min");
+assert.throws(RangeError, () => new Temporal.PlainYearMonth(275760, 10), "max");
+TemporalHelpers.assertPlainYearMonth(new Temporal.PlainYearMonth(-271821, 4),
+ -271821, 4, "M04", "min");
+TemporalHelpers.assertPlainYearMonth(new Temporal.PlainYearMonth(-271821, 4, "iso8601", 18),
+ -271821, 4, "M04", "min with referenceISODay",
+ /* era = */ undefined, /* eraYear = */ undefined, /* referenceISODay = */ 18);
+TemporalHelpers.assertPlainYearMonth(new Temporal.PlainYearMonth(275760, 9),
+ 275760, 9, "M09", "max");
+TemporalHelpers.assertPlainYearMonth(new Temporal.PlainYearMonth(275760, 9, "iso8601", 14),
+ 275760, 9, "M09", "max with referenceISODay",
+ /* era = */ undefined, /* eraYear = */ undefined, /* referenceISODay = */ 14);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/missing-arguments.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/missing-arguments.js
new file mode 100644
index 0000000000..0c0f0d973e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/missing-arguments.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.plainyearmonth
+description: RangeError thrown after processing given args when invoked without all required args
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "valueOf year",
+];
+const actual = [];
+const args = [
+ { valueOf() { actual.push("valueOf year"); return 1; } },
+];
+
+assert.throws(RangeError, () => new Temporal.PlainYearMonth(...args));
+assert.compareArray(actual, expected, "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/name.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/name.js
new file mode 100644
index 0000000000..fec362e0a0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/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.plainyearmonth
+description: Temporal.PlainYearMonth.name is "PlainYearMonth"
+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.PlainYearMonth, "name", {
+ value: "PlainYearMonth",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/negative-infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/negative-infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..3bfb7a66f6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/negative-infinity-throws-rangeerror.js
@@ -0,0 +1,44 @@
+// |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.
+
+/*---
+description: Temporal.PlainYearMonth throws a RangeError if any numerical value is -Infinity
+esid: sec-temporal.plainyearmonth
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const isoCalendar = Temporal.Calendar.from('iso8601');
+
+assert.throws(RangeError, () => new Temporal.PlainYearMonth(-Infinity, 1));
+assert.throws(RangeError, () => new Temporal.PlainYearMonth(1970, -Infinity));
+assert.throws(RangeError, () => new Temporal.PlainYearMonth(1970, 1, isoCalendar, -Infinity));
+
+const O = (primitiveValue, propertyName) => (calls) => TemporalHelpers.toPrimitiveObserver(calls, primitiveValue, propertyName);
+const tests = [
+ [
+ "infinite year",
+ [O(-Infinity, "year"), O(1, "month"), () => "iso8601", O(1, "day")],
+ ["get year.valueOf", "call year.valueOf"]
+ ],
+ [
+ "infinite month",
+ [O(1970, "year"), O(-Infinity, "month"), () => "iso8601", O(1, "day")],
+ ["get year.valueOf", "call year.valueOf", "get month.valueOf", "call month.valueOf"]
+ ],
+ [
+ "infinite day",
+ [O(1970, "year"), O(1, "month"), () => "iso8601", O(-Infinity, "day")],
+ ["get year.valueOf", "call year.valueOf", "get month.valueOf", "call month.valueOf", "get day.valueOf", "call day.valueOf"]
+ ],
+];
+
+for (const [description, args, expected] of tests) {
+ const actual = [];
+ const args_ = args.map((o) => o(actual));
+ assert.throws(RangeError, () => new Temporal.PlainYearMonth(...args_), description);
+ assert.compareArray(actual, expected, `${description} order of operations`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prop-desc.js
new file mode 100644
index 0000000000..fc3afdc33d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/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.plainyearmonth
+description: The "PlainYearMonth" property of Temporal
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainYearMonth,
+ "function",
+ "`typeof PlainYearMonth` is `function`"
+);
+
+verifyProperty(Temporal, "PlainYearMonth", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-duration-max.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-duration-max.js
new file mode 100644
index 0000000000..7189fa8806
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-duration-max.js
@@ -0,0 +1,58 @@
+// |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.plainyearmonth.prototype.add
+description: Maximum allowed duration
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(1970, 1);
+
+const maxCases = [
+ ["P273790Y8M12DT23H59M59.999999999S", "string with max years"],
+ [{ years: 273790, months: 8, days: 12, nanoseconds: 86399999999999 }, "property bag with max years"],
+ ["P3285488M12DT23H59M59.999999999S", "string with max months"],
+ [{ months: 3285488, days: 12, nanoseconds: 86399999999999 }, "property bag with max months"],
+ ["P14285714W2DT23H59M59.999999999S", "string with max weeks"],
+ [{ weeks: 14285714, days: 2, nanoseconds: 86399999999999 }, "property bag with max weeks"],
+ ["P100000000DT23H59M59.999999999S", "string with max days"],
+ [{ days: 100000000, nanoseconds: 86399999999999 }, "property bag with max days"],
+ ["PT2400000023H59M59.999999999S", "string with max hours"],
+ [{ hours: 2400000023, nanoseconds: 3599999999999 }, "property bag with max hours"],
+ ["PT144000001439M59.999999999S", "string with max minutes"],
+ [{ minutes: 144000001439, nanoseconds: 59999999999 }, "property bag with max minutes"],
+ ["PT8640000086399.999999999S", "string with max seconds"],
+ [{ seconds: 8640000086399, nanoseconds: 999999999 }, "property bag with max seconds"],
+];
+
+for (const [arg, descr] of maxCases) {
+ const result = instance.add(arg);
+ TemporalHelpers.assertPlainYearMonth(result, 275760, 9, "M09", `operation succeeds with ${descr}`);
+}
+
+const minCases = [
+ ["-P273790Y8M42DT23H59M59.999999999S", "string with min years"],
+ [{ years: -273790, months: -8, days: -42, nanoseconds: -86399999999999 }, "property bag with min years"],
+ ["-P3285488M42DT23H59M59.999999999S", "string with min months"],
+ [{ months: -3285488, days: -42, nanoseconds: -86399999999999 }, "property bag with min months"],
+ ["-P14285718W5DT23H59M59.999999999S", "string with min weeks"],
+ [{ weeks: -14285718, days: -5, nanoseconds: -86399999999999 }, "property bag with min weeks"],
+ ["-P100000031DT23H59M59.999999999S", "string with min days"],
+ [{ days: -100000031, nanoseconds: -86399999999999 }, "property bag with min days"],
+ ["-PT2400000767H59M59.999999999S", "string with min hours"],
+ [{ hours: -2400000767, nanoseconds: -3599999999999 }, "property bag with min hours"],
+ ["-PT144000046079M59.999999999S", "string with min minutes"],
+ [{ minutes: -144000046079, nanoseconds: -59999999999 }, "property bag with min minutes"],
+ ["-PT8640002764799.999999999S", "string with min seconds"],
+ [{ seconds: -8640002764799, nanoseconds: -999999999 }, "property bag with min seconds"],
+];
+
+for (const [arg, descr] of minCases) {
+ const result = instance.add(arg);
+ TemporalHelpers.assertPlainYearMonth(result, -271821, 4, "M04", `operation succeeds with ${descr}`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-duration-object.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-duration-object.js
new file mode 100644
index 0000000000..57fd0409c0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-duration-object.js
@@ -0,0 +1,16 @@
+// |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.plainyearmonth.prototype.add
+description: A Duration object is supported as the argument
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const nov94 = Temporal.PlainYearMonth.from("1994-11");
+const diff = Temporal.Duration.from("P18Y7M");
+TemporalHelpers.assertPlainYearMonth(nov94.add(diff), 2013, 6, "M06");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-duration-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-duration-out-of-range.js
new file mode 100644
index 0000000000..be8dff7e3e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-duration-out-of-range.js
@@ -0,0 +1,75 @@
+// |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.plainyearmonth.prototype.add
+description: Duration-like argument that is out of range
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(1970, 1);
+
+const cases = [
+ // 2^32 = 4294967296
+ ["P4294967296Y", "string with years > max"],
+ [{ years: 4294967296 }, "property bag with years > max"],
+ ["-P4294967296Y", "string with years < min"],
+ [{ years: -4294967296 }, "property bag with years < min"],
+ ["P4294967296M", "string with months > max"],
+ [{ months: 4294967296 }, "property bag with months > max"],
+ ["-P4294967296M", "string with months < min"],
+ [{ months: -4294967296 }, "property bag with months < min"],
+ ["P4294967296W", "string with weeks > max"],
+ [{ weeks: 4294967296 }, "property bag with weeks > max"],
+ ["-P4294967296W", "string with weeks < min"],
+ [{ weeks: -4294967296 }, "property bag with weeks < min"],
+
+ // ceil(max safe integer / 86400) = 104249991375
+ ["P104249991375D", "string with days > max"],
+ [{ days: 104249991375 }, "property bag with days > max"],
+ ["P104249991374DT24H", "string where hours balance into days > max"],
+ [{ days: 104249991374, hours: 24 }, "property bag where hours balance into days > max"],
+ ["-P104249991375D", "string with days < min"],
+ [{ days: -104249991375 }, "property bag with days < min"],
+ ["-P104249991374DT24H", "string where hours balance into days < min"],
+ [{ days: -104249991374, hours: -24 }, "property bag where hours balance into days < min"],
+
+ // ceil(max safe integer / 3600) = 2501999792984
+ ["PT2501999792984H", "string with hours > max"],
+ [{ hours: 2501999792984 }, "property bag with hours > max"],
+ ["PT2501999792983H60M", "string where minutes balance into hours > max"],
+ [{ hours: 2501999792983, minutes: 60 }, "property bag where minutes balance into hours > max"],
+ ["-PT2501999792984H", "string with hours < min"],
+ [{ hours: -2501999792984 }, "property bag with hours < min"],
+ ["-PT2501999792983H60M", "string where minutes balance into hours < min"],
+ [{ hours: -2501999792983, minutes: -60 }, "property bag where minutes balance into hours < min"],
+
+ // ceil(max safe integer / 60) = 150119987579017
+ ["PT150119987579017M", "string with minutes > max"],
+ [{ minutes: 150119987579017 }, "property bag with minutes > max"],
+ ["PT150119987579016M60S", "string where seconds balance into minutes > max"],
+ [{ minutes: 150119987579016, seconds: 60 }, "property bag where seconds balance into minutes > max"],
+ ["-PT150119987579017M", "string with minutes < min"],
+ [{ minutes: -150119987579017 }, "property bag with minutes < min"],
+ ["-PT150119987579016M60S", "string where seconds balance into minutes < min"],
+ [{ minutes: -150119987579016, seconds: -60 }, "property bag where seconds balance into minutes < min"],
+
+ // 2^53 = 9007199254740992
+ ["PT9007199254740992S", "string with seconds > max"],
+ [{ seconds: 9007199254740992 }, "property bag with seconds > max"],
+ [{ seconds: 9007199254740991, milliseconds: 1000 }, "property bag where milliseconds balance into seconds > max"],
+ [{ seconds: 9007199254740991, microseconds: 1000000 }, "property bag where microseconds balance into seconds > max"],
+ [{ seconds: 9007199254740991, nanoseconds: 1000000000 }, "property bag where nanoseconds balance into seconds > max"],
+ ["-PT9007199254740992S", "string with seconds < min"],
+ [{ seconds: -9007199254740992 }, "property bag with seconds < min"],
+ [{ seconds: -9007199254740991, milliseconds: -1000 }, "property bag where milliseconds balance into seconds < min"],
+ [{ seconds: -9007199254740991, microseconds: -1000000 }, "property bag where microseconds balance into seconds < min"],
+ [{ seconds: -9007199254740991, nanoseconds: -1000000000 }, "property bag where nanoseconds balance into seconds < min"],
+];
+
+for (const [arg, descr] of cases) {
+ assert.throws(RangeError, () => instance.add(arg), `${descr} is out of range`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-invalid-property.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-invalid-property.js
new file mode 100644
index 0000000000..ca3bc50ed5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-invalid-property.js
@@ -0,0 +1,31 @@
+// |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.plainyearmonth.prototype.add
+description: temporalDurationLike object must contain at least one correctly spelled property
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2000, 5);
+
+assert.throws(
+ TypeError,
+ () => instance.add({}),
+ "Throws TypeError if no property is present"
+);
+
+assert.throws(
+ TypeError,
+ () => instance.add({ nonsense: true }),
+ "Throws TypeError if no recognized property is present"
+);
+
+assert.throws(
+ TypeError,
+ () => instance.add({ sign: 1 }),
+ "Sign property is not recognized"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-lower-units.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-lower-units.js
new file mode 100644
index 0000000000..e7fe44f217
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-lower-units.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.plainyearmonth.prototype.add
+description: Using lower units in add() works
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const ym = Temporal.PlainYearMonth.from("2019-11");
+
+const tests = [
+ [{ days: 1 }, 2019, 11, "M11"],
+ [{ days: 29 }, 2019, 11, "M11"],
+ [{ hours: 1 }, 2019, 11, "M11"],
+ [{ minutes: 1 }, 2019, 11, "M11"],
+ [{ seconds: 1 }, 2019, 11, "M11"],
+ [{ milliseconds: 1 }, 2019, 11, "M11"],
+ [{ microseconds: 1 }, 2019, 11, "M11"],
+ [{ nanoseconds: 1 }, 2019, 11, "M11"],
+ [{ days: 30 }, 2019, 12, "M12"],
+ [{ days: 31 }, 2019, 12, "M12"],
+ [{ days: 60 }, 2019, 12, "M12"],
+ [{ days: 61 }, 2020, 1, "M01"],
+ [{ hours: 720 }, 2019, 12, "M12"],
+ [{ minutes: 43200 }, 2019, 12, "M12"],
+ [{ seconds: 2592000 }, 2019, 12, "M12"],
+ [{ milliseconds: 2592000_000 }, 2019, 12, "M12"],
+ [{ microseconds: 2592000_000_000 }, 2019, 12, "M12"],
+ [{ nanoseconds: 2592000_000_000_000 }, 2019, 12, "M12"],
+];
+
+for (const [argument, ...expected] of tests) {
+ TemporalHelpers.assertPlainYearMonth(ym.add(argument), ...expected, "no options");
+ TemporalHelpers.assertPlainYearMonth(ym.add(argument, { overflow: "constrain" }), ...expected, "constrain");
+ TemporalHelpers.assertPlainYearMonth(ym.add(argument, { overflow: "reject" }), ...expected, "reject");
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-mixed-sign.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-mixed-sign.js
new file mode 100644
index 0000000000..100bd0924c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-mixed-sign.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.plainyearmonth.prototype.add
+description: Positive and negative values in the temporalDurationLike argument are not acceptable
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2000, 5);
+
+["constrain", "reject"].forEach((overflow) => {
+ assert.throws(
+ RangeError,
+ () => instance.add({ hours: 1, minutes: -30 }, { overflow }),
+ `mixed positive and negative values always throw (overflow = "${overflow}")`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-not-object.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-not-object.js
new file mode 100644
index 0000000000..3543c747f5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-not-object.js
@@ -0,0 +1,22 @@
+// |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.plainyearmonth.prototype.add
+description: Passing a primitive other than string to add() throws
+features: [Symbol, Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2000, 5);
+assert.throws(TypeError, () => instance.add(undefined), "undefined");
+assert.throws(TypeError, () => instance.add(null), "null");
+assert.throws(TypeError, () => instance.add(true), "boolean");
+assert.throws(RangeError, () => instance.add(""), "empty string");
+assert.throws(TypeError, () => instance.add(Symbol()), "Symbol");
+assert.throws(TypeError, () => instance.add(7), "number");
+assert.throws(TypeError, () => instance.add(7n), "bigint");
+assert.throws(TypeError, () => instance.add([]), "array");
+assert.throws(TypeError, () => instance.add(() => {}), "function");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-object.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-object.js
new file mode 100644
index 0000000000..1ea7154439
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-object.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.plainyearmonth.prototype.add
+description: Passing an object to add() works
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const ym = Temporal.PlainYearMonth.from("2019-11");
+
+const tests = [
+ [{ months: 2 }, 2020, 1, "M01"],
+ [{ years: 1 }, 2020, 11, "M11"],
+ [{ months: -2 }, 2019, 9, "M09"],
+ [{ years: -1 }, 2018, 11, "M11"],
+];
+
+for (const [argument, ...expected] of tests) {
+ TemporalHelpers.assertPlainYearMonth(ym.add(argument), ...expected, "no options");
+ TemporalHelpers.assertPlainYearMonth(ym.add(argument, { overflow: "constrain" }), ...expected, "constrain");
+ TemporalHelpers.assertPlainYearMonth(ym.add(argument, { overflow: "reject" }), ...expected, "reject");
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-singular-properties.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-singular-properties.js
new file mode 100644
index 0000000000..47fdacb939
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-singular-properties.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.plainyearmonth.prototype.add
+description: Singular properties in the property bag are always ignored
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2000, 5);
+
+[
+ { year: 1 },
+ { month: 2 },
+ { week: 3 },
+ { day: 4 },
+ { hour: 5 },
+ { minute: 6 },
+ { second: 7 },
+ { millisecond: 8 },
+ { microsecond: 9 },
+ { nanosecond: 10 },
+].forEach((badObject) => {
+ assert.throws(TypeError, () => instance.add(badObject),
+ "Throw TypeError if temporalDurationLike is not valid");
+});
+
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-string-negative-fractional-units.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-string-negative-fractional-units.js
new file mode 100644
index 0000000000..525a26d87f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-string-negative-fractional-units.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.plainyearmonth.prototype.add
+description: Strings with fractional duration units are treated with the correct sign
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2000, 5);
+
+const resultHours = instance.add("-PT24.567890123H");
+TemporalHelpers.assertPlainYearMonth(resultHours, 2000, 5, "M05", "negative fractional hours");
+
+const resultMinutes = instance.add("-PT1440.567890123M");
+TemporalHelpers.assertPlainYearMonth(resultMinutes, 2000, 5, "M05", "negative fractional minutes");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-string.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-string.js
new file mode 100644
index 0000000000..f7f1095695
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-string.js
@@ -0,0 +1,16 @@
+// |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.plainyearmonth.prototype.add
+description: A string is parsed into the correct object when passed as the argument
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = Temporal.PlainYearMonth.from({ year: 2000, month: 5 });
+const result = instance.add("P3M");
+TemporalHelpers.assertPlainYearMonth(result, 2000, 8, "M08");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/branding.js
new file mode 100644
index 0000000000..7122227f4d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/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.plainyearmonth.prototype.add
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const add = Temporal.PlainYearMonth.prototype.add;
+
+assert.sameValue(typeof add, "function");
+
+const args = [new Temporal.Duration(5)];
+
+assert.throws(TypeError, () => add.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => add.apply(null, args), "null");
+assert.throws(TypeError, () => add.apply(true, args), "true");
+assert.throws(TypeError, () => add.apply("", args), "empty string");
+assert.throws(TypeError, () => add.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => add.apply(1, args), "1");
+assert.throws(TypeError, () => add.apply({}, args), "plain object");
+assert.throws(TypeError, () => add.apply(Temporal.PlainYearMonth, args), "Temporal.PlainYearMonth");
+assert.throws(TypeError, () => add.apply(Temporal.PlainYearMonth.prototype, args), "Temporal.PlainYearMonth.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..97ad7c2fa1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/builtin-calendar-no-array-iteration.js
@@ -0,0 +1,23 @@
+// |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.plainyearmonth.prototype.add
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.PlainYearMonth(2023, 5, "iso8601");
+instance.add({ years: 5, months: 2 });
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..4aa25749dc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plainyearmonth.prototype.add
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dateAddOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dateAdd");
+Object.defineProperty(Temporal.Calendar.prototype, "dateAdd", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("dateAdd should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainYearMonth(2000, 5, "iso8601", 1);
+instance.add(new Temporal.Duration(1));
+
+Object.defineProperty(Temporal.Calendar.prototype, "dateAdd", dateAddOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/builtin.js
new file mode 100644
index 0000000000..9fc7f42ab9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/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.plainyearmonth.prototype.add
+description: >
+ Tests that Temporal.PlainYearMonth.prototype.add
+ 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.PlainYearMonth.prototype.add),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainYearMonth.prototype.add),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainYearMonth.prototype.add),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainYearMonth.prototype.add.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-arguments-extra-options.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-arguments-extra-options.js
new file mode 100644
index 0000000000..a0e16c38bf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-arguments-extra-options.js
@@ -0,0 +1,50 @@
+// |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.plainyearmonth.prototype.add
+description: PlainYearMonth.prototype.add should pass extra fields in copied options objects.
+info: |
+ YearMonthFromFields ( calendar, fields [ , options ] )
+
+ 5. Let yearMonth be ? Invoke(calendar, "yearMonthFromFields", « fields, options »).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const expected = [
+ // CopyDataProperties
+ "ownKeys options",
+ "getOwnPropertyDescriptor options.extra",
+ "get options.extra",
+ // Temporal.Calendar.prototype.dateAdd
+ "get options.overflow",
+ // overwriting property in custom calendar dateAdd
+ "getOwnPropertyDescriptor options.overflow",
+];
+const options = TemporalHelpers.propertyBagObserver(actual, { extra: 5 }, "options");
+
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ dateAdd(date, duration, options) {
+ const result = super.dateAdd(date, duration, options);
+ options.overflow = 'meatloaf';
+ return result;
+ }
+ yearMonthFromFields(...args) {
+ assert.sameValue(args.length, 2, "args.length");
+ assert.sameValue(typeof args[0], "object", "args[0]");
+ assert.notSameValue(args[1], options, "args[1]");
+ return super.yearMonthFromFields(...args);
+ }
+}
+const plainYearMonth = new Temporal.PlainYearMonth(2000, 3, new CustomCalendar());
+const result = plainYearMonth.add({ months: 5 }, options);
+TemporalHelpers.assertPlainYearMonth(result, 2000, 8, "M08");
+assert.compareArray(actual, expected, "extra field options object order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-arguments.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-arguments.js
new file mode 100644
index 0000000000..1fc4f0632c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-arguments.js
@@ -0,0 +1,56 @@
+// |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.plainyearmonth.prototype.add
+description: PlainYearMonth.prototype.add should respect calendar arguments and pass copied options objects.
+info: |
+ YearMonthFromFields ( calendar, fields [ , options ] )
+
+ 5. Let yearMonth be ? Invoke(calendar, "yearMonthFromFields", « fields, options »).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const expected = [
+ // CopyDataProperties
+ "ownKeys options",
+ "getOwnPropertyDescriptor options.overflow",
+ "get options.overflow",
+ // Temporal.Calendar.prototype.dateAdd
+ "get options.overflow",
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+ // overwriting property in custom calendar dateAdd
+ "getOwnPropertyDescriptor options.overflow",
+ // Temporal.Calendar.prototype.yearMonthFromFields (toPrimitiveObserver copied but not options object)
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+];
+const options = TemporalHelpers.propertyBagObserver(actual, { overflow: "constrain" }, "options");
+
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ dateAdd(date, duration, options) {
+ const result = super.dateAdd(date, duration, options);
+ options.overflow = 'meatloaf';
+ return result;
+ }
+ yearMonthFromFields(...args) {
+ assert.sameValue(args.length, 2, "args.length");
+ assert.sameValue(typeof args[0], "object", "args[0]");
+ assert.notSameValue(args[1], options, "args[1]");
+ return super.yearMonthFromFields(...args);
+ }
+}
+
+const plainYearMonth = new Temporal.PlainYearMonth(2000, 3, new CustomCalendar());
+const result = plainYearMonth.add({ months: 10 }, options);
+TemporalHelpers.assertPlainYearMonth(result, 2001, 1, "M01");
+assert.compareArray(actual, expected, "copied options object order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-dateadd-called-with-plaindate-instance.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-dateadd-called-with-plaindate-instance.js
new file mode 100644
index 0000000000..272afc1e73
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-dateadd-called-with-plaindate-instance.js
@@ -0,0 +1,21 @@
+// |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.plainyearmonth.prototype.add
+description: Duration addition to PlainYearMonth calls Calendar.dateAdd the right number of times
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarDateAddPlainDateInstance();
+const instance = new Temporal.PlainYearMonth(1983, 3, calendar);
+TemporalHelpers.assertPlainYearMonth(instance.add({weeks: 5}), 1983, 4, 'M04', "Adding 5 weeks to March in is8601 calendar")
+assert.sameValue(calendar.dateAddCallCount, 1, "dateAdd called once with positive add");
+
+calendar.dateAddCallCount = 0;
+TemporalHelpers.assertPlainYearMonth(instance.add({weeks: -5}), 1983, 2, 'M02', "Adding -5 weeks to March in is8601 calendar")
+assert.sameValue(calendar.dateAddCallCount, 2, "dateAdd called 2 times with negative add");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-dateadd.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-dateadd.js
new file mode 100644
index 0000000000..38f18e0d1b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-dateadd.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.plainyearmonth.prototype.add
+description: PlainYearMonth.prototype.add should call dateAdd with the appropriate values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ dateAdd(plainDate, duration, options) {
+ ++calls;
+ TemporalHelpers.assertPlainDate(plainDate, 2000, 3, "M03", 1, "plainDate argument");
+ TemporalHelpers.assertDuration(duration, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, "duration argument");
+ assert.sameValue(typeof options, "object", "options argument: type");
+ assert.sameValue(Object.getPrototypeOf(options), null, "options argument: prototype");
+ return super.dateAdd(plainDate, duration, options);
+ }
+}
+
+const plainYearMonth = new Temporal.PlainYearMonth(2000, 3, new CustomCalendar());
+const result = plainYearMonth.add({ months: 10 });
+TemporalHelpers.assertPlainYearMonth(result, 2001, 1, "M01");
+assert.sameValue(calls, 1, "should have called dateAdd");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-datefromfields-called.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-datefromfields-called.js
new file mode 100644
index 0000000000..a69ce9aa7d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-datefromfields-called.js
@@ -0,0 +1,164 @@
+// |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.plainyearmonth.prototype.add
+description: >
+ Calls calendar's dateFromFields method to obtain a start date for the
+ operation, based on the sign of the duration
+info: |
+ 8. Let _fields_ be ? PrepareTemporalFields(_yearMonth_, _fieldNames_, «»).
+ 9. Let _sign_ be ! DurationSign(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _balanceResult_.[[Days]], 0, 0, 0, 0, 0, 0).
+ 10. If _sign_ < 0, then
+ a. Let _dayFromCalendar_ be ? CalendarDaysInMonth(_calendar_, _yearMonth_).
+ b. Let _day_ be ? ToPositiveInteger(_dayFromCalendar_).
+ 11. Else,
+ a. Let _day_ be 1.
+ 12. Perform ! CreateDataPropertyOrThrow(_fields_, *"day"*, _day_).
+ 13. Let _date_ be ? DateFromFields(_calendar_, _fields_, *undefined*).
+includes: [deepEqual.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ this.dateFromFieldsCalls = [];
+ }
+ year(date) {
+ // years in this calendar start and end on the same day as ISO 8601 years
+ return date.getISOFields().isoYear;
+ }
+ month(date) {
+ // this calendar has 10 months of 36 days each, plus an 11th month of 5 or 6
+ const { isoYear, isoMonth, isoDay } = date.getISOFields();
+ const isoDate = new Temporal.PlainDate(isoYear, isoMonth, isoDay);
+ return Math.floor((isoDate.dayOfYear - 1) / 36) + 1;
+ }
+ monthCode(date) {
+ return "M" + this.month(date).toString().padStart(2, "0");
+ }
+ day(date) {
+ return (date.dayOfYear - 1) % 36 + 1;
+ }
+ daysInMonth(date) {
+ if (this.month(date) < 11) return 36;
+ return this.daysInYear(date) - 360;
+ }
+ _dateFromFieldsImpl({ year, month, monthCode, day }) {
+ if (year === undefined) throw new TypeError("year required");
+ if (month === undefined && monthCode === undefined) throw new TypeError("one of month or monthCode required");
+ if (month !== undefined && month < 1) throw new RangeError("month < 1");
+ if (day === undefined) throw new TypeError("day required");
+
+ if (monthCode !== undefined) {
+ const numberPart = +(monthCode.slice(1));
+ if ("M" + `${numberPart}`.padStart(2, "0") !== monthCode) throw new RangeError("invalid monthCode");
+ if (month === undefined) {
+ month = numberPart;
+ } else if (month !== numberPart) {
+ throw new RangeError("month and monthCode must match");
+ }
+ }
+
+ const isoDayOfYear = (month - 1) * 36 + day;
+ return new Temporal.PlainDate(year, 1, 1).add({ days: isoDayOfYear - 1 }).withCalendar(this);
+ }
+ dateFromFields(...args) {
+ this.dateFromFieldsCalls.push(args);
+ return this._dateFromFieldsImpl(...args);
+ }
+ yearMonthFromFields(fields, options) {
+ const { isoYear, isoMonth, isoDay } = this._dateFromFieldsImpl({ ...fields, day: 1 }, options).getISOFields();
+ return new Temporal.PlainYearMonth(isoYear, isoMonth, this, isoDay);
+ }
+ monthDayFromFields(fields, options) {
+ const { isoYear, isoMonth, isoDay } = this._dateFromFieldsImpl({ ...fields, year: 2000 }, options).getISOFields();
+ return new Temporal.PlainMonthDay(isoMonth, isoDay, this, isoYear);
+ }
+ dateAdd(date, duration, options) {
+ const {isoYear, isoMonth, isoDay} = date.getISOFields();
+ let {years, months, weeks, days} = duration;
+ let iter = new Temporal.PlainDate(isoYear + years, isoMonth, isoDay, "iso8601");
+ const monthsDays = months * 36;
+ if (iter.dayOfYear + monthsDays > iter.daysInYear || iter.dayOfYear + monthsDays < 1)
+ throw new Error("complicated addition not implemented in this test");
+ return iter.add({ weeks, days: monthsDays + days }).withCalendar(this);
+ }
+ toString() {
+ return "thirty-six";
+ }
+}
+
+const calendar = new CustomCalendar();
+const month2 = Temporal.PlainYearMonth.from({ year: 2022, month: 2, calendar });
+const lessThanOneMonth = new Temporal.Duration(0, 0, 0, 35);
+const oneMonth = new Temporal.Duration(0, 0, 0, 36);
+
+// Reference ISO dates in the custom calendar:
+// M01 = 01-01
+// M02 = 02-06
+// M03 = 03-14
+
+calendar.dateFromFieldsCalls = [];
+TemporalHelpers.assertPlainYearMonth(
+ month2.add(lessThanOneMonth),
+ 2022, 2, "M02",
+ "adding positive less than one month's worth of days yields the same month",
+ /* era = */ undefined, /* eraYear = */ undefined, /* referenceISODay = */ 6
+);
+assert.sameValue(calendar.dateFromFieldsCalls.length, 1, "dateFromFields was called");
+assert.deepEqual(
+ calendar.dateFromFieldsCalls[0][0],
+ { year: 2022, monthCode: "M02", day: 1 },
+ "first day of month 2 passed to dateFromFields when adding positive duration"
+);
+assert.sameValue(calendar.dateFromFieldsCalls[0][1], undefined, "undefined options passed");
+
+calendar.dateFromFieldsCalls = [];
+TemporalHelpers.assertPlainYearMonth(
+ month2.add(oneMonth),
+ 2022, 3, "M03",
+ "adding positive one month's worth of days yields the following month",
+ /* era = */ undefined, /* eraYear = */ undefined, /* referenceISODay = */ 14
+);
+assert.sameValue(calendar.dateFromFieldsCalls.length, 1, "dateFromFields was called");
+assert.deepEqual(
+ calendar.dateFromFieldsCalls[0][0],
+ { year: 2022, monthCode: "M02", day: 1 },
+ "first day of month 2 passed to dateFromFields when adding positive duration"
+);
+assert.sameValue(calendar.dateFromFieldsCalls[0][1], undefined, "undefined options passed");
+
+calendar.dateFromFieldsCalls = [];
+TemporalHelpers.assertPlainYearMonth(
+ month2.add(lessThanOneMonth.negated()),
+ 2022, 2, "M02",
+ "adding negative less than one month's worth of days yields the same month",
+ /* era = */ undefined, /* eraYear = */ undefined, /* referenceISODay = */ 6
+);
+assert.sameValue(calendar.dateFromFieldsCalls.length, 2, "dateFromFields was called twice");
+assert.deepEqual(
+ calendar.dateFromFieldsCalls[1][0],
+ { year: 2022, monthCode: "M02", day: 36 },
+ "last day of month 2 passed to dateFromFields when adding negative duration"
+);
+assert.sameValue(calendar.dateFromFieldsCalls[0][1], undefined, "undefined options passed");
+
+calendar.dateFromFieldsCalls = [];
+TemporalHelpers.assertPlainYearMonth(
+ month2.add(oneMonth.negated()),
+ 2022, 1, "M01",
+ "adding negative one month's worth of days yields the previous month",
+ /* era = */ undefined, /* eraYear = */ undefined, /* referenceISODay = */ 1
+);
+assert.sameValue(calendar.dateFromFieldsCalls.length, 2, "dateFromFields was called twice");
+assert.deepEqual(
+ calendar.dateFromFieldsCalls[1][0],
+ { year: 2022, monthCode: "M02", day: 36 },
+ "last day of month 2 passed to dateFromFields when adding negative duration"
+);
+assert.sameValue(calendar.dateFromFieldsCalls[0][1], undefined, "undefined options passed");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-fields-iterable.js
new file mode 100644
index 0000000000..57d5f3549e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-fields-iterable.js
@@ -0,0 +1,30 @@
+// |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.plainyearmonth.prototype.add
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plainyearmonth.prototype.add step 8:
+ 8. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "monthCode",
+ "year",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+const yearmonth = new Temporal.PlainYearMonth(2000, 5, calendar);
+yearmonth.add({ months: 1 });
+
+assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-fromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-fromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..0fdeb5d83c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-fromfields-called-with-null-prototype-fields.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.plainyearmonth.prototype.add
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.PlainYearMonth(2000, 5, calendar);
+instance.add(new Temporal.Duration(1));
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should have been called on the calendar");
+assert.sameValue(calendar.yearMonthFromFieldsCallCount, 1, "yearMonthFromFields should have been called on the calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-yearmonthfromfields-called-with-null-prototype-options.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-yearmonthfromfields-called-with-null-prototype-options.js
new file mode 100644
index 0000000000..9daccc5f77
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-yearmonthfromfields-called-with-null-prototype-options.js
@@ -0,0 +1,20 @@
+// |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.plainyearmonth.prototype.add
+description: >
+ Calendar.yearMonthFromFields method is called with a null-prototype object
+ as the options value when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckOptionsPrototypePollution();
+const instance = new Temporal.PlainYearMonth(2019, 6, calendar);
+const argument = new Temporal.Duration(1, 1);
+instance.add(argument);
+assert.sameValue(calendar.yearMonthFromFieldsCallCount, 1, "yearMonthFromFields should be called on the calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..f92a871207
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/constructor-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |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.plainyearmonth.prototype.add
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const ym = new Temporal.PlainYearMonth(2023, 5, calendar);
+
+assert.throws(RangeError, () => ym.add({days: 123}));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/custom-daysInMonth-irrelevant.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/custom-daysInMonth-irrelevant.js
new file mode 100644
index 0000000000..e5af2a5d7a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/custom-daysInMonth-irrelevant.js
@@ -0,0 +1,26 @@
+// |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.plainyearmonth.prototype.add
+description: Addition of a negative duration to a PlainYearMonth is not influenced by the implementation of daysInMonth()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ daysInMonth(ym, ...args) {
+ return 15;
+ }
+}
+
+const customCalendar = new CustomCalendar();
+const instance = new Temporal.PlainYearMonth(2023, 3, customCalendar);
+
+TemporalHelpers.assertPlainYearMonth(instance.add({days: -30}), 2023, 3, 'M03', "Adding -30 days from calendar reimplementing daysinMonth()")
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/duplicate-calendar-fields.js
new file mode 100644
index 0000000000..8c26670fdc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/duplicate-calendar-fields.js
@@ -0,0 +1,18 @@
+// |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.plainyearmonth.prototype.add
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['monthCode'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const ym = new Temporal.PlainYearMonth(2023, 5, calendar);
+
+ assert.throws(RangeError, () => ym.add({days: 123}));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/end-of-month-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/end-of-month-out-of-range.js
new file mode 100644
index 0000000000..58fd4b9fe3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/end-of-month-out-of-range.js
@@ -0,0 +1,33 @@
+// |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.plainyearmonth.prototype.add
+description: RangeError thrown when adding negative duration and end of month is out of range
+features: [Temporal]
+info: |
+ AddDurationToOrSubtractDurationFromPlainYearMonth:
+ 12. If _sign_ &lt; 0, then
+ a. Let _oneMonthDuration_ be ! CreateTemporalDuration(0, 1, 0, 0, 0, 0, 0, 0, 0, 0).
+ b. Let _nextMonth_ be ? CalendarDateAdd(_calendar_, _intermediateDate_, _oneMonthDuration_, *undefined*, _dateAdd_).
+ c. Let _endOfMonthISO_ be ! AddISODate(_nextMonth_.[[ISOYear]], _nextMonth_.[[ISOMonth]], _nextMonth_.[[ISODay]], 0, 0, 0, -1, *"constrain"*).
+ d. Let _endOfMonth_ be ? CreateTemporalDate(_endOfMonthISO_.[[Year]], _endOfMonthISO_.[[Month]], _endOfMonthISO_.[[Day]], _calendar_).
+---*/
+
+// Based on a test case by André Bargull <andre.bargull@gmail.com>
+
+const duration = new Temporal.Duration(0, 0, 0, -1);
+
+// Calendar addition result is out of range
+assert.throws(RangeError, () => new Temporal.PlainYearMonth(275760, 9).add(duration), "Addition of 1 month to receiver out of range");
+
+// Calendar addition succeeds, but subtracting 1 day gives out of range result
+const cal = new class extends Temporal.Calendar {
+ dateAdd() {
+ return new Temporal.PlainDate(-271821, 4, 19);
+ }
+}("iso8601");
+assert.throws(RangeError, () => new Temporal.PlainYearMonth(2000, 1, cal).add(duration), "Subtraction of 1 day from next month out of range");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..263c8f9532
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/infinity-throws-rangeerror.js
@@ -0,0 +1,38 @@
+// |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.
+
+/*---
+description: Temporal.PlainYearMonth.prototype.add throws a RangeError if any value in a property bag is Infinity
+esid: sec-temporal.plainyearmonth.prototype.add
+features: [Temporal]
+---*/
+
+const overflows = ["constrain", "reject"];
+const fields = ["years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"];
+
+const instance = Temporal.PlainYearMonth.from({ year: 2000, month: 5 });
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.add({ [field]: Infinity }, { overflow }));
+ });
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return Infinity;
+ }
+};
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => instance.add({ [field]: obj }, { overflow }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/length.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/length.js
new file mode 100644
index 0000000000..7fd1c9d437
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/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.plainyearmonth.prototype.add
+description: Temporal.PlainYearMonth.prototype.add.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.PlainYearMonth.prototype.add, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/limits.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/limits.js
new file mode 100644
index 0000000000..190bb5bbda
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/limits.js
@@ -0,0 +1,16 @@
+// |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.plainyearmonth.prototype.add
+description: RangeError thrown when going out of range
+features: [Temporal]
+---*/
+
+const max = Temporal.PlainYearMonth.from("+275760-09");
+for (const overflow of ["reject", "constrain"]) {
+ assert.throws(RangeError, () => max.add({ months: 1 }, { overflow }), overflow);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/month-length.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/month-length.js
new file mode 100644
index 0000000000..9ab1999987
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/month-length.js
@@ -0,0 +1,31 @@
+// |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.plainyearmonth.prototype.add
+description: add() takes month length into account
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const ym = Temporal.PlainYearMonth.from("2019-11");
+
+TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from("2019-02").add({ days: 27 }),
+ 2019, 2, "M02");
+TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from("2019-02").add({ days: 28 }),
+ 2019, 3, "M03");
+TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from("2020-02").add({ days: 28 }),
+ 2020, 2, "M02");
+TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from("2020-02").add({ days: 29 }),
+ 2020, 3, "M03");
+TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from("2019-11").add({ days: 29 }),
+ 2019, 11, "M11");
+TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from("2019-11").add({ days: 30 }),
+ 2019, 12, "M12");
+TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from("2020-01").add({ days: 30 }),
+ 2020, 1, "M01");
+TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from("2020-01").add({ days: 31 }),
+ 2020, 2, "M02");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/name.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/name.js
new file mode 100644
index 0000000000..eb28c6ec22
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/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.plainyearmonth.prototype.add
+description: Temporal.PlainYearMonth.prototype.add.name is "add".
+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.PlainYearMonth.prototype.add, "name", {
+ value: "add",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/negative-infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/negative-infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..6af1f2bf62
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/negative-infinity-throws-rangeerror.js
@@ -0,0 +1,38 @@
+// |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.
+
+/*---
+description: Temporal.PlainYearMonth.prototype.add throws a RangeError if any value in a property bag is -Infinity
+esid: sec-temporal.plainyearmonth.prototype.add
+features: [Temporal]
+---*/
+
+const overflows = ["constrain", "reject"];
+const fields = ["years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"];
+
+const instance = Temporal.PlainYearMonth.from({ year: 2000, month: 5 });
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.add({ [field]: -Infinity }, { overflow }));
+ });
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return -Infinity;
+ }
+};
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => instance.add({ [field]: obj }, { overflow }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/non-integer-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/non-integer-throws-rangeerror.js
new file mode 100644
index 0000000000..c9291564a3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/non-integer-throws-rangeerror.js
@@ -0,0 +1,29 @@
+// |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.plainyearmonth.prototype.add
+description: A non-integer value for any recognized property in the property bag, throws a RangeError
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2000, 5);
+const fields = [
+ "years",
+ "months",
+ "weeks",
+ "days",
+ "hours",
+ "minutes",
+ "seconds",
+ "milliseconds",
+ "microseconds",
+ "nanoseconds",
+];
+fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.add({ [field]: 1.5 }));
+ assert.throws(RangeError, () => instance.add({ [field]: -1.5 }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/not-a-constructor.js
new file mode 100644
index 0000000000..f547f9b87f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/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.plainyearmonth.prototype.add
+description: >
+ Temporal.PlainYearMonth.prototype.add 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.PlainYearMonth.prototype.add();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainYearMonth.prototype.add), false,
+ "isConstructor(Temporal.PlainYearMonth.prototype.add)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/options-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/options-invalid.js
new file mode 100644
index 0000000000..49d0259b77
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/options-invalid.js
@@ -0,0 +1,17 @@
+// |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.plainyearmonth.prototype.add
+description: Invalid options throw
+features: [Temporal]
+---*/
+
+const ym = Temporal.PlainYearMonth.from("2019-11");
+const values = [null, true, "hello", Symbol("foo"), 1, 1n];
+for (const badOptions of values) {
+ assert.throws(TypeError, () => ym.add({ years: 1 }, badOptions));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/options-object.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/options-object.js
new file mode 100644
index 0000000000..b1e9c17962
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/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.plainyearmonth.prototype.add
+description: Empty or a function object may be used as options
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2019, 10);
+
+const result1 = instance.add({ months: 1 }, {});
+TemporalHelpers.assertPlainYearMonth(
+ result1, 2019, 11, "M11",
+ "options may be an empty plain object"
+);
+
+const result2 = instance.add({ months: 1 }, () => {});
+TemporalHelpers.assertPlainYearMonth(
+ result2, 2019, 11, "M11",
+ "options may be a function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/options-undefined.js
new file mode 100644
index 0000000000..1dc3954e38
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/options-undefined.js
@@ -0,0 +1,33 @@
+// |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.plainyearmonth.prototype.add
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+// overflow option has no effect on addition in the ISO calendar, so verify this
+// with a custom calendar
+class CheckedAdd extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ dateAdd(date, duration, options, constructor) {
+ this.called = true;
+ assert.notSameValue(options, undefined, "options not undefined");
+ return super.dateAdd(date, duration, options, constructor);
+ }
+}
+const calendar = new CheckedAdd();
+
+const yearmonth = new Temporal.PlainYearMonth(2000, 1, calendar);
+const duration = { months: 1 };
+
+yearmonth.add(duration, undefined);
+yearmonth.add(duration);
+
+assert(calendar.called);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/options-wrong-type.js
new file mode 100644
index 0000000000..ec5796ece6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/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.plainyearmonth.prototype.add
+description: TypeError thrown when options argument is a primitive
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const badOptions = [
+ null,
+ true,
+ "some string",
+ Symbol(),
+ 1,
+ 2n,
+];
+
+const instance = new Temporal.PlainYearMonth(2019, 10);
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.add({ months: 1 }, value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/order-of-operations.js
new file mode 100644
index 0000000000..f1be5cbd2b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/order-of-operations.js
@@ -0,0 +1,183 @@
+// |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.plainyearmonth.prototype.add
+description: Properties on an object passed to add() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ // ToTemporalDuration
+ "get fields.days",
+ "get fields.days.valueOf",
+ "call fields.days.valueOf",
+ "get fields.hours",
+ "get fields.hours.valueOf",
+ "call fields.hours.valueOf",
+ "get fields.microseconds",
+ "get fields.microseconds.valueOf",
+ "call fields.microseconds.valueOf",
+ "get fields.milliseconds",
+ "get fields.milliseconds.valueOf",
+ "call fields.milliseconds.valueOf",
+ "get fields.minutes",
+ "get fields.minutes.valueOf",
+ "call fields.minutes.valueOf",
+ "get fields.months",
+ "get fields.months.valueOf",
+ "call fields.months.valueOf",
+ "get fields.nanoseconds",
+ "get fields.nanoseconds.valueOf",
+ "call fields.nanoseconds.valueOf",
+ "get fields.seconds",
+ "get fields.seconds.valueOf",
+ "call fields.seconds.valueOf",
+ "get fields.weeks",
+ "get fields.weeks.valueOf",
+ "call fields.weeks.valueOf",
+ "get fields.years",
+ "get fields.years.valueOf",
+ "call fields.years.valueOf",
+ // lookup
+ "get this.calendar.dateAdd",
+ "get this.calendar.dateFromFields",
+ "get this.calendar.day",
+ "get this.calendar.fields",
+ "get this.calendar.yearMonthFromFields",
+ // CalendarFields
+ "call this.calendar.fields",
+ // PrepareTemporalFields on receiver
+ "get this.calendar.monthCode",
+ "call this.calendar.monthCode",
+ "get this.calendar.year",
+ "call this.calendar.year",
+ // CalendarDateFromFields
+ "call this.calendar.dateFromFields",
+ // CopyDataProperties
+ "ownKeys options",
+ "getOwnPropertyDescriptor options.overflow",
+ "get options.overflow",
+ // CalendarDateAdd
+ "call this.calendar.dateAdd",
+ // inside Calendar.p.dateAdd
+ "get options.overflow",
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+ // PrepareTemporalFields on added date
+ "get this.calendar.monthCode",
+ "call this.calendar.monthCode",
+ "get this.calendar.year",
+ "call this.calendar.year",
+ // CalendarYearMonthFromFields
+ "call this.calendar.yearMonthFromFields",
+ // inside Calendar.p.yearMonthFromFields
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+];
+const actual = [];
+
+const calendar = TemporalHelpers.calendarObserver(actual, "this.calendar");
+const instance = new Temporal.PlainYearMonth(2000, 5, calendar);
+// clear observable operations that occurred during the constructor call
+actual.splice(0);
+
+const fields = TemporalHelpers.propertyBagObserver(actual, {
+ years: 1,
+ months: 1,
+ weeks: 1,
+ days: 1,
+ hours: 1,
+ minutes: 1,
+ seconds: 1,
+ milliseconds: 1,
+ microseconds: 1,
+ nanoseconds: 1,
+}, "fields");
+
+const options = TemporalHelpers.propertyBagObserver(actual, { overflow: "constrain" }, "options");
+
+instance.add(fields, options);
+assert.compareArray(actual, expected, "order of operations");
+
+actual.splice(0); // clear
+
+const noCalendarExpected = [
+ // ToTemporalDuration
+ "get fields.days",
+ "get fields.days.valueOf",
+ "call fields.days.valueOf",
+ "get fields.hours",
+ "get fields.hours.valueOf",
+ "call fields.hours.valueOf",
+ "get fields.microseconds",
+ "get fields.microseconds.valueOf",
+ "call fields.microseconds.valueOf",
+ "get fields.milliseconds",
+ "get fields.milliseconds.valueOf",
+ "call fields.milliseconds.valueOf",
+ "get fields.minutes",
+ "get fields.minutes.valueOf",
+ "call fields.minutes.valueOf",
+ "get fields.months",
+ "get fields.nanoseconds",
+ "get fields.nanoseconds.valueOf",
+ "call fields.nanoseconds.valueOf",
+ "get fields.seconds",
+ "get fields.seconds.valueOf",
+ "call fields.seconds.valueOf",
+ "get fields.weeks",
+ "get fields.years",
+ // lookup
+ "get this.calendar.dateAdd",
+ "get this.calendar.dateFromFields",
+ "get this.calendar.day",
+ "get this.calendar.fields",
+ "get this.calendar.yearMonthFromFields",
+ // CalendarFields
+ "call this.calendar.fields",
+ // PrepareTemporalFields on receiver
+ "get this.calendar.monthCode",
+ "call this.calendar.monthCode",
+ "get this.calendar.year",
+ "call this.calendar.year",
+ // CalendarDateFromFields
+ "call this.calendar.dateFromFields",
+ // SnapshotOwnProperties
+ "ownKeys options",
+ "getOwnPropertyDescriptor options.overflow",
+ "get options.overflow",
+ // AddDate
+ "get options.overflow",
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+ // PrepareTemporalFields on added date
+ "get this.calendar.monthCode",
+ "call this.calendar.monthCode",
+ "get this.calendar.year",
+ "call this.calendar.year",
+ // CalendarYearMonthFromFields
+ "call this.calendar.yearMonthFromFields",
+ // inside Calendar.p.yearMonthFromFields
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+];
+
+const noCalendarFields = TemporalHelpers.propertyBagObserver(actual, {
+ days: 1,
+ hours: 1,
+ minutes: 1,
+ seconds: 1,
+ milliseconds: 1,
+ microseconds: 1,
+ nanoseconds: 1,
+}, "fields");
+
+instance.add(noCalendarFields, options);
+assert.compareArray(actual, noCalendarExpected, "order of operations with no calendar units");
+
+actual.splice(0); // clear
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/overflow-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/overflow-invalid-string.js
new file mode 100644
index 0000000000..5ee0755a73
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/overflow-invalid-string.js
@@ -0,0 +1,34 @@
+// |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.plainyearmonth.prototype.add
+description: RangeError thrown when overflow option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-isoyearmonthfromfields step 2:
+ 2. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plainyearmonth.prototype.add steps 13–15:
+ 13. Let _addedDate_ be ? CalendarDateAdd(_calendar_, _date_, _durationToAdd_, _options_).
+ 14. ...
+ 15. Return ? YearMonthFromFields(_calendar_, _addedDateFields_, _options_).
+features: [Temporal]
+---*/
+
+const yearmonth = new Temporal.PlainYearMonth(2000, 5);
+const duration = new Temporal.Duration(1, 1);
+
+const badOverflows = ["", "CONSTRAIN", "balance", "other string", "constra\u0131n", "reject\0"];
+for (const overflow of badOverflows) {
+ assert.throws(
+ RangeError,
+ () => yearmonth.add(duration, { overflow }),
+ `invalid overflow ("${overflow}")`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/overflow-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/overflow-undefined.js
new file mode 100644
index 0000000000..be130752f5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/overflow-undefined.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.plainyearmonth.prototype.add
+description: Fallback value for overflow option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-isoyearmonthfromfields step 2:
+ 2. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plainyearmonth.prototype.add steps 13–15:
+ 13. Let _addedDate_ be ? CalendarDateAdd(_calendar_, _date_, _durationToAdd_, _options_).
+ 14. ...
+ 15. Return ? YearMonthFromFields(_calendar_, _addedDateFields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+// In the ISO calendar, PlainYearMonth.prototype.add() actually ignores the
+// overflow option. There is no addition in the ISO calendar that we could test
+// which would actually show a difference between the 'constrain' and 'reject'
+// values.
+const yearmonth = new Temporal.PlainYearMonth(2000, 5);
+const duration = new Temporal.Duration(1, 1);
+const explicit = yearmonth.add(duration, { overflow: undefined });
+TemporalHelpers.assertPlainYearMonth(explicit, 2001, 6, "M06", "default overflow is constrain");
+const implicit = yearmonth.add(duration, {});
+TemporalHelpers.assertPlainYearMonth(implicit, 2001, 6, "M06", "default overflow is constrain");
+const lambda = yearmonth.add(duration, () => {});
+TemporalHelpers.assertPlainYearMonth(lambda, 2001, 6, "M06", "default overflow is constrain");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/overflow-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/overflow-wrong-type.js
new file mode 100644
index 0000000000..8a228a78b5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/overflow-wrong-type.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.plainyearmonth.prototype.add
+description: Type conversions for overflow option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-isoyearmonthfromfields step 2:
+ 2. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plainyearmonth.prototype.add steps 13–15:
+ 13. Let _addedDate_ be ? CalendarDateAdd(_calendar_, _date_, _durationToAdd_, _options_).
+ 14. ...
+ 15. Return ? YearMonthFromFields(_calendar_, _addedDateFields_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const yearmonth = new Temporal.PlainYearMonth(2000, 5);
+const duration = new Temporal.Duration(1, 1);
+
+// See TemporalHelpers.checkStringOptionWrongType(); this code path has
+// different expectations for observable calls
+
+assert.throws(RangeError, () => yearmonth.add(duration, { overflow: null }), "null");
+assert.throws(RangeError, () => yearmonth.add(duration, { overflow: true }), "true");
+assert.throws(RangeError, () => yearmonth.add(duration, { overflow: false }), "false");
+assert.throws(TypeError, () => yearmonth.add(duration, { overflow: Symbol() }), "symbol");
+assert.throws(RangeError, () => yearmonth.add(duration, { overflow: 2 }), "number");
+assert.throws(RangeError, () => yearmonth.add(duration, { overflow: 2n }), "bigint");
+assert.throws(RangeError, () => yearmonth.add(duration, { overflow: {} }), "plain object");
+
+// toString property is read once by Calendar.dateAdd() and then once again by
+// calendar.yearMonthFromFields().
+const expected = [
+ "get overflow.toString",
+ "call overflow.toString",
+ "get overflow.toString",
+ "call overflow.toString",
+];
+const actual = [];
+const observer = TemporalHelpers.toPrimitiveObserver(actual, "constrain", "overflow");
+const result = yearmonth.add(duration, { overflow: observer });
+TemporalHelpers.assertPlainYearMonth(result, 2001, 6, "M06", "object with toString");
+assert.compareArray(actual, expected, "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/prop-desc.js
new file mode 100644
index 0000000000..ef7c146f3c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/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.plainyearmonth.prototype.add
+description: The "add" property of Temporal.PlainYearMonth.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainYearMonth.prototype.add,
+ "function",
+ "`typeof PlainYearMonth.prototype.add` is `function`"
+);
+
+verifyProperty(Temporal.PlainYearMonth.prototype, "add", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/proto-in-calendar-fields.js
new file mode 100644
index 0000000000..de323c1214
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/proto-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |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.plainyearmonth.prototype.add
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const ym = new Temporal.PlainYearMonth(2023, 5, calendar);
+
+assert.throws(RangeError, () => ym.add({days: 123}));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/shell.js
new file mode 100644
index 0000000000..346758ebd5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/shell.js
@@ -0,0 +1,353 @@
+// GENERATED, DO NOT EDIT
+// file: deepEqual.js
+// Copyright 2019 Ron Buckton. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+description: >
+ Compare two values structurally
+defines: [assert.deepEqual]
+---*/
+
+assert.deepEqual = function(actual, expected, message) {
+ var format = assert.deepEqual.format;
+ assert(
+ assert.deepEqual._compare(actual, expected),
+ `Expected ${format(actual)} to be structurally equal to ${format(expected)}. ${(message || '')}`
+ );
+};
+
+assert.deepEqual.format = function(value, seen) {
+ switch (typeof value) {
+ case 'string':
+ return typeof JSON !== "undefined" ? JSON.stringify(value) : `"${value}"`;
+ case 'number':
+ case 'boolean':
+ case 'symbol':
+ case 'bigint':
+ return value.toString();
+ case 'undefined':
+ return 'undefined';
+ case 'function':
+ return `[Function${value.name ? `: ${value.name}` : ''}]`;
+ case 'object':
+ if (value === null) return 'null';
+ if (value instanceof Date) return `Date "${value.toISOString()}"`;
+ if (value instanceof RegExp) return value.toString();
+ if (!seen) {
+ seen = {
+ counter: 0,
+ map: new Map()
+ };
+ }
+
+ let usage = seen.map.get(value);
+ if (usage) {
+ usage.used = true;
+ return `[Ref: #${usage.id}]`;
+ }
+
+ usage = { id: ++seen.counter, used: false };
+ seen.map.set(value, usage);
+
+ if (typeof Set !== "undefined" && value instanceof Set) {
+ return `Set {${Array.from(value).map(value => assert.deepEqual.format(value, seen)).join(', ')}}${usage.used ? ` as #${usage.id}` : ''}`;
+ }
+ if (typeof Map !== "undefined" && value instanceof Map) {
+ return `Map {${Array.from(value).map(pair => `${assert.deepEqual.format(pair[0], seen)} => ${assert.deepEqual.format(pair[1], seen)}}`).join(', ')}}${usage.used ? ` as #${usage.id}` : ''}`;
+ }
+ if (Array.isArray ? Array.isArray(value) : value instanceof Array) {
+ return `[${value.map(value => assert.deepEqual.format(value, seen)).join(', ')}]${usage.used ? ` as #${usage.id}` : ''}`;
+ }
+ let tag = Symbol.toStringTag in value ? value[Symbol.toStringTag] : 'Object';
+ if (tag === 'Object' && Object.getPrototypeOf(value) === null) {
+ tag = '[Object: null prototype]';
+ }
+ return `${tag ? `${tag} ` : ''}{ ${Object.keys(value).map(key => `${key.toString()}: ${assert.deepEqual.format(value[key], seen)}`).join(', ')} }${usage.used ? ` as #${usage.id}` : ''}`;
+ default:
+ return typeof value;
+ }
+};
+
+assert.deepEqual._compare = (function () {
+ var EQUAL = 1;
+ var NOT_EQUAL = -1;
+ var UNKNOWN = 0;
+
+ function deepEqual(a, b) {
+ return compareEquality(a, b) === EQUAL;
+ }
+
+ function compareEquality(a, b, cache) {
+ return compareIf(a, b, isOptional, compareOptionality)
+ || compareIf(a, b, isPrimitiveEquatable, comparePrimitiveEquality)
+ || compareIf(a, b, isObjectEquatable, compareObjectEquality, cache)
+ || NOT_EQUAL;
+ }
+
+ function compareIf(a, b, test, compare, cache) {
+ return !test(a)
+ ? !test(b) ? UNKNOWN : NOT_EQUAL
+ : !test(b) ? NOT_EQUAL : cacheComparison(a, b, compare, cache);
+ }
+
+ function tryCompareStrictEquality(a, b) {
+ return a === b ? EQUAL : UNKNOWN;
+ }
+
+ function tryCompareTypeOfEquality(a, b) {
+ return typeof a !== typeof b ? NOT_EQUAL : UNKNOWN;
+ }
+
+ function tryCompareToStringTagEquality(a, b) {
+ var aTag = Symbol.toStringTag in a ? a[Symbol.toStringTag] : undefined;
+ var bTag = Symbol.toStringTag in b ? b[Symbol.toStringTag] : undefined;
+ return aTag !== bTag ? NOT_EQUAL : UNKNOWN;
+ }
+
+ function isOptional(value) {
+ return value === undefined
+ || value === null;
+ }
+
+ function compareOptionality(a, b) {
+ return tryCompareStrictEquality(a, b)
+ || NOT_EQUAL;
+ }
+
+ function isPrimitiveEquatable(value) {
+ switch (typeof value) {
+ case 'string':
+ case 'number':
+ case 'bigint':
+ case 'boolean':
+ case 'symbol':
+ return true;
+ default:
+ return isBoxed(value);
+ }
+ }
+
+ function comparePrimitiveEquality(a, b) {
+ if (isBoxed(a)) a = a.valueOf();
+ if (isBoxed(b)) b = b.valueOf();
+ return tryCompareStrictEquality(a, b)
+ || tryCompareTypeOfEquality(a, b)
+ || compareIf(a, b, isNaNEquatable, compareNaNEquality)
+ || NOT_EQUAL;
+ }
+
+ function isNaNEquatable(value) {
+ return typeof value === 'number';
+ }
+
+ function compareNaNEquality(a, b) {
+ return isNaN(a) && isNaN(b) ? EQUAL : NOT_EQUAL;
+ }
+
+ function isObjectEquatable(value) {
+ return typeof value === 'object';
+ }
+
+ function compareObjectEquality(a, b, cache) {
+ if (!cache) cache = new Map();
+ return getCache(cache, a, b)
+ || setCache(cache, a, b, EQUAL) // consider equal for now
+ || cacheComparison(a, b, tryCompareStrictEquality, cache)
+ || cacheComparison(a, b, tryCompareToStringTagEquality, cache)
+ || compareIf(a, b, isValueOfEquatable, compareValueOfEquality)
+ || compareIf(a, b, isToStringEquatable, compareToStringEquality)
+ || compareIf(a, b, isArrayLikeEquatable, compareArrayLikeEquality, cache)
+ || compareIf(a, b, isStructurallyEquatable, compareStructuralEquality, cache)
+ || compareIf(a, b, isIterableEquatable, compareIterableEquality, cache)
+ || cacheComparison(a, b, fail, cache);
+ }
+
+ function isBoxed(value) {
+ return value instanceof String
+ || value instanceof Number
+ || value instanceof Boolean
+ || typeof Symbol === 'function' && value instanceof Symbol
+ || typeof BigInt === 'function' && value instanceof BigInt;
+ }
+
+ function isValueOfEquatable(value) {
+ return value instanceof Date;
+ }
+
+ function compareValueOfEquality(a, b) {
+ return compareIf(a.valueOf(), b.valueOf(), isPrimitiveEquatable, comparePrimitiveEquality)
+ || NOT_EQUAL;
+ }
+
+ function isToStringEquatable(value) {
+ return value instanceof RegExp;
+ }
+
+ function compareToStringEquality(a, b) {
+ return compareIf(a.toString(), b.toString(), isPrimitiveEquatable, comparePrimitiveEquality)
+ || NOT_EQUAL;
+ }
+
+ function isArrayLikeEquatable(value) {
+ return (Array.isArray ? Array.isArray(value) : value instanceof Array)
+ || (typeof Uint8Array === 'function' && value instanceof Uint8Array)
+ || (typeof Uint8ClampedArray === 'function' && value instanceof Uint8ClampedArray)
+ || (typeof Uint16Array === 'function' && value instanceof Uint16Array)
+ || (typeof Uint32Array === 'function' && value instanceof Uint32Array)
+ || (typeof Int8Array === 'function' && value instanceof Int8Array)
+ || (typeof Int16Array === 'function' && value instanceof Int16Array)
+ || (typeof Int32Array === 'function' && value instanceof Int32Array)
+ || (typeof Float32Array === 'function' && value instanceof Float32Array)
+ || (typeof Float64Array === 'function' && value instanceof Float64Array)
+ || (typeof BigUint64Array === 'function' && value instanceof BigUint64Array)
+ || (typeof BigInt64Array === 'function' && value instanceof BigInt64Array);
+ }
+
+ function compareArrayLikeEquality(a, b, cache) {
+ if (a.length !== b.length) return NOT_EQUAL;
+ for (var i = 0; i < a.length; i++) {
+ if (compareEquality(a[i], b[i], cache) === NOT_EQUAL) {
+ return NOT_EQUAL;
+ }
+ }
+ return EQUAL;
+ }
+
+ function isStructurallyEquatable(value) {
+ return !(typeof Promise === 'function' && value instanceof Promise // only comparable by reference
+ || typeof WeakMap === 'function' && value instanceof WeakMap // only comparable by reference
+ || typeof WeakSet === 'function' && value instanceof WeakSet // only comparable by reference
+ || typeof Map === 'function' && value instanceof Map // comparable via @@iterator
+ || typeof Set === 'function' && value instanceof Set); // comparable via @@iterator
+ }
+
+ function compareStructuralEquality(a, b, cache) {
+ var aKeys = [];
+ for (var key in a) aKeys.push(key);
+
+ var bKeys = [];
+ for (var key in b) bKeys.push(key);
+
+ if (aKeys.length !== bKeys.length) {
+ return NOT_EQUAL;
+ }
+
+ aKeys.sort();
+ bKeys.sort();
+
+ for (var i = 0; i < aKeys.length; i++) {
+ var aKey = aKeys[i];
+ var bKey = bKeys[i];
+ if (compareEquality(aKey, bKey, cache) === NOT_EQUAL) {
+ return NOT_EQUAL;
+ }
+ if (compareEquality(a[aKey], b[bKey], cache) === NOT_EQUAL) {
+ return NOT_EQUAL;
+ }
+ }
+
+ return compareIf(a, b, isIterableEquatable, compareIterableEquality, cache)
+ || EQUAL;
+ }
+
+ function isIterableEquatable(value) {
+ return typeof Symbol === 'function'
+ && typeof value[Symbol.iterator] === 'function';
+ }
+
+ function compareIteratorEquality(a, b, cache) {
+ if (typeof Map === 'function' && a instanceof Map && b instanceof Map ||
+ typeof Set === 'function' && a instanceof Set && b instanceof Set) {
+ if (a.size !== b.size) return NOT_EQUAL; // exit early if we detect a difference in size
+ }
+
+ var ar, br;
+ while (true) {
+ ar = a.next();
+ br = b.next();
+ if (ar.done) {
+ if (br.done) return EQUAL;
+ if (b.return) b.return();
+ return NOT_EQUAL;
+ }
+ if (br.done) {
+ if (a.return) a.return();
+ return NOT_EQUAL;
+ }
+ if (compareEquality(ar.value, br.value, cache) === NOT_EQUAL) {
+ if (a.return) a.return();
+ if (b.return) b.return();
+ return NOT_EQUAL;
+ }
+ }
+ }
+
+ function compareIterableEquality(a, b, cache) {
+ return compareIteratorEquality(a[Symbol.iterator](), b[Symbol.iterator](), cache);
+ }
+
+ function cacheComparison(a, b, compare, cache) {
+ var result = compare(a, b, cache);
+ if (cache && (result === EQUAL || result === NOT_EQUAL)) {
+ setCache(cache, a, b, /** @type {EQUAL | NOT_EQUAL} */(result));
+ }
+ return result;
+ }
+
+ function fail() {
+ return NOT_EQUAL;
+ }
+
+ function setCache(cache, left, right, result) {
+ var otherCache;
+
+ otherCache = cache.get(left);
+ if (!otherCache) cache.set(left, otherCache = new Map());
+ otherCache.set(right, result);
+
+ otherCache = cache.get(right);
+ if (!otherCache) cache.set(right, otherCache = new Map());
+ otherCache.set(left, result);
+ }
+
+ function getCache(cache, left, right) {
+ var otherCache;
+ var result;
+
+ otherCache = cache.get(left);
+ result = otherCache && otherCache.get(right);
+ if (result) return result;
+
+ otherCache = cache.get(right);
+ result = otherCache && otherCache.get(left);
+ if (result) return result;
+
+ return UNKNOWN;
+ }
+
+ return deepEqual;
+})();
+
+// 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/PlainYearMonth/prototype/add/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/subclassing-ignored.js
new file mode 100644
index 0000000000..2afd7da12a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/subclassing-ignored.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.plainyearmonth.prototype.add
+description: Objects of a subclass are never created as return values for add()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.PlainYearMonth,
+ [2000, 5],
+ "add",
+ [{ months: 1 }],
+ (result) => TemporalHelpers.assertPlainYearMonth(result, 2000, 6, "M06"),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/calendarId/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/calendarId/branding.js
new file mode 100644
index 0000000000..0d9b9175de
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/calendarId/branding.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-get-temporal.plainyearmonth.prototype.calendarid
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const calendarId = Object.getOwnPropertyDescriptor(Temporal.PlainYearMonth.prototype, "calendarId").get;
+
+assert.sameValue(typeof calendarId, "function");
+
+assert.throws(TypeError, () => calendarId.call(undefined), "undefined");
+assert.throws(TypeError, () => calendarId.call(null), "null");
+assert.throws(TypeError, () => calendarId.call(true), "true");
+assert.throws(TypeError, () => calendarId.call(""), "empty string");
+assert.throws(TypeError, () => calendarId.call(Symbol()), "symbol");
+assert.throws(TypeError, () => calendarId.call(1), "1");
+assert.throws(TypeError, () => calendarId.call({}), "plain object");
+assert.throws(TypeError, () => calendarId.call(Temporal.PlainYearMonth), "Temporal.PlainYearMonth");
+assert.throws(TypeError, () => calendarId.call(Temporal.PlainYearMonth.prototype), "Temporal.PlainYearMonth.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/calendarId/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/calendarId/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/calendarId/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/calendarId/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/calendarId/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..40e1330c6d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/calendarId/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plainyearmonth.prototype.calendarid
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const idOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "id");
+Object.defineProperty(Temporal.Calendar.prototype, "id", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("id should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainYearMonth(2000, 5, "iso8601", 1);
+instance.calendarId;
+
+Object.defineProperty(Temporal.Calendar.prototype, "id", idOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/calendarId/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/calendarId/prop-desc.js
new file mode 100644
index 0000000000..ec83805f4c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/calendarId/prop-desc.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-get-temporal.plainyearmonth.prototype.calendarid
+description: The "calendarId" property of Temporal.PlainYearMonth.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainYearMonth.prototype, "calendarId");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/calendarId/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/calendarId/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/calendarId/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/constructor.js
new file mode 100644
index 0000000000..5584c1d064
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/constructor.js
@@ -0,0 +1,20 @@
+// |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.plainyearmonth.prototype.constructor
+description: Test for Temporal.PlainYearMonth.prototype.constructor.
+info: The initial value of Temporal.PlainYearMonth.prototype.constructor is %Temporal.PlainYearMonth%.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainYearMonth.prototype, "constructor", {
+ value: Temporal.PlainYearMonth,
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/basic.js
new file mode 100644
index 0000000000..1a39263196
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/basic.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-get-temporal.plainyearmonth.prototype.daysinmonth
+description: daysInMonth works
+features: [Temporal]
+---*/
+
+const tests = [
+ [new Temporal.PlainYearMonth(1976, 2), 29],
+ [new Temporal.PlainYearMonth(1976, 11), 30],
+ [new Temporal.PlainYearMonth(1976, 12), 31],
+ [new Temporal.PlainYearMonth(1977, 2), 28],
+];
+for (const [plainYearMonth, expected] of tests) {
+ assert.sameValue(plainYearMonth.daysInMonth, expected, `${expected} days in the month of ${plainYearMonth}`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/branding.js
new file mode 100644
index 0000000000..4f62df5ae4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/branding.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-get-temporal.plainyearmonth.prototype.daysinmonth
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const daysInMonth = Object.getOwnPropertyDescriptor(Temporal.PlainYearMonth.prototype, "daysInMonth").get;
+
+assert.sameValue(typeof daysInMonth, "function");
+
+assert.throws(TypeError, () => daysInMonth.call(undefined), "undefined");
+assert.throws(TypeError, () => daysInMonth.call(null), "null");
+assert.throws(TypeError, () => daysInMonth.call(true), "true");
+assert.throws(TypeError, () => daysInMonth.call(""), "empty string");
+assert.throws(TypeError, () => daysInMonth.call(Symbol()), "symbol");
+assert.throws(TypeError, () => daysInMonth.call(1), "1");
+assert.throws(TypeError, () => daysInMonth.call({}), "plain object");
+assert.throws(TypeError, () => daysInMonth.call(Temporal.PlainYearMonth), "Temporal.PlainYearMonth");
+assert.throws(TypeError, () => daysInMonth.call(Temporal.PlainYearMonth.prototype), "Temporal.PlainYearMonth.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..a15e38458f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plainyearmonth.prototype.daysinmonth
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const daysInMonthOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "daysInMonth");
+Object.defineProperty(Temporal.Calendar.prototype, "daysInMonth", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("daysInMonth should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainYearMonth(2000, 5, "iso8601", 1);
+instance.daysInMonth;
+
+Object.defineProperty(Temporal.Calendar.prototype, "daysInMonth", daysInMonthOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/custom.js
new file mode 100644
index 0000000000..6dec04d593
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/custom.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-get-temporal.plainyearmonth.prototype.daysinmonth
+description: Custom calendar tests for daysInMonth().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ daysInMonth(...args) {
+ ++calls;
+ assert.compareArray(args, [instance], "daysInMonth arguments");
+ return 7;
+ }
+}
+
+const calendar = new CustomCalendar();
+const instance = new Temporal.PlainYearMonth(1830, 8, calendar);
+const result = instance.daysInMonth;
+assert.sameValue(result, 7, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/prop-desc.js
new file mode 100644
index 0000000000..65a27466d8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/prop-desc.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-get-temporal.plainyearmonth.prototype.daysinmonth
+description: The "daysInMonth" property of Temporal.PlainYearMonth.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainYearMonth.prototype, "daysInMonth");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/validate-calendar-value.js
new file mode 100644
index 0000000000..bbdfb954a1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/validate-calendar-value.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-get-temporal.plainyearmonth.prototype.daysinmonth
+description: Validate result returned from calendar daysInMonth() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [null, TypeError],
+ [false, TypeError],
+ [Infinity, RangeError],
+ [-Infinity, RangeError],
+ [NaN, RangeError],
+ [-7, RangeError],
+ [-0.1, RangeError],
+ ["string", TypeError],
+ [Symbol("foo"), TypeError],
+ [7n, TypeError],
+ [{}, TypeError],
+ [true, TypeError],
+ [7.1, RangeError],
+ ["7", TypeError],
+ ["7.5", TypeError],
+ [{valueOf() { return 7; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ daysInMonth() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainYearMonth(1981, 12, calendar);
+ assert.throws(error, () => instance.daysInMonth, `${typeof result} ${String(result)} not converted to positive integer`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/basic.js
new file mode 100644
index 0000000000..d5454b3721
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/basic.js
@@ -0,0 +1,14 @@
+// |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-get-temporal.plainyearmonth.prototype.daysinyear
+description: daysInYear works
+features: [Temporal]
+---*/
+
+assert.sameValue((new Temporal.PlainYearMonth(1976, 11)).daysInYear, 366, "leap year");
+assert.sameValue((new Temporal.PlainYearMonth(1977, 11)).daysInYear, 365, "non-leap year");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/branding.js
new file mode 100644
index 0000000000..c8f726278e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/branding.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-get-temporal.plainyearmonth.prototype.daysinyear
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const daysInYear = Object.getOwnPropertyDescriptor(Temporal.PlainYearMonth.prototype, "daysInYear").get;
+
+assert.sameValue(typeof daysInYear, "function");
+
+assert.throws(TypeError, () => daysInYear.call(undefined), "undefined");
+assert.throws(TypeError, () => daysInYear.call(null), "null");
+assert.throws(TypeError, () => daysInYear.call(true), "true");
+assert.throws(TypeError, () => daysInYear.call(""), "empty string");
+assert.throws(TypeError, () => daysInYear.call(Symbol()), "symbol");
+assert.throws(TypeError, () => daysInYear.call(1), "1");
+assert.throws(TypeError, () => daysInYear.call({}), "plain object");
+assert.throws(TypeError, () => daysInYear.call(Temporal.PlainYearMonth), "Temporal.PlainYearMonth");
+assert.throws(TypeError, () => daysInYear.call(Temporal.PlainYearMonth.prototype), "Temporal.PlainYearMonth.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..80be46b506
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plainyearmonth.prototype.daysinyear
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const daysInYearOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "daysInYear");
+Object.defineProperty(Temporal.Calendar.prototype, "daysInYear", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("daysInYear should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainYearMonth(2000, 5, "iso8601", 1);
+instance.daysInYear;
+
+Object.defineProperty(Temporal.Calendar.prototype, "daysInYear", daysInYearOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/custom.js
new file mode 100644
index 0000000000..fbe58f3918
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/custom.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-get-temporal.plainyearmonth.prototype.daysinyear
+description: Custom calendar tests for daysInYear().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ daysInYear(...args) {
+ ++calls;
+ assert.compareArray(args, [instance], "daysInYear arguments");
+ return 7;
+ }
+}
+
+const calendar = new CustomCalendar();
+const instance = new Temporal.PlainYearMonth(1830, 8, calendar);
+const result = instance.daysInYear;
+assert.sameValue(result, 7, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/prop-desc.js
new file mode 100644
index 0000000000..c9d5ae7d5a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/prop-desc.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-get-temporal.plainyearmonth.prototype.daysinyear
+description: The "daysInYear" property of Temporal.PlainYearMonth.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainYearMonth.prototype, "daysInYear");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/validate-calendar-value.js
new file mode 100644
index 0000000000..f600bdec79
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/validate-calendar-value.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-get-temporal.plainyearmonth.prototype.daysinyear
+description: Validate result returned from calendar daysInYear() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [null, TypeError],
+ [false, TypeError],
+ [Infinity, RangeError],
+ [-Infinity, RangeError],
+ [NaN, RangeError],
+ [-7, RangeError],
+ [-0.1, RangeError],
+ ["string", TypeError],
+ [Symbol("foo"), TypeError],
+ [7n, TypeError],
+ [{}, TypeError],
+ [true, TypeError],
+ [7.1, RangeError],
+ ["7", TypeError],
+ ["7.5", TypeError],
+ [{valueOf() { return 7; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ daysInYear() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainYearMonth(1981, 12, calendar);
+ assert.throws(error, () => instance.daysInYear, `${typeof result} ${String(result)} not converted to positive integer`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..88039009aa
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-builtin-calendar-no-array-iteration.js
@@ -0,0 +1,24 @@
+// |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.plainyearmonth.prototype.equals
+description: >
+ Calling the method with a property bag argument with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.PlainYearMonth(2000, 5);
+const arg = { year: 2000, month: 5, calendar: "iso8601" };
+instance.equals(arg);
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-cast.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-cast.js
new file mode 100644
index 0000000000..b14f01000b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-cast.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.plainyearmonth.prototype.equals
+description: equals() casts its argument
+features: [Temporal]
+---*/
+
+const nov94 = Temporal.PlainYearMonth.from("1994-11");
+
+assert.sameValue(nov94.equals({ year: 2013, month: 6 }), false, "object");
+assert.sameValue(nov94.equals({ year: 1994, month: 11 }), true, "object");
+assert.sameValue(nov94.equals("2013-06"), false, "string");
+assert.sameValue(nov94.equals("1994-11"), true, "string");
+assert.throws(TypeError, () => nov94.equals({ year: 2013 }), "missing property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-number.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-number.js
new file mode 100644
index 0000000000..abcf713fd2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/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.plainyearmonth.prototype.equals
+description: A number is invalid in place of an ISO string for Temporal.PlainYearMonth
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2019, 6);
+
+const numbers = [
+ 1,
+ 201906,
+ -201906,
+ 1234567,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => instance.equals(arg),
+ `A number (${arg}) is not a valid ISO string for PlainYearMonth`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-propertybag-calendar-case-insensitive.js
new file mode 100644
index 0000000000..785f769b50
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-propertybag-calendar-case-insensitive.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.plainyearmonth.prototype.equals
+description: The calendar name is case-insensitive
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2019, 6);
+
+const calendar = "IsO8601";
+
+const arg = { year: 2019, monthCode: "M06", calendar };
+const result = instance.equals(arg);
+assert.sameValue(result, true, "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-propertybag-calendar-leap-second.js
new file mode 100644
index 0000000000..1b405e75e4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-propertybag-calendar-leap-second.js
@@ -0,0 +1,23 @@
+// |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.plainyearmonth.prototype.equals
+description: Leap second is a valid ISO string for a calendar in a property bag
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2019, 6);
+
+const calendar = "2016-12-31T23:59:60";
+
+const arg = { year: 2019, monthCode: "M06", calendar };
+const result = instance.equals(arg);
+assert.sameValue(
+ result,
+ true,
+ "leap second is a valid ISO string for calendar"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-propertybag-calendar-number.js
new file mode 100644
index 0000000000..d0c0c6570a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-propertybag-calendar-number.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.plainyearmonth.prototype.equals
+description: A number as calendar in a property bag is not accepted
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2019, 6);
+
+const numbers = [
+ 1,
+ 19970327,
+ -19970327,
+ 1234567890,
+];
+
+for (const calendar of numbers) {
+ const arg = { year: 2019, monthCode: "M06", calendar };
+ assert.throws(
+ TypeError,
+ () => instance.equals(arg),
+ "Numbers cannot be used as a calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-propertybag-calendar-string.js
new file mode 100644
index 0000000000..1f6ce3ef8f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-propertybag-calendar-string.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.plainyearmonth.prototype.equals
+description: A calendar ID is valid input for Calendar
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2019, 6);
+
+const calendar = "iso8601";
+
+const arg = { year: 2019, monthCode: "M06", calendar };
+const result = instance.equals(arg);
+assert.sameValue(result, true, `Calendar created from string "${calendar}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-propertybag-calendar-wrong-type.js
new file mode 100644
index 0000000000..5ee7390dad
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-propertybag-calendar-wrong-type.js
@@ -0,0 +1,46 @@
+// |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.plainyearmonth.prototype.equals
+description: >
+ Appropriate error thrown when a calendar property from a property bag cannot
+ be converted to a calendar object or string
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.PlainYearMonth(2000, 5);
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [calendar, description] of primitiveTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ typeof calendar === 'string' ? RangeError : TypeError,
+ () => instance.equals(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+ [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields()
+];
+
+for (const [calendar, description] of typeErrorTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(TypeError, () => instance.equals(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/PlainYearMonth/prototype/equals/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-propertybag-calendar-year-zero.js
new file mode 100644
index 0000000000..0e652fd79b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-propertybag-calendar-year-zero.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.plainyearmonth.prototype.equals
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T17:45",
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+01:00",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+const instance = new Temporal.PlainYearMonth(2000, 5);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..5ba6126ca4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-calendar-annotation.js
@@ -0,0 +1,31 @@
+// |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.plainyearmonth.prototype.equals
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2019-12-15T15:23[u-ca=iso8601]", "without time zone"],
+ ["2019-12-15T15:23[UTC][u-ca=iso8601]", "with time zone"],
+ ["2019-12-15T15:23[!u-ca=iso8601]", "with ! and no time zone"],
+ ["2019-12-15T15:23[UTC][!u-ca=iso8601]", "with ! and time zone"],
+ ["2019-12-15T15:23[u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+const instance = new Temporal.PlainYearMonth(2019, 12);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.equals(arg);
+
+ assert.sameValue(
+ result,
+ true,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..9d108c2a70
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-critical-unknown-annotation.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.plainyearmonth.prototype.equals
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "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.PlainYearMonth(2000, 5);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..b24b89be0b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-date-with-utc-offset.js
@@ -0,0 +1,55 @@
+// |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.plainyearmonth.prototype.equals
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2019, 12);
+
+const validStrings = [
+ "2019-12[Africa/Abidjan]",
+ "2019-12[!Africa/Abidjan]",
+ "2019-12[u-ca=iso8601]",
+ "2019-12[Africa/Abidjan][u-ca=iso8601]",
+ "2019-12-15T00+00:00",
+ "2019-12-15T00+00:00[UTC]",
+ "2019-12-15T00+00:00[!UTC]",
+ "2019-12-15T00-02:30[America/St_Johns]",
+];
+
+for (const arg of validStrings) {
+ const result = instance.equals(arg);
+
+ assert.sameValue(
+ result,
+ true,
+ `"${arg}" is a valid UTC offset with time for PlainYearMonth`
+ );
+}
+
+const invalidStrings = [
+ "2022-09[u-ca=hebrew]",
+ "2022-09Z",
+ "2022-09+01:00",
+ "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.equals(arg),
+ `"${arg}" UTC offset without time is not valid for PlainYearMonth`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-invalid.js
new file mode 100644
index 0000000000..dd4584f445
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-invalid.js
@@ -0,0 +1,18 @@
+// |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.plainyearmonth.prototype.equals
+description: An invalid ISO string is never supported
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(1976, 11);
+
+for (const arg of TemporalHelpers.ISO.plainYearMonthStringsInvalid()) {
+ assert.throws(RangeError, () => instance.equals(arg), `"${arg}" is not a valid PlainYearMonth string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..e2abd6b0ef
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/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.plainyearmonth.prototype.equals
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[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.PlainYearMonth(2000, 5);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..76e3ea2a8a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-multiple-time-zone.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.plainyearmonth.prototype.equals
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "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.PlainYearMonth(2000, 5);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-time-separators.js
new file mode 100644
index 0000000000..5eb55c5eed
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-time-separators.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.plainyearmonth.prototype.equals
+description: Time separator in string argument can vary
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2019-12-15T15:23", "uppercase T"],
+ ["2019-12-15t15:23", "lowercase T"],
+ ["2019-12-15 15:23", "space between date and time"],
+];
+
+const instance = new Temporal.PlainYearMonth(2019, 12);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.equals(arg);
+
+ assert.sameValue(
+ result,
+ true,
+ `variant time separators (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..797b6ceaf1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-time-zone-annotation.js
@@ -0,0 +1,34 @@
+// |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.plainyearmonth.prototype.equals
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2019-12-15T15:23[Asia/Kolkata]", "named, with no offset"],
+ ["2019-12-15T15:23[!Europe/Vienna]", "named, with ! and no offset"],
+ ["2019-12-15T15:23[+00:00]", "numeric, with no offset"],
+ ["2019-12-15T15:23[!-02:30]", "numeric, with ! and no offset"],
+ ["2019-12-15T15:23+00:00[UTC]", "named, with offset"],
+ ["2019-12-15T15:23+00:00[!Africa/Abidjan]", "named, with offset and !"],
+ ["2019-12-15T15:23+00:00[+01:00]", "numeric, with offset"],
+ ["2019-12-15T15:23+00:00[!-08:00]", "numeric, with offset and !"],
+];
+
+const instance = new Temporal.PlainYearMonth(2019, 12);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.equals(arg);
+
+ assert.sameValue(
+ result,
+ true,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..ce906672c3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-unknown-annotation.js
@@ -0,0 +1,31 @@
+// |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.plainyearmonth.prototype.equals
+description: Various forms of unknown annotation
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2019-12-15T15:23[foo=bar]", "alone"],
+ ["2019-12-15T15:23[UTC][foo=bar]", "with time zone"],
+ ["2019-12-15T15:23[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["2019-12-15T15:23[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["2019-12-15T15:23[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+const instance = new Temporal.PlainYearMonth(2019, 12);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.equals(arg);
+
+ assert.sameValue(
+ result,
+ true,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..7a5ffda315
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-with-utc-designator.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.plainyearmonth.prototype.equals
+description: RangeError thrown if a string with UTC designator is used as a PlainYearMonth
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "2019-10-01T09:00:00Z",
+ "2019-10-01T09:00:00Z[UTC]",
+];
+const instance = new Temporal.PlainYearMonth(2000, 5);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ "String with UTC designator should not be valid as a PlainYearMonth"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string.js
new file mode 100644
index 0000000000..2a3cae1340
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string.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.plainyearmonth.prototype.equals
+description: A string argument is parsed into a PlainYearMonth
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(1976, 11);
+for (const arg of TemporalHelpers.ISO.plainYearMonthStringsValid()) {
+ const result = instance.equals(arg);
+ assert.sameValue(result, true, `"${arg}" is a valid PlainYearMonth string`);
+}
+
+const instanceNegativeYear = new Temporal.PlainYearMonth(-9999, 11);
+for (const arg of TemporalHelpers.ISO.plainYearMonthStringsValidNegativeYear()) {
+ const result = instanceNegativeYear.equals(arg);
+ assert.sameValue(result, true, `"${arg}" is a valid PlainYearMonth string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-wrong-type.js
new file mode 100644
index 0000000000..1ea064cb25
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/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.plainyearmonth.prototype.equals
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainYearMonth
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2000, 5);
+
+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.equals(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.PlainYearMonth, "Temporal.PlainYearMonth, object"],
+ [Temporal.PlainYearMonth.prototype, "Temporal.PlainYearMonth.prototype, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.equals(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/PlainYearMonth/prototype/equals/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/basic.js
new file mode 100644
index 0000000000..11e1fdfeba
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/basic.js
@@ -0,0 +1,18 @@
+// |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.plainyearmonth.prototype.equals
+description: Basic tests for equals()
+features: [Temporal]
+---*/
+
+const nov94 = Temporal.PlainYearMonth.from("1994-11");
+const nov94bis = Temporal.PlainYearMonth.from("1994-11");
+const jun13 = Temporal.PlainYearMonth.from("2013-06");
+assert.sameValue(nov94.equals(nov94), true, "same object");
+assert.sameValue(nov94.equals(nov94bis), true, "different object");
+assert.sameValue(nov94.equals(jun13), false, "different year-months");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/branding.js
new file mode 100644
index 0000000000..5245e85687
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/branding.js
@@ -0,0 +1,27 @@
+// |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.plainyearmonth.prototype.equals
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const equals = Temporal.PlainYearMonth.prototype.equals;
+
+assert.sameValue(typeof equals, "function");
+
+const args = [new Temporal.PlainYearMonth(2022, 6)];
+
+assert.throws(TypeError, () => equals.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => equals.apply(null, args), "null");
+assert.throws(TypeError, () => equals.apply(true, args), "true");
+assert.throws(TypeError, () => equals.apply("", args), "empty string");
+assert.throws(TypeError, () => equals.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => equals.apply(1, args), "1");
+assert.throws(TypeError, () => equals.apply({}, args), "plain object");
+assert.throws(TypeError, () => equals.apply(Temporal.PlainYearMonth, args), "Temporal.PlainYearMonth");
+assert.throws(TypeError, () => equals.apply(Temporal.PlainYearMonth.prototype, args), "Temporal.PlainYearMonth.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..5e517712ec
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plainyearmonth.prototype.equals
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const idOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "id");
+Object.defineProperty(Temporal.Calendar.prototype, "id", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("id should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainYearMonth(2000, 5, "iso8601", 1);
+instance.equals(new Temporal.PlainYearMonth(2000, 5));
+
+Object.defineProperty(Temporal.Calendar.prototype, "id", idOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/builtin.js
new file mode 100644
index 0000000000..13b8448d6e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/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.plainyearmonth.prototype.equals
+description: >
+ Tests that Temporal.PlainYearMonth.prototype.equals
+ 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.PlainYearMonth.prototype.equals),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainYearMonth.prototype.equals),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainYearMonth.prototype.equals),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainYearMonth.prototype.equals.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/calendar-datefromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..02d8d9ee6a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/calendar-datefromfields-called-with-null-prototype-fields.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.plainyearmonth.prototype.equals
+description: >
+ Calendar.yearMonthFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.PlainYearMonth(2000, 5);
+const arg = { year: 2000, month: 5, calendar };
+instance.equals(arg);
+assert.sameValue(calendar.yearMonthFromFieldsCallCount, 1, "yearMonthFromFields should be called on the property bag's calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/calendar-fields-iterable.js
new file mode 100644
index 0000000000..d82f9bd6d8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/calendar-fields-iterable.js
@@ -0,0 +1,35 @@
+// |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.plainyearmonth.prototype.equals
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plainyearmonth.prototype.equals step 3:
+ 3. Set _other_ to ? ToTemporalYearMonth(_other_).
+ sec-temporal-totemporalyearmonth step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const yearmonth = new Temporal.PlainYearMonth(2000, 5, calendar1);
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+yearmonth.equals({ year: 2005, month: 6, calendar: calendar2 });
+
+assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/calendar-temporal-object.js
new file mode 100644
index 0000000000..358f74051f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/calendar-temporal-object.js
@@ -0,0 +1,29 @@
+// |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.plainyearmonth.prototype.equals
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plainyearmonth.prototype.equals step 3:
+ 3. Set _other_ to ? ToTemporalYearMonth(_other_).
+ sec-temporal-totemporalyearmonth step 2.b:
+ b. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const yearmonth = new Temporal.PlainYearMonth(2000, 5, temporalObject);
+ yearmonth.equals({ year: 2005, month: 6, calendar: temporalObject });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/calendar-yearmonthfromfields-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/calendar-yearmonthfromfields-called-with-options-undefined.js
new file mode 100644
index 0000000000..b9dfa79a80
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/calendar-yearmonthfromfields-called-with-options-undefined.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.plainyearmonth.prototype.equals
+description: >
+ Calendar.yearMonthFromFields method is called with undefined as the options
+ value when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+let calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
+let instance = new Temporal.PlainYearMonth(2000, 5, calendar);
+instance.equals({ year: 2000, month: 6, calendar });
+assert.sameValue(calendar.yearMonthFromFieldsCallCount, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/compare-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/compare-calendar.js
new file mode 100644
index 0000000000..b16fab695c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/compare-calendar.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.plainyearmonth.prototype.equals
+description: equals() takes the calendar into account
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+class CustomCalendar extends Temporal.Calendar {
+ constructor(id) {
+ super("iso8601");
+ this._id = id;
+ }
+ get id() {
+ actual.push(this._id);
+ return this._id;
+ }
+ toString() {
+ TemporalHelpers.assertUnreachable("should not call toString");
+ }
+}
+
+const sharedCalendar = new CustomCalendar("a");
+const ym1 = new Temporal.PlainYearMonth(2000, 1, sharedCalendar, 1);
+const ym2 = new Temporal.PlainYearMonth(2000, 1, sharedCalendar, 1);
+assert.sameValue(ym1.equals(ym2), true);
+assert.compareArray(actual, [], "should not call toString if objects are equal");
+
+const ym3 = new Temporal.PlainYearMonth(2000, 1, new CustomCalendar("b"), 1);
+const ym4 = new Temporal.PlainYearMonth(2000, 1, new CustomCalendar("c"), 2);
+assert.sameValue(ym3.equals(ym4), false);
+assert.compareArray(actual, [], "should not call toString if ISO dates differ");
+
+const ym5 = new Temporal.PlainYearMonth(2000, 1, new CustomCalendar("d"), 1);
+const ym6 = new Temporal.PlainYearMonth(2000, 1, new CustomCalendar("e"), 1);
+assert.sameValue(ym5.equals(ym6), false);
+assert.compareArray(actual, ["d", "e"], "order of operations");
+
+actual.splice(0); // empty it for the next check
+const ym7 = new Temporal.PlainYearMonth(2000, 1, new CustomCalendar("f"), 1);
+const ym8 = new Temporal.PlainYearMonth(2000, 1, new CustomCalendar("f"), 1);
+assert.sameValue(ym7.equals(ym8), true);
+assert.compareArray(actual, ["f", "f"], "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/compare-reference-day.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/compare-reference-day.js
new file mode 100644
index 0000000000..12a1ebcfe6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/compare-reference-day.js
@@ -0,0 +1,16 @@
+// |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.plainyearmonth.prototype.equals
+description: equals() takes the reference day into account
+features: [Temporal]
+---*/
+
+const iso = Temporal.Calendar.from("iso8601");
+const ym1 = new Temporal.PlainYearMonth(2000, 1, iso, 1);
+const ym2 = new Temporal.PlainYearMonth(2000, 1, iso, 2);
+assert.sameValue(ym1.equals(ym2), false);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..a86af5a09d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/constructor-in-calendar-fields.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.plainyearmonth.prototype.equals
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.PlainYearMonth(2000, 5, calendar);
+
+assert.throws(RangeError, () => instance.equals(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/duplicate-calendar-fields.js
new file mode 100644
index 0000000000..028b5de48e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/duplicate-calendar-fields.js
@@ -0,0 +1,19 @@
+// |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.plainyearmonth.prototype.equals
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['month'], ['monthCode'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar };
+ const instance = new Temporal.PlainYearMonth(2000, 5, calendar);
+
+ assert.throws(RangeError, () => instance.equals(arg));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..51b7eb7e73
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/infinity-throws-rangeerror.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.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plainyearmonth.prototype.equals
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2000, 5);
+const base = { year: 2000, month: 5 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.equals({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.equals({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/leap-second.js
new file mode 100644
index 0000000000..ecfb5aa8e0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/leap-second.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.plainyearmonth.prototype.equals
+description: Leap second is a valid ISO string for PlainYearMonth
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2016, 12);
+
+let arg = "2016-12-31T23:59:60";
+const result1 = instance.equals(arg);
+assert.sameValue(
+ result1,
+ true,
+ "leap second is a valid ISO string for PlainYearMonth"
+);
+
+arg = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+const result2 = instance.equals(arg);
+assert.sameValue(
+ result2,
+ true,
+ "second: 60 is ignored in property bag for PlainYearMonth"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/length.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/length.js
new file mode 100644
index 0000000000..844a0f96da
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/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.plainyearmonth.prototype.equals
+description: Temporal.PlainYearMonth.prototype.equals.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.PlainYearMonth.prototype.equals, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/name.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/name.js
new file mode 100644
index 0000000000..686b5adae1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/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.plainyearmonth.prototype.equals
+description: Temporal.PlainYearMonth.prototype.equals.name is "equals".
+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.PlainYearMonth.prototype.equals, "name", {
+ value: "equals",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/not-a-constructor.js
new file mode 100644
index 0000000000..77c8d7e6eb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/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.plainyearmonth.prototype.equals
+description: >
+ Temporal.PlainYearMonth.prototype.equals 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.PlainYearMonth.prototype.equals();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainYearMonth.prototype.equals), false,
+ "isConstructor(Temporal.PlainYearMonth.prototype.equals)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/prop-desc.js
new file mode 100644
index 0000000000..8da8c28acc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/prop-desc.js
@@ -0,0 +1,24 @@
+// |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.plainyearmonth.prototype.equals
+description: The "equals" property of Temporal.PlainYearMonth.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainYearMonth.prototype.equals,
+ "function",
+ "`typeof PlainYearMonth.prototype.equals` is `function`"
+);
+
+verifyProperty(Temporal.PlainYearMonth.prototype, "equals", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/proto-in-calendar-fields.js
new file mode 100644
index 0000000000..cf77b12b86
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/proto-in-calendar-fields.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.plainyearmonth.prototype.equals
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.PlainYearMonth(2000, 5, calendar);
+
+assert.throws(RangeError, () => instance.equals(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/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/PlainYearMonth/prototype/equals/use-internal-slots.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/use-internal-slots.js
new file mode 100644
index 0000000000..a7d728148c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/use-internal-slots.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.plainyearmonth.prototype.equals
+description: equals() ignores the observable properties and uses internal slots
+features: [Temporal]
+---*/
+
+function CustomError() {}
+
+class AvoidGettersYearMonth extends Temporal.PlainYearMonth {
+ get year() {
+ throw new CustomError();
+ }
+ get month() {
+ throw new CustomError();
+ }
+}
+
+const one = new AvoidGettersYearMonth(2000, 5);
+const two = new AvoidGettersYearMonth(2006, 3);
+assert.sameValue(one.equals(two), false);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/year-zero.js
new file mode 100644
index 0000000000..97c0979390
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/year-zero.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.plainyearmonth.prototype.equals
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-06",
+ "-000000-06-24",
+ "-000000-06-24T15:43:27",
+ "-000000-06-24T15:43:27+01:00",
+ "-000000-06-24T15:43:27+00:00[UTC]",
+];
+const instance = new Temporal.PlainYearMonth(2000, 5);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getCalendar/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getCalendar/branding.js
new file mode 100644
index 0000000000..e98b91f378
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getCalendar/branding.js
@@ -0,0 +1,25 @@
+// |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.plainyearmonth.prototype.getcalendar
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const getCalendar = Temporal.PlainYearMonth.prototype.getCalendar;
+
+assert.sameValue(typeof getCalendar, "function");
+
+assert.throws(TypeError, () => getCalendar.call(undefined), "undefined");
+assert.throws(TypeError, () => getCalendar.call(null), "null");
+assert.throws(TypeError, () => getCalendar.call(true), "true");
+assert.throws(TypeError, () => getCalendar.call(""), "empty string");
+assert.throws(TypeError, () => getCalendar.call(Symbol()), "symbol");
+assert.throws(TypeError, () => getCalendar.call(1), "1");
+assert.throws(TypeError, () => getCalendar.call({}), "plain object");
+assert.throws(TypeError, () => getCalendar.call(Temporal.PlainYearMonth), "Temporal.PlainYearMonth");
+assert.throws(TypeError, () => getCalendar.call(Temporal.PlainYearMonth.prototype), "Temporal.PlainYearMonth.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getCalendar/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getCalendar/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getCalendar/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getCalendar/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getCalendar/builtin.js
new file mode 100644
index 0000000000..dd5e96de16
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getCalendar/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.plainyearmonth.prototype.getcalendar
+description: >
+ Tests that Temporal.PlainYearMonth.prototype.getCalendar
+ 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.PlainYearMonth.prototype.getCalendar),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainYearMonth.prototype.getCalendar),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainYearMonth.prototype.getCalendar),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainYearMonth.prototype.getCalendar.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getCalendar/length.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getCalendar/length.js
new file mode 100644
index 0000000000..197adcd4d8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getCalendar/length.js
@@ -0,0 +1,28 @@
+// |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.plainyearmonth.prototype.getcalendar
+description: Temporal.PlainYearMonth.prototype.getCalendar.length is 0
+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.PlainYearMonth.prototype.getCalendar, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getCalendar/name.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getCalendar/name.js
new file mode 100644
index 0000000000..80d117d82e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getCalendar/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.plainyearmonth.prototype.getcalendar
+description: Temporal.PlainYearMonth.prototype.getCalendar.name is "getCalendar".
+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.PlainYearMonth.prototype.getCalendar, "name", {
+ value: "getCalendar",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getCalendar/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getCalendar/not-a-constructor.js
new file mode 100644
index 0000000000..4da964f6cf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getCalendar/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.plainyearmonth.prototype.getcalendar
+description: >
+ Temporal.PlainYearMonth.prototype.getCalendar 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.PlainYearMonth.prototype.getCalendar();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainYearMonth.prototype.getCalendar), false,
+ "isConstructor(Temporal.PlainYearMonth.prototype.getCalendar)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getCalendar/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getCalendar/prop-desc.js
new file mode 100644
index 0000000000..76161c39d8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getCalendar/prop-desc.js
@@ -0,0 +1,24 @@
+// |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.plainyearmonth.prototype.getcalendar
+description: The "getCalendar" property of Temporal.PlainYearMonth.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainYearMonth.prototype.getCalendar,
+ "function",
+ "`typeof PlainYearMonth.prototype.getCalendar` is `function`"
+);
+
+verifyProperty(Temporal.PlainYearMonth.prototype, "getCalendar", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getCalendar/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getCalendar/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getCalendar/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/PlainYearMonth/prototype/getISOFields/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/branding.js
new file mode 100644
index 0000000000..c8b335d42a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/branding.js
@@ -0,0 +1,25 @@
+// |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.plainyearmonth.prototype.getisofields
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const getISOFields = Temporal.PlainYearMonth.prototype.getISOFields;
+
+assert.sameValue(typeof getISOFields, "function");
+
+assert.throws(TypeError, () => getISOFields.call(undefined), "undefined");
+assert.throws(TypeError, () => getISOFields.call(null), "null");
+assert.throws(TypeError, () => getISOFields.call(true), "true");
+assert.throws(TypeError, () => getISOFields.call(""), "empty string");
+assert.throws(TypeError, () => getISOFields.call(Symbol()), "symbol");
+assert.throws(TypeError, () => getISOFields.call(1), "1");
+assert.throws(TypeError, () => getISOFields.call({}), "plain object");
+assert.throws(TypeError, () => getISOFields.call(Temporal.PlainYearMonth), "Temporal.PlainYearMonth");
+assert.throws(TypeError, () => getISOFields.call(Temporal.PlainYearMonth.prototype), "Temporal.PlainYearMonth.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/builtin.js
new file mode 100644
index 0000000000..802f49025f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/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.plainyearmonth.prototype.getisofields
+description: >
+ Tests that Temporal.PlainYearMonth.prototype.getISOFields
+ 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.PlainYearMonth.prototype.getISOFields),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainYearMonth.prototype.getISOFields),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainYearMonth.prototype.getISOFields),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainYearMonth.prototype.getISOFields.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/custom.js
new file mode 100644
index 0000000000..66cdc86229
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/custom.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.plainyearmonth.prototype.getisofields
+description: getISOFields does not call into user code.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarThrowEverything();
+const instance = new Temporal.PlainYearMonth(2000, 5, calendar);
+const result = instance.getISOFields();
+
+assert.sameValue(result.isoYear, 2000, "isoYear result");
+assert.sameValue(result.isoMonth, 5, "isoMonth result");
+assert.sameValue(result.isoDay, 1, "isoDay result");
+assert.sameValue(result.calendar, calendar, "calendar result");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/field-names.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/field-names.js
new file mode 100644
index 0000000000..e655c4af18
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/field-names.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.plainyearmonth.prototype.getisofields
+description: Correct field names on the object returned from getISOFields
+features: [Temporal]
+---*/
+
+const ym = new Temporal.PlainYearMonth(2000, 5);
+
+const result = ym.getISOFields();
+assert.sameValue(result.isoYear, 2000, "isoYear result");
+assert.sameValue(result.isoMonth, 5, "isoMonth result");
+assert.sameValue(result.isoDay, 1, "isoDay result");
+assert.sameValue(result.calendar, "iso8601", "calendar result");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/field-prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/field-prop-desc.js
new file mode 100644
index 0000000000..cc86065908
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/field-prop-desc.js
@@ -0,0 +1,30 @@
+// |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.plainyearmonth.prototype.getisofields
+description: Properties on the returned object have the correct descriptor
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "calendar",
+ "isoDay",
+ "isoMonth",
+ "isoYear",
+];
+
+const ym = new Temporal.PlainYearMonth(2000, 5);
+const result = ym.getISOFields();
+
+for (const property of expected) {
+ verifyProperty(result, property, {
+ writable: true,
+ enumerable: true,
+ configurable: true,
+ });
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/field-traversal-order.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/field-traversal-order.js
new file mode 100644
index 0000000000..0134ca0cbd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/field-traversal-order.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.plainyearmonth.prototype.getisofields
+description: Properties added in correct order to object returned from getISOFields
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "calendar",
+ "isoDay",
+ "isoMonth",
+ "isoYear",
+];
+
+const ym = new Temporal.PlainYearMonth(2000, 5);
+const result = ym.getISOFields();
+
+assert.compareArray(Object.keys(result), expected);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/length.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/length.js
new file mode 100644
index 0000000000..ce4b0d448f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/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.plainyearmonth.prototype.getisofields
+description: Temporal.PlainYearMonth.prototype.getISOFields.length is 0
+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.PlainYearMonth.prototype.getISOFields, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/name.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/name.js
new file mode 100644
index 0000000000..5294ab200d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/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.plainyearmonth.prototype.getisofields
+description: Temporal.PlainYearMonth.prototype.getISOFields.name is "getISOFields".
+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.PlainYearMonth.prototype.getISOFields, "name", {
+ value: "getISOFields",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/not-a-constructor.js
new file mode 100644
index 0000000000..c1b408b939
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/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.plainyearmonth.prototype.getisofields
+description: >
+ Temporal.PlainYearMonth.prototype.getISOFields 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.PlainYearMonth.prototype.getISOFields();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainYearMonth.prototype.getISOFields), false,
+ "isConstructor(Temporal.PlainYearMonth.prototype.getISOFields)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/prop-desc.js
new file mode 100644
index 0000000000..654a834e03
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/prop-desc.js
@@ -0,0 +1,24 @@
+// |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.plainyearmonth.prototype.getisofields
+description: The "getISOFields" property of Temporal.PlainYearMonth.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainYearMonth.prototype.getISOFields,
+ "function",
+ "`typeof PlainYearMonth.prototype.getISOFields` is `function`"
+);
+
+verifyProperty(Temporal.PlainYearMonth.prototype, "getISOFields", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/prototype.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/prototype.js
new file mode 100644
index 0000000000..7f6bdda8ac
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/prototype.js
@@ -0,0 +1,15 @@
+// |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.plainyearmonth.prototype.getisofields
+description: Correct prototype on the object returned from getISOFields
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2000, 5);
+const result = instance.getISOFields();
+assert.sameValue(Object.getPrototypeOf(result), Object.prototype, "prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/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/PlainYearMonth/prototype/inLeapYear/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/inLeapYear/basic.js
new file mode 100644
index 0000000000..3797a79e4b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/inLeapYear/basic.js
@@ -0,0 +1,16 @@
+// |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-get-temporal.plainyearmonth.prototype.inleapyear
+description: Basic test for inLeapYear
+features: [Temporal]
+---*/
+
+assert.sameValue((new Temporal.PlainYearMonth(1976, 11)).inLeapYear,
+ true, "leap year");
+assert.sameValue((new Temporal.PlainYearMonth(1977, 11)).inLeapYear,
+ false, "non-leap year");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/inLeapYear/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/inLeapYear/branding.js
new file mode 100644
index 0000000000..d89a21bde5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/inLeapYear/branding.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-get-temporal.plainyearmonth.prototype.inleapyear
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const inLeapYear = Object.getOwnPropertyDescriptor(Temporal.PlainYearMonth.prototype, "inLeapYear").get;
+
+assert.sameValue(typeof inLeapYear, "function");
+
+assert.throws(TypeError, () => inLeapYear.call(undefined), "undefined");
+assert.throws(TypeError, () => inLeapYear.call(null), "null");
+assert.throws(TypeError, () => inLeapYear.call(true), "true");
+assert.throws(TypeError, () => inLeapYear.call(""), "empty string");
+assert.throws(TypeError, () => inLeapYear.call(Symbol()), "symbol");
+assert.throws(TypeError, () => inLeapYear.call(1), "1");
+assert.throws(TypeError, () => inLeapYear.call({}), "plain object");
+assert.throws(TypeError, () => inLeapYear.call(Temporal.PlainYearMonth), "Temporal.PlainYearMonth");
+assert.throws(TypeError, () => inLeapYear.call(Temporal.PlainYearMonth.prototype), "Temporal.PlainYearMonth.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/inLeapYear/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/inLeapYear/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/inLeapYear/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/inLeapYear/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/inLeapYear/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..a826d38570
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/inLeapYear/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plainyearmonth.prototype.inleapyear
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const inLeapYearOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "inLeapYear");
+Object.defineProperty(Temporal.Calendar.prototype, "inLeapYear", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("inLeapYear should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainYearMonth(2000, 5, "iso8601", 1);
+instance.inLeapYear;
+
+Object.defineProperty(Temporal.Calendar.prototype, "inLeapYear", inLeapYearOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/inLeapYear/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/inLeapYear/custom.js
new file mode 100644
index 0000000000..f131227eac
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/inLeapYear/custom.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-get-temporal.plainyearmonth.prototype.inleapyear
+description: Custom calendar tests for inLeapYear().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ inLeapYear(...args) {
+ ++calls;
+ assert.compareArray(args, [instance], "inLeapYear arguments");
+ return true;
+ }
+}
+
+const calendar = new CustomCalendar();
+const instance = new Temporal.PlainYearMonth(1830, 8, calendar);
+const result = instance.inLeapYear;
+assert.sameValue(result, true, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/inLeapYear/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/inLeapYear/prop-desc.js
new file mode 100644
index 0000000000..ab83da7706
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/inLeapYear/prop-desc.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-get-temporal.plainyearmonth.prototype.inleapyear
+description: The "inLeapYear" property of Temporal.PlainYearMonth.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainYearMonth.prototype, "inLeapYear");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/inLeapYear/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/inLeapYear/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/inLeapYear/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/inLeapYear/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/inLeapYear/validate-calendar-value.js
new file mode 100644
index 0000000000..dbcb59baec
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/inLeapYear/validate-calendar-value.js
@@ -0,0 +1,56 @@
+// |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-get-temporal.plainyearmonth.prototype.inleapyear
+description: Validate result returned from calendar inLeapYear() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [null, TypeError],
+ [0, TypeError],
+ [-0, TypeError],
+ [42, TypeError],
+ [7.1, TypeError],
+ [NaN, TypeError],
+ [Infinity, TypeError],
+ [-Infinity, TypeError],
+ ["", TypeError],
+ ["a string", TypeError],
+ ["0", TypeError],
+ [Symbol("foo"), TypeError],
+ [0n, TypeError],
+ [42n, TypeError],
+ [{}, TypeError],
+ [{valueOf() { return false; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ inLeapYear() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainYearMonth(1981, 12, calendar);
+ assert.throws(error, () => instance.inLeapYear, `${typeof result} ${String(result)} not converted to boolean`);
+});
+
+const preservedResults = [
+ true,
+ false,
+];
+
+preservedResults.forEach(result => {
+ const calendar = new class extends Temporal.Calendar {
+ inLeapYear() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainYearMonth(1981, 12, calendar);
+ assert.sameValue(instance.inLeapYear, result, `${typeof result} ${String(result)} preserved`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/month/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/month/branding.js
new file mode 100644
index 0000000000..5f69bd865d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/month/branding.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-get-temporal.plainyearmonth.prototype.month
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const month = Object.getOwnPropertyDescriptor(Temporal.PlainYearMonth.prototype, "month").get;
+
+assert.sameValue(typeof month, "function");
+
+assert.throws(TypeError, () => month.call(undefined), "undefined");
+assert.throws(TypeError, () => month.call(null), "null");
+assert.throws(TypeError, () => month.call(true), "true");
+assert.throws(TypeError, () => month.call(""), "empty string");
+assert.throws(TypeError, () => month.call(Symbol()), "symbol");
+assert.throws(TypeError, () => month.call(1), "1");
+assert.throws(TypeError, () => month.call({}), "plain object");
+assert.throws(TypeError, () => month.call(Temporal.PlainYearMonth), "Temporal.PlainYearMonth");
+assert.throws(TypeError, () => month.call(Temporal.PlainYearMonth.prototype), "Temporal.PlainYearMonth.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/month/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/month/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/month/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/month/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/month/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..5750040a72
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/month/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plainyearmonth.prototype.month
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const monthOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "month");
+Object.defineProperty(Temporal.Calendar.prototype, "month", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("month should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainYearMonth(2000, 5, "iso8601", 1);
+instance.month;
+
+Object.defineProperty(Temporal.Calendar.prototype, "month", monthOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/month/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/month/custom.js
new file mode 100644
index 0000000000..64afcd70f3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/month/custom.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-get-temporal.plainyearmonth.prototype.month
+description: Custom calendar tests for month().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ month(...args) {
+ ++calls;
+ assert.compareArray(args, [instance], "month arguments");
+ return 7;
+ }
+}
+
+const calendar = new CustomCalendar();
+const instance = new Temporal.PlainYearMonth(1830, 8, calendar);
+const result = instance.month;
+assert.sameValue(result, 7, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/month/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/month/prop-desc.js
new file mode 100644
index 0000000000..b4f16e85ac
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/month/prop-desc.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-get-temporal.plainyearmonth.prototype.month
+description: The "month" property of Temporal.PlainYearMonth.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainYearMonth.prototype, "month");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/month/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/month/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/month/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/month/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/month/validate-calendar-value.js
new file mode 100644
index 0000000000..2beafb2476
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/month/validate-calendar-value.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-get-temporal.plainyearmonth.prototype.month
+description: Validate result returned from calendar month() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [null, TypeError],
+ [false, TypeError],
+ [Infinity, RangeError],
+ [-Infinity, RangeError],
+ [NaN, RangeError],
+ [-7, RangeError],
+ [-0.1, RangeError],
+ ["string", TypeError],
+ [Symbol("foo"), TypeError],
+ [7n, TypeError],
+ [{}, TypeError],
+ [true, TypeError],
+ [7.1, RangeError],
+ ["7", TypeError],
+ ["7.5", TypeError],
+ [{valueOf() { return 7; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ month() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainYearMonth(1981, 12, calendar);
+ assert.throws(error, () => instance.month, `${typeof result} ${String(result)} not converted to positive integer`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthCode/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthCode/branding.js
new file mode 100644
index 0000000000..d6b41374a8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthCode/branding.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-get-temporal.plainyearmonth.prototype.monthcode
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const monthCode = Object.getOwnPropertyDescriptor(Temporal.PlainYearMonth.prototype, "monthCode").get;
+
+assert.sameValue(typeof monthCode, "function");
+
+assert.throws(TypeError, () => monthCode.call(undefined), "undefined");
+assert.throws(TypeError, () => monthCode.call(null), "null");
+assert.throws(TypeError, () => monthCode.call(true), "true");
+assert.throws(TypeError, () => monthCode.call(""), "empty string");
+assert.throws(TypeError, () => monthCode.call(Symbol()), "symbol");
+assert.throws(TypeError, () => monthCode.call(1), "1");
+assert.throws(TypeError, () => monthCode.call({}), "plain object");
+assert.throws(TypeError, () => monthCode.call(Temporal.PlainYearMonth), "Temporal.PlainYearMonth");
+assert.throws(TypeError, () => monthCode.call(Temporal.PlainYearMonth.prototype), "Temporal.PlainYearMonth.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthCode/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthCode/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthCode/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthCode/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthCode/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..d3db8ede45
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthCode/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plainyearmonth.prototype.monthcode
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const monthCodeOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "monthCode");
+Object.defineProperty(Temporal.Calendar.prototype, "monthCode", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("monthCode should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainYearMonth(2000, 5, "iso8601", 1);
+instance.monthCode;
+
+Object.defineProperty(Temporal.Calendar.prototype, "monthCode", monthCodeOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthCode/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthCode/custom.js
new file mode 100644
index 0000000000..bb7255b0ee
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthCode/custom.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-get-temporal.plainyearmonth.prototype.monthcode
+description: Custom calendar tests for monthCode().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ monthCode(...args) {
+ ++calls;
+ assert.compareArray(args, [instance], "monthCode arguments");
+ return "M01";
+ }
+}
+
+const calendar = new CustomCalendar();
+const instance = new Temporal.PlainYearMonth(1830, 8, calendar);
+const result = instance.monthCode;
+assert.sameValue(result, "M01", "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthCode/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthCode/prop-desc.js
new file mode 100644
index 0000000000..00fd595d89
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthCode/prop-desc.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-get-temporal.plainyearmonth.prototype.monthcode
+description: The "monthCode" property of Temporal.PlainYearMonth.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainYearMonth.prototype, "monthCode");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthCode/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthCode/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthCode/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthCode/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthCode/validate-calendar-value.js
new file mode 100644
index 0000000000..49326950bd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthCode/validate-calendar-value.js
@@ -0,0 +1,31 @@
+// |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-get-temporal.plainyearmonth.prototype.monthcode
+description: Validate result returned from calendar monthCode() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [Symbol("foo"), TypeError],
+ [null, TypeError],
+ [true, TypeError],
+ [false, TypeError],
+ [7.1, TypeError],
+ [{toString() { return "M01"; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ monthCode() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainYearMonth(1981, 12, calendar);
+ assert.throws(error, () => instance.monthCode, `${typeof result} ${String(result)} not converted to string`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/basic.js
new file mode 100644
index 0000000000..9b6c340271
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/basic.js
@@ -0,0 +1,14 @@
+// |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-get-temporal.plainyearmonth.prototype.monthsinyear
+description: monthsInYear works
+features: [Temporal]
+---*/
+
+const ym = new Temporal.PlainYearMonth(1976, 11);
+assert.sameValue(ym.monthsInYear, 12);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/branding.js
new file mode 100644
index 0000000000..a96c7b5a5f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/branding.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-get-temporal.plainyearmonth.prototype.monthsinyear
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const monthsInYear = Object.getOwnPropertyDescriptor(Temporal.PlainYearMonth.prototype, "monthsInYear").get;
+
+assert.sameValue(typeof monthsInYear, "function");
+
+assert.throws(TypeError, () => monthsInYear.call(undefined), "undefined");
+assert.throws(TypeError, () => monthsInYear.call(null), "null");
+assert.throws(TypeError, () => monthsInYear.call(true), "true");
+assert.throws(TypeError, () => monthsInYear.call(""), "empty string");
+assert.throws(TypeError, () => monthsInYear.call(Symbol()), "symbol");
+assert.throws(TypeError, () => monthsInYear.call(1), "1");
+assert.throws(TypeError, () => monthsInYear.call({}), "plain object");
+assert.throws(TypeError, () => monthsInYear.call(Temporal.PlainYearMonth), "Temporal.PlainYearMonth");
+assert.throws(TypeError, () => monthsInYear.call(Temporal.PlainYearMonth.prototype), "Temporal.PlainYearMonth.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..1ecaeffa7b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plainyearmonth.prototype.monthsinyear
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const monthsInYearOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "monthsInYear");
+Object.defineProperty(Temporal.Calendar.prototype, "monthsInYear", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("monthsInYear should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainYearMonth(2000, 5, "iso8601", 1);
+instance.monthsInYear;
+
+Object.defineProperty(Temporal.Calendar.prototype, "monthsInYear", monthsInYearOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/custom.js
new file mode 100644
index 0000000000..db48e8f44f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/custom.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-get-temporal.plainyearmonth.prototype.monthsinyear
+description: Custom calendar tests for monthsInYear().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ monthsInYear(...args) {
+ ++calls;
+ assert.compareArray(args, [instance], "monthsInYear arguments");
+ return 7;
+ }
+}
+
+const calendar = new CustomCalendar();
+const instance = new Temporal.PlainYearMonth(1830, 8, calendar);
+const result = instance.monthsInYear;
+assert.sameValue(result, 7, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/prop-desc.js
new file mode 100644
index 0000000000..b877378c7b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/prop-desc.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-get-temporal.plainyearmonth.prototype.monthsinyear
+description: The "monthsInYear" property of Temporal.PlainYearMonth.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainYearMonth.prototype, "monthsInYear");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/validate-calendar-value.js
new file mode 100644
index 0000000000..a06c23ef68
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/validate-calendar-value.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-get-temporal.plainyearmonth.prototype.monthsinyear
+description: Validate result returned from calendar monthsInYear() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [null, TypeError],
+ [false, TypeError],
+ [Infinity, RangeError],
+ [-Infinity, RangeError],
+ [NaN, RangeError],
+ [-7, RangeError],
+ [-0.1, RangeError],
+ ["string", TypeError],
+ [Symbol("foo"), TypeError],
+ [7n, TypeError],
+ [{}, TypeError],
+ [true, TypeError],
+ [7.1, RangeError],
+ ["7", TypeError],
+ ["7.5", TypeError],
+ [{valueOf() { return 7; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ monthsInYear() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainYearMonth(1981, 12, calendar);
+ assert.throws(error, () => instance.monthsInYear, `${typeof result} ${String(result)} not converted to positive integer`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/prop-desc.js
new file mode 100644
index 0000000000..3444d7fc57
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/prop-desc.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-plainyearmonth-prototype
+description: The "prototype" property of Temporal.PlainYearMonth
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(typeof Temporal.PlainYearMonth.prototype, "object");
+assert.notSameValue(Temporal.PlainYearMonth.prototype, null);
+
+verifyProperty(Temporal.PlainYearMonth, "prototype", {
+ writable: false,
+ enumerable: false,
+ configurable: false,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..7a611af37f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-builtin-calendar-no-array-iteration.js
@@ -0,0 +1,24 @@
+// |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.plainyearmonth.prototype.since
+description: >
+ Calling the method with a property bag argument with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.PlainYearMonth(2000, 5);
+const arg = { year: 2000, month: 5, calendar: "iso8601" };
+instance.since(arg);
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-casting.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-casting.js
new file mode 100644
index 0000000000..22f10a717e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-casting.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.plainyearmonth.prototype.since
+description: Calls to PYM.since cast arguments.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const nov94 = new Temporal.PlainYearMonth(1994, 11);
+const jun13 = new Temporal.PlainYearMonth(2013, 6);
+const diff = jun13.since(nov94);
+
+TemporalHelpers.assertDurationsEqual(jun13.since({ year: 1994, month: 11 }), diff, 'Casts object argument');
+TemporalHelpers.assertDurationsEqual(jun13.since('1994-11'), diff, 'Casts string argument');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-number.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-number.js
new file mode 100644
index 0000000000..a156c0e661
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/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.plainyearmonth.prototype.since
+description: A number is invalid in place of an ISO string for Temporal.PlainYearMonth
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2019, 6);
+
+const numbers = [
+ 1,
+ 201906,
+ -201906,
+ 1234567,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => instance.since(arg),
+ `A number (${arg}) is not a valid ISO string for PlainYearMonth`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-propertybag-calendar-case-insensitive.js
new file mode 100644
index 0000000000..0c2882525b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-propertybag-calendar-case-insensitive.js
@@ -0,0 +1,20 @@
+// |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.plainyearmonth.prototype.since
+description: The calendar name is case-insensitive
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2019, 6);
+
+const calendar = "IsO8601";
+
+const arg = { year: 2019, monthCode: "M06", calendar };
+const result = instance.since(arg);
+TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-propertybag-calendar-leap-second.js
new file mode 100644
index 0000000000..4c9b860cff
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-propertybag-calendar-leap-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.plainyearmonth.prototype.since
+description: Leap second is a valid ISO string for a calendar in a property bag
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2019, 6);
+
+const calendar = "2016-12-31T23:59:60";
+
+const arg = { year: 2019, monthCode: "M06", calendar };
+const result = instance.since(arg);
+TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "leap second is a valid ISO string for calendar"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-propertybag-calendar-number.js
new file mode 100644
index 0000000000..08a1caa46b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-propertybag-calendar-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.plainyearmonth.prototype.since
+description: A number as calendar in a property bag is not accepted
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(1976, 11, 18);
+
+const numbers = [
+ 1,
+ 19970327,
+ -19970327,
+ 1234567890,
+];
+for (const calendar of numbers) {
+ const arg = { year: 2019, monthCode: "M06", calendar };
+ assert.throws(
+ TypeError,
+ () => instance.since(arg),
+ "Numbers cannot be used as a calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-propertybag-calendar-string.js
new file mode 100644
index 0000000000..94f7794ff5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-propertybag-calendar-string.js
@@ -0,0 +1,20 @@
+// |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.plainyearmonth.prototype.since
+description: A calendar ID is valid input for Calendar
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2019, 6);
+
+const calendar = "iso8601";
+
+const arg = { year: 2019, monthCode: "M06", calendar };
+const result = instance.since(arg);
+TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, `Calendar created from string "${calendar}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-propertybag-calendar-wrong-type.js
new file mode 100644
index 0000000000..6f2564adcd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-propertybag-calendar-wrong-type.js
@@ -0,0 +1,46 @@
+// |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.plainyearmonth.prototype.since
+description: >
+ Appropriate error thrown when a calendar property from a property bag cannot
+ be converted to a calendar object or string
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.PlainYearMonth(2000, 5);
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [calendar, description] of primitiveTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ typeof calendar === 'string' ? RangeError : TypeError,
+ () => instance.since(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+ [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields()
+];
+
+for (const [calendar, description] of typeErrorTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ 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/PlainYearMonth/prototype/since/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-propertybag-calendar-year-zero.js
new file mode 100644
index 0000000000..6c0b70f03b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-propertybag-calendar-year-zero.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.plainyearmonth.prototype.since
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T17:45",
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+01:00",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+const instance = new Temporal.PlainYearMonth(2000, 5);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.since(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..49771507ed
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-calendar-annotation.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.plainyearmonth.prototype.since
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2019-12-15T15:23[u-ca=iso8601]", "without time zone"],
+ ["2019-12-15T15:23[UTC][u-ca=iso8601]", "with time zone"],
+ ["2019-12-15T15:23[!u-ca=iso8601]", "with ! and no time zone"],
+ ["2019-12-15T15:23[UTC][!u-ca=iso8601]", "with ! and time zone"],
+ ["2019-12-15T15:23[u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+const instance = new Temporal.PlainYearMonth(2019, 12);
+
+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/PlainYearMonth/prototype/since/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..5ecbd63ffa
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-critical-unknown-annotation.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.plainyearmonth.prototype.since
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "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.PlainYearMonth(2000, 5);
+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/PlainYearMonth/prototype/since/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..fc41f5a947
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-date-with-utc-offset.js
@@ -0,0 +1,56 @@
+// |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.plainyearmonth.prototype.since
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2019, 12);
+
+const validStrings = [
+ "2019-12[Africa/Abidjan]",
+ "2019-12[!Africa/Abidjan]",
+ "2019-12[u-ca=iso8601]",
+ "2019-12[Africa/Abidjan][u-ca=iso8601]",
+ "2019-12-15T00+00:00",
+ "2019-12-15T00+00:00[UTC]",
+ "2019-12-15T00+00:00[!UTC]",
+ "2019-12-15T00-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 PlainYearMonth`
+ );
+}
+
+const invalidStrings = [
+ "2022-09[u-ca=hebrew]",
+ "2022-09Z",
+ "2022-09+01:00",
+ "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 PlainYearMonth`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-invalid.js
new file mode 100644
index 0000000000..8c2f08fa7e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-invalid.js
@@ -0,0 +1,18 @@
+// |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.plainyearmonth.prototype.since
+description: An invalid ISO string is never supported
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(1976, 11);
+
+for (const arg of TemporalHelpers.ISO.plainYearMonthStringsInvalid()) {
+ assert.throws(RangeError, () => instance.since(arg), `"${arg}" is not a valid PlainYearMonth string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..f2ee8748ad
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/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.plainyearmonth.prototype.since
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[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.PlainYearMonth(2000, 5);
+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/PlainYearMonth/prototype/since/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..24999eface
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-multiple-time-zone.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.plainyearmonth.prototype.since
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "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.PlainYearMonth(2000, 5);
+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/PlainYearMonth/prototype/since/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-time-separators.js
new file mode 100644
index 0000000000..266a179849
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-time-separators.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.plainyearmonth.prototype.since
+description: Time separator in string argument can vary
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2019-12-15T15:23", "uppercase T"],
+ ["2019-12-15t15:23", "lowercase T"],
+ ["2019-12-15 15:23", "space between date and time"],
+];
+
+const instance = new Temporal.PlainYearMonth(2019, 12);
+
+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/PlainYearMonth/prototype/since/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..683d085094
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-time-zone-annotation.js
@@ -0,0 +1,35 @@
+// |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.plainyearmonth.prototype.since
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2019-12-15T15:23[Asia/Kolkata]", "named, with no offset"],
+ ["2019-12-15T15:23[!Europe/Vienna]", "named, with ! and no offset"],
+ ["2019-12-15T15:23[+00:00]", "numeric, with no offset"],
+ ["2019-12-15T15:23[!-02:30]", "numeric, with ! and no offset"],
+ ["2019-12-15T15:23+00:00[UTC]", "named, with offset"],
+ ["2019-12-15T15:23+00:00[!Africa/Abidjan]", "named, with offset and !"],
+ ["2019-12-15T15:23+00:00[+01:00]", "numeric, with offset"],
+ ["2019-12-15T15:23+00:00[!-08:00]", "numeric, with offset and !"],
+];
+
+const instance = new Temporal.PlainYearMonth(2019, 12);
+
+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/PlainYearMonth/prototype/since/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..cdc91aed7d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-unknown-annotation.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.plainyearmonth.prototype.since
+description: Various forms of unknown annotation
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2019-12-15T15:23[foo=bar]", "alone"],
+ ["2019-12-15T15:23[UTC][foo=bar]", "with time zone"],
+ ["2019-12-15T15:23[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["2019-12-15T15:23[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["2019-12-15T15:23[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+const instance = new Temporal.PlainYearMonth(2019, 12);
+
+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/PlainYearMonth/prototype/since/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..1ee9e2d49b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-with-utc-designator.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.plainyearmonth.prototype.since
+description: RangeError thrown if a string with UTC designator is used as a PlainYearMonth
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "2019-10-01T09:00:00Z",
+ "2019-10-01T09:00:00Z[UTC]",
+];
+const instance = new Temporal.PlainYearMonth(2000, 5);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.since(arg),
+ "String with UTC designator should not be valid as a PlainYearMonth"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string.js
new file mode 100644
index 0000000000..efc5c95b3f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string.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.plainyearmonth.prototype.since
+description: A string argument is parsed into a PlainYearMonth
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(1976, 11);
+for (const arg of TemporalHelpers.ISO.plainYearMonthStringsValid()) {
+ const result = instance.since(arg);
+ TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, `"${arg}" is a valid PlainYearMonth string`);
+}
+
+const instanceNegativeYear = new Temporal.PlainYearMonth(-9999, 11);
+for (const arg of TemporalHelpers.ISO.plainYearMonthStringsValidNegativeYear()) {
+ const result = instanceNegativeYear.since(arg);
+ TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, `"${arg}" is a valid PlainYearMonth string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-wrong-type.js
new file mode 100644
index 0000000000..b2951d004b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/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.plainyearmonth.prototype.since
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainYearMonth
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2000, 5);
+
+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.PlainYearMonth, "Temporal.PlainYearMonth, object"],
+ [Temporal.PlainYearMonth.prototype, "Temporal.PlainYearMonth.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/PlainYearMonth/prototype/since/arguments-missing-throws.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/arguments-missing-throws.js
new file mode 100644
index 0000000000..4627a2af88
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/arguments-missing-throws.js
@@ -0,0 +1,16 @@
+// |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.plainyearmonth.prototype.since
+description: Calls to PYM.since throw when missing required arguments.
+features: [Temporal]
+---*/
+
+const jun13 = new Temporal.PlainYearMonth(2013, 6);
+
+assert.throws(TypeError, () => jun13.since({ year: 1994 }), 'Throws when missing required month');
+assert.throws(TypeError, () => jun13.since({ month: 11 }), 'Throws when missing required year');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/branding.js
new file mode 100644
index 0000000000..5b9adf908e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/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.plainyearmonth.prototype.since
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const since = Temporal.PlainYearMonth.prototype.since;
+
+assert.sameValue(typeof since, "function");
+
+const args = [new Temporal.PlainYearMonth(2022, 6)];
+
+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.PlainYearMonth, args), "Temporal.PlainYearMonth");
+assert.throws(TypeError, () => since.apply(Temporal.PlainYearMonth.prototype, args), "Temporal.PlainYearMonth.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..2b862b8a7b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/builtin-calendar-no-array-iteration.js
@@ -0,0 +1,23 @@
+// |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.plainyearmonth.prototype.since
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.PlainYearMonth(2023, 5, "iso8601");
+instance.since({ year: 2005, month: 3 });
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..9332585b8c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plainyearmonth.prototype.since
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dateUntilOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dateUntil");
+Object.defineProperty(Temporal.Calendar.prototype, "dateUntil", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("dateUntil should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainYearMonth(2000, 5, "iso8601", 1);
+instance.since(new Temporal.PlainYearMonth(1999, 4));
+
+Object.defineProperty(Temporal.Calendar.prototype, "dateUntil", dateUntilOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/builtin.js
new file mode 100644
index 0000000000..c6d3d8a7c2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/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.plainyearmonth.prototype.since
+description: >
+ Tests that Temporal.PlainYearMonth.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.PlainYearMonth.prototype.since),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainYearMonth.prototype.since),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainYearMonth.prototype.since),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainYearMonth.prototype.since.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-dateadd-called-with-plaindate-instance.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-dateadd-called-with-plaindate-instance.js
new file mode 100644
index 0000000000..9ee3ebe408
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-dateadd-called-with-plaindate-instance.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.plainyearmonth.prototype.since
+description: >
+ relativeTo parameters that are not ZonedDateTime or undefined, are always
+ converted to PlainDate for observable calendar calls
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarDateAddPlainDateInstance();
+const instance = new Temporal.PlainYearMonth(1970, 1, calendar);
+instance.since(new Temporal.PlainYearMonth(2000, 5, calendar), { smallestUnit: "year" });
+assert(calendar.dateAddCallCount > 0, "assertions in calendar.dateAdd() should have been tested");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-datefromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..75f16cfa45
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-datefromfields-called-with-null-prototype-fields.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.plainyearmonth.prototype.since
+description: >
+ Calendar.yearMonthFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.PlainYearMonth(2000, 5);
+const arg = { year: 2000, month: 5, calendar };
+instance.since(arg);
+assert.sameValue(calendar.yearMonthFromFieldsCallCount, 1, "yearMonthFromFields should be called on the property bag's calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-datefromfields-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-datefromfields-called-with-options-undefined.js
new file mode 100644
index 0000000000..af2e53d56a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-datefromfields-called-with-options-undefined.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.plainyearmonth.prototype.since
+description: >
+ Calendar.dateFromFields method is called with undefined as the options value
+ when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
+const instance = new Temporal.PlainYearMonth(2000, 5, calendar);
+instance.since({ year: 2000, month: 6, day: 3, calendar });
+assert.sameValue(calendar.dateFromFieldsCallCount, 2);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-dateuntil-called-with-null-prototype-options.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-dateuntil-called-with-null-prototype-options.js
new file mode 100644
index 0000000000..bfa120aab8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-dateuntil-called-with-null-prototype-options.js
@@ -0,0 +1,20 @@
+// |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.plainyearmonth.prototype.since
+description: >
+ Calendar.dateUntil method is called with a null-prototype object as the
+ options value when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckOptionsPrototypePollution();
+const instance = new Temporal.PlainYearMonth(2000, 5, calendar);
+const argument = new Temporal.PlainYearMonth(2022, 6, calendar);
+instance.since(argument);
+assert.sameValue(calendar.dateUntilCallCount, 1, "dateUntil should have been called on the calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-dateuntil-called-with-singular-largestunit.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-dateuntil-called-with-singular-largestunit.js
new file mode 100644
index 0000000000..5c4df0383d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-dateuntil-called-with-singular-largestunit.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.plainyearmonth.prototype.since
+description: The options object passed to calendar.dateUntil has a largestUnit property with its value in the singular form
+info: |
+ sec-temporal.plainyearmonth.prototype.since steps 21–22:
+ 21. Let _untilOptions_ be ? MergeLargestUnitOption(_options_, _largestUnit_).
+ 22. Let _result_ be ? CalendarDateUntil(_calendar_, _thisDate_, _otherDate_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkCalendarDateUntilLargestUnitSingular(
+ (calendar, largestUnit) => {
+ const earlier = new Temporal.PlainYearMonth(2000, 5, calendar);
+ const later = new Temporal.PlainYearMonth(2001, 6, calendar);
+ later.since(earlier, { largestUnit });
+ },
+ {
+ years: ["year"],
+ months: ["month"]
+ }
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-fields-iterable.js
new file mode 100644
index 0000000000..9404d314da
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-fields-iterable.js
@@ -0,0 +1,43 @@
+// |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.plainyearmonth.prototype.since
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plainyearmonth.prototype.since step 3:
+ 3. Set _other_ to ? ToTemporalYearMonth(_other_).
+ sec-temporal.plainyearmonth.prototype.since step 14:
+ 14. Let fieldNames be ? CalendarFields(_calendar_, « *"monthCode"*, *"year"* »).
+ sec-temporal-totemporalyearmonth step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected1 = [
+ "monthCode",
+ "year",
+];
+const expected2 = [
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const yearmonth = new Temporal.PlainYearMonth(2000, 5, calendar1);
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+yearmonth.since({ year: 2005, month: 6, calendar: calendar2 });
+
+assert.sameValue(calendar1.fieldsCallCount, 1, "fields() method not called");
+assert.compareArray(calendar1.fieldsCalledWith[0], expected1, "fields() method called with correct args");
+assert(calendar1.iteratorExhausted[0], "iterated through the whole iterable");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected2, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-fromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-fromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..db22d52738
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-fromfields-called-with-null-prototype-fields.js
@@ -0,0 +1,18 @@
+// |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.plainyearmonth.prototype.since
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.PlainYearMonth(2000, 5, calendar);
+instance.since(new Temporal.PlainYearMonth(2019, 2));
+assert.sameValue(calendar.dateFromFieldsCallCount, 2, "dateFromFields should have been called on the calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-temporal-object.js
new file mode 100644
index 0000000000..4bcf60f6ca
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-temporal-object.js
@@ -0,0 +1,29 @@
+// |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.plainyearmonth.prototype.since
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plainyearmonth.prototype.since step 3:
+ 3. Set _other_ to ? ToTemporalYearMonth(_other_).
+ sec-temporal-totemporalyearmonth step 2.b:
+ b. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const yearmonth = new Temporal.PlainYearMonth(2000, 5, temporalObject);
+ yearmonth.since({ year: 2005, month: 6, calendar: temporalObject });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-yearmonthfromfields-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-yearmonthfromfields-called-with-options-undefined.js
new file mode 100644
index 0000000000..e1e6c19be5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-yearmonthfromfields-called-with-options-undefined.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.plainyearmonth.prototype.since
+description: >
+ Calendar.yearMonthFromFields method is called with undefined as the options
+ value when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+let calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
+let instance = new Temporal.PlainYearMonth(2000, 5, calendar);
+instance.since({ year: 2000, month: 6, calendar });
+assert.sameValue(calendar.yearMonthFromFieldsCallCount, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..7bb6258ee7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/constructor-in-calendar-fields.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.plainyearmonth.prototype.since
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.PlainYearMonth(2000, 5, calendar);
+
+assert.throws(RangeError, () => instance.since(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/duplicate-calendar-fields.js
new file mode 100644
index 0000000000..54cb1ae302
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/duplicate-calendar-fields.js
@@ -0,0 +1,19 @@
+// |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.plainyearmonth.prototype.since
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['month'], ['monthCode'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar };
+ const instance = new Temporal.PlainYearMonth(2000, 5, calendar);
+
+ assert.throws(RangeError, () => instance.since(arg));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..e8aa44d442
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/infinity-throws-rangeerror.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.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plainyearmonth.prototype.since
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2000, 5);
+const base = { year: 2000, month: 5 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.since({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.since({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-auto.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-auto.js
new file mode 100644
index 0000000000..dbcc180f93
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-auto.js
@@ -0,0 +1,20 @@
+// |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.plainyearmonth.prototype.since
+description: auto value for largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+
+TemporalHelpers.assertDuration(later.since(earlier, { largestUnit: "auto" }),
+ 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, "auto largestUnit is year (pos)");
+TemporalHelpers.assertDuration(earlier.since(later, { largestUnit: "auto" }),
+ -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, "auto largestUnit is year (neg)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-disallowed-units.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-disallowed-units.js
new file mode 100644
index 0000000000..72b038c0ae
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-disallowed-units.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.plainyearmonth.prototype.since
+description: Since throws on to0-small largestUnit
+features: [Temporal, arrow-function]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2019, 1);
+const later = new Temporal.PlainYearMonth(2021, 9);
+
+[
+ 'weeks',
+ 'days',
+ 'hours',
+ 'minutes',
+ 'seconds',
+ 'milliseconds',
+ 'microseconds',
+ 'nanoseconds'
+].forEach((largestUnit) => {
+ assert.throws(RangeError, () => later.since(earlier, { largestUnit }),`throws on disallowed or invalid largestUnit: ${largestUnit}`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-invalid-string.js
new file mode 100644
index 0000000000..29cf4602f0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-invalid-string.js
@@ -0,0 +1,45 @@
+// |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.plainyearmonth.prototype.since
+description: RangeError thrown when largestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+const badValues = [
+ "era",
+ "eraYear",
+ "week",
+ "day",
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+ "month\0",
+ "YEAR",
+ "eras",
+ "eraYears",
+ "weeks",
+ "days",
+ "hours",
+ "minutes",
+ "seconds",
+ "milliseconds",
+ "microseconds",
+ "nanoseconds",
+ "months\0",
+ "YEARS",
+ "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/PlainYearMonth/prototype/since/largestunit-months.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-months.js
new file mode 100644
index 0000000000..61ce3f3567
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-months.js
@@ -0,0 +1,20 @@
+// |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.plainyearmonth.prototype.since
+description: months value for largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+
+TemporalHelpers.assertDuration(later.since(earlier, { largestUnit: "months" }),
+ 0, 13, 0, 0, 0, 0, 0, 0, 0, 0, "largestUnit is months (pos)");
+TemporalHelpers.assertDuration(earlier.since(later, { largestUnit: "months" }),
+ 0, -13, 0, 0, 0, 0, 0, 0, 0, 0, "largestUnit is months (neg)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-plurals-accepted.js
new file mode 100644
index 0000000000..e47e7c6112
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-plurals-accepted.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.plainyearmonth.prototype.since
+description: Plural units are accepted as well for the largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+const validUnits = [
+ "year",
+ "month",
+];
+TemporalHelpers.checkPluralUnitsAccepted((largestUnit) => later.since(earlier, { largestUnit }), validUnits);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-smallestunit-mismatch.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-smallestunit-mismatch.js
new file mode 100644
index 0000000000..6b212d9377
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/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.plainyearmonth.prototype.since
+description: RangeError thrown when smallestUnit is larger than largestUnit
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+const units = ["years", "months"];
+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/PlainYearMonth/prototype/since/largestunit-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-undefined.js
new file mode 100644
index 0000000000..8abe071470
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-undefined.js
@@ -0,0 +1,30 @@
+// |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.plainyearmonth.prototype.since
+description: Fallback value for largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+
+TemporalHelpers.assertDuration(later.since(earlier, { largestUnit: undefined }),
+ 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, "default largestUnit is year (explicit, pos)");
+TemporalHelpers.assertDuration(earlier.since(later, { largestUnit: undefined }),
+ -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, "default largestUnit is year (explicit, neg)");
+
+TemporalHelpers.assertDuration(later.since(earlier, {}),
+ 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, "default largestUnit is year (implicit, pos)");
+TemporalHelpers.assertDuration(earlier.since(later, {}),
+ -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, "default largestUnit is year (implicit, neg)");
+
+TemporalHelpers.assertDuration(later.since(earlier, () => {}),
+ 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, "default largestUnit is year (arrow function, pos)");
+TemporalHelpers.assertDuration(earlier.since(later, () => {}),
+ -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, "default largestUnit is year (arrow function, neg)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-wrong-type.js
new file mode 100644
index 0000000000..17711d11e9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/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.plainyearmonth.prototype.since
+description: Type conversions for largestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+TemporalHelpers.checkStringOptionWrongType("largestUnit", "month",
+ (largestUnit) => later.since(earlier, { largestUnit }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 13, 0, 0, 0, 0, 0, 0, 0, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-years.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-years.js
new file mode 100644
index 0000000000..b5d8cea58a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-years.js
@@ -0,0 +1,20 @@
+// |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.plainyearmonth.prototype.since
+description: years value for largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+
+TemporalHelpers.assertDuration(later.since(earlier, { largestUnit: "years" }),
+ 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, "largestUnit is years (pos)");
+TemporalHelpers.assertDuration(earlier.since(later, { largestUnit: "years" }),
+ -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, "largestUnit is years (neg)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/leap-second.js
new file mode 100644
index 0000000000..1ad065f65a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/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.plainyearmonth.prototype.since
+description: Leap second is a valid ISO string for PlainYearMonth
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2016, 12);
+
+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 PlainYearMonth"
+);
+
+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 PlainYearMonth"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/length.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/length.js
new file mode 100644
index 0000000000..2883b71ed8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/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.plainyearmonth.prototype.since
+description: Temporal.PlainYearMonth.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.PlainYearMonth.prototype.since, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/mixed-calendar-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/mixed-calendar-invalid.js
new file mode 100644
index 0000000000..9db026f10b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/mixed-calendar-invalid.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.plainyearmonth.prototype.since
+description: Mixed calendars throw as invalid
+features: [Temporal]
+---*/
+
+class customCal extends Temporal.Calendar {
+ constructor () {
+ super('iso8601');
+ }
+
+ get id() {
+ return "I am a secret cal.";
+ }
+}
+
+const ym1 = new Temporal.PlainYearMonth(2000, 1);
+const ym2 = new Temporal.PlainYearMonth(2000, 1, new customCal());
+
+assert.throws(RangeError, () => ym1.since(ym2), 'since throws with different calendars');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/name.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/name.js
new file mode 100644
index 0000000000..29a276f95f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/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.plainyearmonth.prototype.since
+description: Temporal.PlainYearMonth.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.PlainYearMonth.prototype.since, "name", {
+ value: "since",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/not-a-constructor.js
new file mode 100644
index 0000000000..69770b793b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/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.plainyearmonth.prototype.since
+description: >
+ Temporal.PlainYearMonth.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.PlainYearMonth.prototype.since();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainYearMonth.prototype.since), false,
+ "isConstructor(Temporal.PlainYearMonth.prototype.since)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/options-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/options-invalid.js
new file mode 100644
index 0000000000..822d4570a1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/options-invalid.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.plainyearmonth.prototype.since
+description: Verify that invalid options are handled correctly.
+features: [Temporal]
+---*/
+
+const feb20 = new Temporal.PlainYearMonth(2020, 2);
+const feb21 = new Temporal.PlainYearMonth(2021, 2);
+
+[
+ null,
+ 1,
+ 'hello',
+ true,
+ Symbol('foo'),
+ 1n
+].forEach((badOption) =>
+ assert.throws(TypeError, () => feb21.since(feb20, badOption), `${String(badOption)} throws TypeError`)
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/options-object.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/options-object.js
new file mode 100644
index 0000000000..dacf123949
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/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.plainyearmonth.prototype.since
+description: Empty or a function object may be used as options
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2019, 10);
+
+const result1 = instance.since(new Temporal.PlainYearMonth(1976, 11), {});
+TemporalHelpers.assertDuration(
+ result1, 42, 11, 0, 0, 0, 0, 0, 0, 0, 0,
+ "options may be an empty plain object"
+);
+
+const result2 = instance.since(new Temporal.PlainYearMonth(1976, 11), () => {});
+TemporalHelpers.assertDuration(
+ result2, 42, 11, 0, 0, 0, 0, 0, 0, 0, 0,
+ "options may be a function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/options-undefined.js
new file mode 100644
index 0000000000..29fc58b30a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/options-undefined.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.plainyearmonth.prototype.since
+description: Verify that undefined options are handled correctly.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2002, 12);
+
+TemporalHelpers.assertDuration(later.since(earlier, undefined),
+ 2, 7, 0, 0, 0, 0, 0, 0, 0, 0, "default largestUnit is year (explicit, pos)");
+TemporalHelpers.assertDuration(earlier.since(later, undefined),
+ -2, -7, 0, 0, 0, 0, 0, 0, 0, 0, "default largestUnit is year (explicit, neg)");
+
+TemporalHelpers.assertDuration(later.since(earlier),
+ 2, 7, 0, 0, 0, 0, 0, 0, 0, 0, "default largestUnit is year (implicit, pos)");
+TemporalHelpers.assertDuration(earlier.since(later),
+ -2, -7, 0, 0, 0, 0, 0, 0, 0, 0, "default largestUnit is year (implicit, neg)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/options-wrong-type.js
new file mode 100644
index 0000000000..5a166db02a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/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.plainyearmonth.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.PlainYearMonth(2019, 10);
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.since(new Temporal.PlainYearMonth(1976, 11), value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/order-of-operations.js
new file mode 100644
index 0000000000..d5ca77eff3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/order-of-operations.js
@@ -0,0 +1,192 @@
+// |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.plainyearmonth.prototype.since
+description: Properties on objects passed to since() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expectedMinimal = [
+ // ToTemporalYearMonth
+ "get other.calendar",
+ "has other.calendar.dateAdd",
+ "has other.calendar.dateFromFields",
+ "has other.calendar.dateUntil",
+ "has other.calendar.day",
+ "has other.calendar.dayOfWeek",
+ "has other.calendar.dayOfYear",
+ "has other.calendar.daysInMonth",
+ "has other.calendar.daysInWeek",
+ "has other.calendar.daysInYear",
+ "has other.calendar.fields",
+ "has other.calendar.id",
+ "has other.calendar.inLeapYear",
+ "has other.calendar.mergeFields",
+ "has other.calendar.month",
+ "has other.calendar.monthCode",
+ "has other.calendar.monthDayFromFields",
+ "has other.calendar.monthsInYear",
+ "has other.calendar.weekOfYear",
+ "has other.calendar.year",
+ "has other.calendar.yearMonthFromFields",
+ "has other.calendar.yearOfWeek",
+ "get other.calendar.fields",
+ "get other.calendar.yearMonthFromFields",
+ "call other.calendar.fields",
+ "get other.month",
+ "get other.month.valueOf",
+ "call other.month.valueOf",
+ "get other.monthCode",
+ "get other.monthCode.toString",
+ "call other.monthCode.toString",
+ "get other.year",
+ "get other.year.valueOf",
+ "call other.year.valueOf",
+ "call other.calendar.yearMonthFromFields",
+ // CalendarEquals
+ "get this.calendar.id",
+ "get other.calendar.id",
+ // 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 expected = expectedMinimal.concat([
+ // lookup
+ "get this.calendar.dateAdd",
+ "get this.calendar.dateFromFields",
+ "get this.calendar.dateUntil",
+ "get this.calendar.fields",
+ // CalendarFields
+ "call this.calendar.fields",
+ // PrepareTemporalFields / CalendarDateFromFields (receiver)
+ "get this.calendar.monthCode",
+ "call this.calendar.monthCode",
+ "get this.calendar.year",
+ "call this.calendar.year",
+ "call this.calendar.dateFromFields",
+ // PrepareTemporalFields / CalendarDateFromFields (argument)
+ "get other.calendar.monthCode",
+ "call other.calendar.monthCode",
+ "get other.calendar.year",
+ "call other.calendar.year",
+ "call this.calendar.dateFromFields",
+ // CalendarDateUntil
+ "call this.calendar.dateUntil",
+]);
+const actual = [];
+
+const ownCalendar = TemporalHelpers.calendarObserver(actual, "this.calendar");
+const instance = new Temporal.PlainYearMonth(2000, 5, ownCalendar, 1);
+
+const otherYearMonthPropertyBag = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2001,
+ month: 6,
+ monthCode: "M06",
+ calendar: TemporalHelpers.calendarObserver(actual, "other.calendar"),
+}, "other");
+
+function createOptionsObserver({ smallestUnit = "months", largestUnit = "auto", roundingMode = "halfExpand", roundingIncrement = 1 } = {}) {
+ return TemporalHelpers.propertyBagObserver(actual, {
+ // order is significant, due to iterating through properties in order to
+ // copy them to an internal null-prototype object:
+ roundingIncrement,
+ roundingMode,
+ largestUnit,
+ smallestUnit,
+ additional: "property",
+ }, "options");
+}
+
+// clear any observable things that happened while constructing the objects
+actual.splice(0);
+
+// code path that skips RoundDuration:
+instance.since(otherYearMonthPropertyBag, createOptionsObserver({ smallestUnit: "months", roundingIncrement: 1 }));
+assert.compareArray(actual, expected, "order of operations with no rounding");
+actual.splice(0); // clear
+
+// short-circuit for identical objects:
+const identicalPropertyBag = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2000,
+ month: 5,
+ monthCode: "M05",
+ calendar: TemporalHelpers.calendarObserver(actual, "other.calendar"),
+}, "other");
+
+instance.since(identicalPropertyBag, createOptionsObserver());
+assert.compareArray(actual, expectedMinimal, "order of operations with identical year-months");
+actual.splice(0); // clear
+
+// code path through RoundDuration that rounds to the nearest year:
+const expectedOpsForYearRounding = expected.concat([
+ // RoundDuration
+ "call this.calendar.dateAdd", // 7.e
+ "call this.calendar.dateAdd", // 7.g
+ "call this.calendar.dateUntil", // 7.o
+ "call this.calendar.dateAdd", // 7.y MoveRelativeDate
+ // (7.s not called because other units can't add up to >1 year at this point)
+ // BalanceDateDurationRelative
+ "call this.calendar.dateAdd", // 9.c
+ "call this.calendar.dateUntil" // 9.d
+]);
+instance.since(otherYearMonthPropertyBag, createOptionsObserver({ smallestUnit: "years" }));
+assert.compareArray(actual, expectedOpsForYearRounding, "order of operations with smallestUnit = years");
+actual.splice(0); // clear
+
+// code path through RoundDuration that rounds to the nearest year and skips a DateUntil call
+const otherYearMonthPropertyBagSameMonth = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2001,
+ month: 5,
+ monthCode: "M05",
+ calendar: TemporalHelpers.calendarObserver(actual, "other.calendar"),
+}, "other");
+const expectedOpsForYearRoundingSameMonth = expected.concat([
+ "call this.calendar.dateAdd", // 7.e
+ "call this.calendar.dateAdd", // 7.g
+ "call this.calendar.dateAdd", // 7.y MoveRelativeDate
+ // (7.o not called because months and weeks == 0)
+ // BalanceDateDurationRelative
+ "call this.calendar.dateAdd", // 9.c
+ "call this.calendar.dateUntil" // 9.d
+]);
+instance.since(otherYearMonthPropertyBagSameMonth, createOptionsObserver({ smallestUnit: "years" }));
+assert.compareArray(actual, expectedOpsForYearRoundingSameMonth, "order of operations with smallestUnit = years and no excess months");
+actual.splice(0); // clear
+
+// code path through RoundDuration that rounds to the nearest month:
+const expectedOpsForMonthRounding = expected.concat([
+ // RoundDuration
+ "call this.calendar.dateAdd", // 10.c
+ "call this.calendar.dateAdd", // 10.e
+ "call this.calendar.dateAdd", // 10.k MoveRelativeDate
+ // (10.n.iii MoveRelativeDate not called because weeks == 0)
+ // BalanceDateDurationRelative
+ "call this.calendar.dateAdd", // 10.d
+ "call this.calendar.dateUntil" // 10.e
+]);
+instance.since(otherYearMonthPropertyBag, createOptionsObserver({ smallestUnit: "months", roundingIncrement: 2 }));
+assert.compareArray(actual, expectedOpsForMonthRounding, "order of operations with smallestUnit = months");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/prop-desc.js
new file mode 100644
index 0000000000..252d36870d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/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.plainyearmonth.prototype.since
+description: The "since" property of Temporal.PlainYearMonth.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainYearMonth.prototype.since,
+ "function",
+ "`typeof PlainYearMonth.prototype.since` is `function`"
+);
+
+verifyProperty(Temporal.PlainYearMonth.prototype, "since", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/proto-in-calendar-fields.js
new file mode 100644
index 0000000000..9a2d96724b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/proto-in-calendar-fields.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.plainyearmonth.prototype.since
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.PlainYearMonth(2000, 5, calendar);
+
+assert.throws(RangeError, () => instance.since(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/round-cross-unit-boundary.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/round-cross-unit-boundary.js
new file mode 100644
index 0000000000..89a4645edd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/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.plainyearmonth.prototype.since
+description: Rounding can cross unit boundaries up to largestUnit
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2022, 1);
+const later = new Temporal.PlainYearMonth(2023, 12);
+const duration = earlier.since(later, { largestUnit: "years", smallestUnit: "months", roundingIncrement: 3, roundingMode: "expand" });
+TemporalHelpers.assertDuration(duration, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, "-1 year -12 months balances to -2 years");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/rounding-zero-year-month-length.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/rounding-zero-year-month-length.js
new file mode 100644
index 0000000000..c91f5bab6d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/rounding-zero-year-month-length.js
@@ -0,0 +1,33 @@
+// |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.plainyearmonth.prototype.since
+description: >
+ A malicious calendar resulting in a year, month, or week length of zero is
+ handled correctly
+info: |
+ RoundDuration
+ 10.z. If _oneYearDays_ = 0, throw a *RangeError* exception.
+ ...
+ 11.z. If _oneMonthDays_ = 0, throw a *RangeError* exception.
+ ...
+ 12.s. If _oneWeekDays_ = 0, throw a *RangeError* exception.
+features: [Temporal]
+---*/
+
+const cal = new class extends Temporal.Calendar {
+ dateAdd(date, duration, options) {
+ // Called several times, last call sets oneYear/Month/WeekDays to 0
+ return new Temporal.PlainDate(1970, 1, 1);
+ }
+}("iso8601");
+
+const ym1 = new Temporal.PlainYearMonth(1970, 1, cal);
+const ym2 = new Temporal.PlainYearMonth(1971, 1, cal);
+
+assert.throws(RangeError, () => ym1.since(ym2, { smallestUnit: "years" }), "zero year length handled correctly");
+assert.throws(RangeError, () => ym1.since(ym2, { smallestUnit: "months", roundingIncrement: 2 }), "zero month length handled correctly");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingincrement-as-expected.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingincrement-as-expected.js
new file mode 100644
index 0000000000..4baad1a8f8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingincrement-as-expected.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.plainyearmonth.prototype.since
+description: Since rounding increments work as expected
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2019, 1);
+const later = new Temporal.PlainYearMonth(2021, 9);
+
+const laterSinceYear = later.since(earlier, { smallestUnit: "years", roundingIncrement: 4, roundingMode: "halfExpand" });
+TemporalHelpers.assertDuration(laterSinceYear,
+ /* years = */ 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, "rounds to an increment of years");
+
+const laterSinceMixed = later.since(earlier, { smallestUnit: "months", roundingIncrement: 5 });
+TemporalHelpers.assertDuration(laterSinceMixed,
+ /* years = */ 2, /* months = */ 5, 0, 0, 0, 0, 0, 0, 0, 0, "rounds to an increment of months mixed with years");
+
+const laterSinceMonth = later.since(earlier, { largestUnit: "months", smallestUnit: "months", roundingIncrement: 10 });
+TemporalHelpers.assertDuration(laterSinceMonth,
+ 0, /* months = */ 30, 0, 0, 0, 0, 0, 0, 0, 0, "rounds to an increment of pure months");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingincrement-nan.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingincrement-nan.js
new file mode 100644
index 0000000000..b6fd705a95
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/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.plainyearmonth.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.plainyearmonth.prototype.since step 13:
+ 13. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, *undefined*, *false*).
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: NaN }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingincrement-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingincrement-non-integer.js
new file mode 100644
index 0000000000..b0a7983f1a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingincrement-non-integer.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.plainyearmonth.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.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2000, 10);
+const result = later.since(earlier, { roundingIncrement: 2.5, roundingMode: "trunc" });
+TemporalHelpers.assertDuration(result, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, "roundingIncrement 2.5 truncates to 2");
+// Cannot test the upper bound of 1e9 + 0.5 here, because the duration is
+// rounded relative to the receiver PlainYearMonth, and 1e9 months is outside of
+// the PlainYearMonth range.
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingincrement-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingincrement-out-of-range.js
new file mode 100644
index 0000000000..2a75b13429
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/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.plainyearmonth.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.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2000, 10);
+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/PlainYearMonth/prototype/since/roundingincrement-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingincrement-undefined.js
new file mode 100644
index 0000000000..b0aee1cfb8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/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.plainyearmonth.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.plainyearmonth.prototype.since step 13:
+ 13. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, *undefined*, *false*).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+
+const explicit = later.since(earlier, { roundingIncrement: undefined });
+TemporalHelpers.assertDuration(explicit, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, "default roundingIncrement is 1");
+
+const implicit = later.since(earlier, {});
+TemporalHelpers.assertDuration(implicit, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, "default roundingIncrement is 1");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingincrement-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingincrement-wrong-type.js
new file mode 100644
index 0000000000..5760d55317
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/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.plainyearmonth.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.plainyearmonth.prototype.since step 13:
+ 13. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, *undefined*, *false*).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+
+TemporalHelpers.checkRoundingIncrementOptionWrongType(
+ (roundingIncrement) => later.since(earlier, { roundingIncrement }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, descr),
+ (result, descr) => TemporalHelpers.assertDuration(result, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-ceil.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-ceil.js
new file mode 100644
index 0000000000..1a0df86da5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-ceil.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.plainyearmonth.prototype.since
+description: Tests calculations with roundingMode "ceil".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2019, 1);
+const later = new Temporal.PlainYearMonth(2021, 9);
+
+const expected = [
+ ["years", [3], [-2]],
+ ["months", [2, 8], [-2, -8]],
+];
+
+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/PlainYearMonth/prototype/since/roundingmode-expand.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-expand.js
new file mode 100644
index 0000000000..dda697ab6d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-expand.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.plainyearmonth.prototype.since
+description: Tests calculations with roundingMode "expand".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2019, 1);
+const later = new Temporal.PlainYearMonth(2021, 9);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [2, 8], [-2, -8]],
+];
+
+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/PlainYearMonth/prototype/since/roundingmode-floor.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-floor.js
new file mode 100644
index 0000000000..43295a8cb0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-floor.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.plainyearmonth.prototype.since
+description: Tests calculations with roundingMode "floor".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2019, 1);
+const later = new Temporal.PlainYearMonth(2021, 9);
+
+const expected = [
+ ["years", [2], [-3]],
+ ["months", [2, 8], [-2, -8]],
+];
+
+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/PlainYearMonth/prototype/since/roundingmode-halfCeil.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-halfCeil.js
new file mode 100644
index 0000000000..c1c743a1ac
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-halfCeil.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.plainyearmonth.prototype.since
+description: Tests calculations with roundingMode "halfCeil".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2019, 1);
+const later = new Temporal.PlainYearMonth(2021, 9);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [2, 8], [-2, -8]],
+];
+
+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/PlainYearMonth/prototype/since/roundingmode-halfEven.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-halfEven.js
new file mode 100644
index 0000000000..1d264b755a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-halfEven.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.plainyearmonth.prototype.since
+description: Tests calculations with roundingMode "halfEven".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2019, 1);
+const later = new Temporal.PlainYearMonth(2021, 9);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [2, 8], [-2, -8]],
+];
+
+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/PlainYearMonth/prototype/since/roundingmode-halfExpand.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-halfExpand.js
new file mode 100644
index 0000000000..e68173a741
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-halfExpand.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.plainyearmonth.prototype.since
+description: Tests calculations with roundingMode "halfExpand".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2019, 1);
+const later = new Temporal.PlainYearMonth(2021, 9);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [2, 8], [-2, -8]],
+];
+
+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/PlainYearMonth/prototype/since/roundingmode-halfFloor.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-halfFloor.js
new file mode 100644
index 0000000000..cdd03d23af
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-halfFloor.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.plainyearmonth.prototype.since
+description: Tests calculations with roundingMode "halfFloor".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2019, 1);
+const later = new Temporal.PlainYearMonth(2021, 9);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [2, 8], [-2, -8]],
+];
+
+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/PlainYearMonth/prototype/since/roundingmode-halfTrunc.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-halfTrunc.js
new file mode 100644
index 0000000000..55b3f2a109
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-halfTrunc.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.plainyearmonth.prototype.since
+description: Tests calculations with roundingMode "halfTrunc".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2019, 1);
+const later = new Temporal.PlainYearMonth(2021, 9);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [2, 8], [-2, -8]],
+];
+
+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/PlainYearMonth/prototype/since/roundingmode-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-invalid-string.js
new file mode 100644
index 0000000000..f4b95b6588
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/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.plainyearmonth.prototype.since
+description: RangeError thrown when roundingMode option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+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/PlainYearMonth/prototype/since/roundingmode-trunc.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-trunc.js
new file mode 100644
index 0000000000..a8b5763120
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-trunc.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.plainyearmonth.prototype.since
+description: Tests calculations with roundingMode "trunc".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2019, 1);
+const later = new Temporal.PlainYearMonth(2021, 9);
+
+const expected = [
+ ["years", [2], [-2]],
+ ["months", [2, 8], [-2, -8]],
+];
+
+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/PlainYearMonth/prototype/since/roundingmode-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-undefined.js
new file mode 100644
index 0000000000..b87ea94053
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-undefined.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.plainyearmonth.prototype.since
+description: Fallback value for roundingMode option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 1);
+
+const later1 = new Temporal.PlainYearMonth(2005, 2);
+const explicit1 = later1.since(earlier, { smallestUnit: "year", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit1, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, "default roundingMode is trunc");
+const implicit1 = later1.since(earlier, { smallestUnit: "year" });
+TemporalHelpers.assertDuration(implicit1, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, "default roundingMode is trunc");
+
+const later2 = new Temporal.PlainYearMonth(2005, 12);
+const explicit2 = later2.since(earlier, { smallestUnit: "year", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit2, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, "default roundingMode is trunc");
+const implicit2 = later2.since(earlier, { smallestUnit: "year" });
+TemporalHelpers.assertDuration(implicit2, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, "default roundingMode is trunc");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-wrong-type.js
new file mode 100644
index 0000000000..d31c4709c2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/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.plainyearmonth.prototype.since
+description: Type conversions for roundingMode option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+TemporalHelpers.checkStringOptionWrongType("roundingMode", "trunc",
+ (roundingMode) => later.since(earlier, { smallestUnit: "year", roundingMode }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/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/PlainYearMonth/prototype/since/smallestunit-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/smallestunit-invalid-string.js
new file mode 100644
index 0000000000..ff7ea1df01
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/smallestunit-invalid-string.js
@@ -0,0 +1,45 @@
+// |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.plainyearmonth.prototype.since
+description: RangeError thrown when smallestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+const badValues = [
+ "era",
+ "eraYear",
+ "week",
+ "day",
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+ "month\0",
+ "YEAR",
+ "eras",
+ "eraYears",
+ "weeks",
+ "days",
+ "hours",
+ "minutes",
+ "seconds",
+ "milliseconds",
+ "microseconds",
+ "nanoseconds",
+ "months\0",
+ "YEARS",
+ "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/PlainYearMonth/prototype/since/smallestunit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/smallestunit-plurals-accepted.js
new file mode 100644
index 0000000000..147d14b4cf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/smallestunit-plurals-accepted.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.plainyearmonth.prototype.since
+description: Plural units are accepted as well for the smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+const validUnits = [
+ "year",
+ "month",
+];
+TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => later.since(earlier, { smallestUnit }), validUnits);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/smallestunit-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/smallestunit-undefined.js
new file mode 100644
index 0000000000..a041e0b4d3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/smallestunit-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.plainyearmonth.prototype.since
+description: Fallback value for smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+
+const explicit = later.since(earlier, { smallestUnit: undefined });
+TemporalHelpers.assertDuration(explicit, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, "default smallestUnit is month");
+const implicit = later.since(earlier, {});
+TemporalHelpers.assertDuration(implicit, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, "default smallestUnit is month");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/smallestunit-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/smallestunit-wrong-type.js
new file mode 100644
index 0000000000..d25b44a89c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/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.plainyearmonth.prototype.since
+description: Type conversions for smallestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+TemporalHelpers.checkStringOptionWrongType("smallestUnit", "year",
+ (smallestUnit) => later.since(earlier, { smallestUnit }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/symmetry.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/symmetry.js
new file mode 100644
index 0000000000..f5d8a8396a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/symmetry.js
@@ -0,0 +1,18 @@
+// |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.plainyearmonth.prototype.since
+description: Since observes symmetry with until
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const nov94 = new Temporal.PlainYearMonth(1994, 11);
+const jun13 = new Temporal.PlainYearMonth(2013, 6);
+const diff = jun13.since(nov94);
+
+TemporalHelpers.assertDurationsEqual(diff, nov94.until(jun13), 'Since is inverse of until');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/year-zero.js
new file mode 100644
index 0000000000..5558dc3927
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/year-zero.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.plainyearmonth.prototype.since
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-06",
+ "-000000-06-24",
+ "-000000-06-24T15:43:27",
+ "-000000-06-24T15:43:27+01:00",
+ "-000000-06-24T15:43:27+00:00[UTC]",
+];
+const instance = new Temporal.PlainYearMonth(2000, 5);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.since(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-duration-max.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-duration-max.js
new file mode 100644
index 0000000000..2396a974c4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-duration-max.js
@@ -0,0 +1,58 @@
+// |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.plainyearmonth.prototype.subtract
+description: Maximum allowed duration
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(1970, 1);
+
+const maxCases = [
+ ["P273790Y8M42DT23H59M59.999999999S", "string with max years"],
+ [{ years: 273790, months: 8, days: 42, nanoseconds: 86399999999999 }, "property bag with max years"],
+ ["P3285488M42DT23H59M59.999999999S", "string with max months"],
+ [{ months: 3285488, days: 42, nanoseconds: 86399999999999 }, "property bag with max months"],
+ ["P14285718W5DT23H59M59.999999999S", "string with max weeks"],
+ [{ weeks: 14285718, days: 5, nanoseconds: 86399999999999 }, "property bag with max weeks"],
+ ["P100000031DT23H59M59.999999999S", "string with max days"],
+ [{ days: 100000031, nanoseconds: 86399999999999 }, "property bag with max days"],
+ ["PT2400000767H59M59.999999999S", "string with max hours"],
+ [{ hours: 2400000767, nanoseconds: 3599999999999 }, "property bag with max hours"],
+ ["PT144000046079M59.999999999S", "string with max minutes"],
+ [{ minutes: 144000046079, nanoseconds: 59999999999 }, "property bag with max minutes"],
+ ["PT8640002764799.999999999S", "string with max seconds"],
+ [{ seconds: 8640002764799, nanoseconds: 999999999 }, "property bag with max seconds"],
+];
+
+for (const [arg, descr] of maxCases) {
+ const result = instance.subtract(arg);
+ TemporalHelpers.assertPlainYearMonth(result, -271821, 4, "M04", `operation succeeds with ${descr}`);
+}
+
+const minCases = [
+ ["-P273790Y8M12DT23H59M59.999999999S", "string with min years"],
+ [{ years: -273790, months: -8, days: -12, nanoseconds: -86399999999999 }, "property bag with min years"],
+ ["-P3285488M12DT23H59M59.999999999S", "string with min months"],
+ [{ months: -3285488, days: -12, nanoseconds: -86399999999999 }, "property bag with min months"],
+ ["-P14285714W2DT23H59M59.999999999S", "string with min weeks"],
+ [{ weeks: -14285714, days: -2, nanoseconds: -86399999999999 }, "property bag with min weeks"],
+ ["-P100000000DT23H59M59.999999999S", "string with min days"],
+ [{ days: -100000000, nanoseconds: -86399999999999 }, "property bag with min days"],
+ ["-PT2400000023H59M59.999999999S", "string with min hours"],
+ [{ hours: -2400000023, nanoseconds: -3599999999999 }, "property bag with min hours"],
+ ["-PT144000001439M59.999999999S", "string with min minutes"],
+ [{ minutes: -144000001439, nanoseconds: -59999999999 }, "property bag with min minutes"],
+ ["-PT8640000086399.999999999S", "string with min seconds"],
+ [{ seconds: -8640000086399, nanoseconds: -999999999 }, "property bag with min seconds"],
+];
+
+for (const [arg, descr] of minCases) {
+ const result = instance.subtract(arg);
+ TemporalHelpers.assertPlainYearMonth(result, 275760, 9, "M09", `operation succeeds with ${descr}`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-duration-object.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-duration-object.js
new file mode 100644
index 0000000000..3d72383b35
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-duration-object.js
@@ -0,0 +1,16 @@
+// |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.plainyearmonth.prototype.subtract
+description: A Duration object is supported as the argument
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const jun13 = Temporal.PlainYearMonth.from("2013-06");
+const diff = Temporal.Duration.from("P18Y7M");
+TemporalHelpers.assertPlainYearMonth(jun13.subtract(diff), 1994, 11, "M11");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-duration-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-duration-out-of-range.js
new file mode 100644
index 0000000000..d0977de87a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-duration-out-of-range.js
@@ -0,0 +1,75 @@
+// |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.plainyearmonth.prototype.subtract
+description: Duration-like argument that is out of range
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(1970, 1);
+
+const cases = [
+ // 2^32 = 4294967296
+ ["P4294967296Y", "string with years > max"],
+ [{ years: 4294967296 }, "property bag with years > max"],
+ ["-P4294967296Y", "string with years < min"],
+ [{ years: -4294967296 }, "property bag with years < min"],
+ ["P4294967296M", "string with months > max"],
+ [{ months: 4294967296 }, "property bag with months > max"],
+ ["-P4294967296M", "string with months < min"],
+ [{ months: -4294967296 }, "property bag with months < min"],
+ ["P4294967296W", "string with weeks > max"],
+ [{ weeks: 4294967296 }, "property bag with weeks > max"],
+ ["-P4294967296W", "string with weeks < min"],
+ [{ weeks: -4294967296 }, "property bag with weeks < min"],
+
+ // ceil(max safe integer / 86400) = 104249991375
+ ["P104249991375D", "string with days > max"],
+ [{ days: 104249991375 }, "property bag with days > max"],
+ ["P104249991374DT24H", "string where hours balance into days > max"],
+ [{ days: 104249991374, hours: 24 }, "property bag where hours balance into days > max"],
+ ["-P104249991375D", "string with days < min"],
+ [{ days: -104249991375 }, "property bag with days < min"],
+ ["-P104249991374DT24H", "string where hours balance into days < min"],
+ [{ days: -104249991374, hours: -24 }, "property bag where hours balance into days < min"],
+
+ // ceil(max safe integer / 3600) = 2501999792984
+ ["PT2501999792984H", "string with hours > max"],
+ [{ hours: 2501999792984 }, "property bag with hours > max"],
+ ["PT2501999792983H60M", "string where minutes balance into hours > max"],
+ [{ hours: 2501999792983, minutes: 60 }, "property bag where minutes balance into hours > max"],
+ ["-PT2501999792984H", "string with hours < min"],
+ [{ hours: -2501999792984 }, "property bag with hours < min"],
+ ["-PT2501999792983H60M", "string where minutes balance into hours < min"],
+ [{ hours: -2501999792983, minutes: -60 }, "property bag where minutes balance into hours < min"],
+
+ // ceil(max safe integer / 60) = 150119987579017
+ ["PT150119987579017M", "string with minutes > max"],
+ [{ minutes: 150119987579017 }, "property bag with minutes > max"],
+ ["PT150119987579016M60S", "string where seconds balance into minutes > max"],
+ [{ minutes: 150119987579016, seconds: 60 }, "property bag where seconds balance into minutes > max"],
+ ["-PT150119987579017M", "string with minutes < min"],
+ [{ minutes: -150119987579017 }, "property bag with minutes < min"],
+ ["-PT150119987579016M60S", "string where seconds balance into minutes < min"],
+ [{ minutes: -150119987579016, seconds: -60 }, "property bag where seconds balance into minutes < min"],
+
+ // 2^53 = 9007199254740992
+ ["PT9007199254740992S", "string with seconds > max"],
+ [{ seconds: 9007199254740992 }, "property bag with seconds > max"],
+ [{ seconds: 9007199254740991, milliseconds: 1000 }, "property bag where milliseconds balance into seconds > max"],
+ [{ seconds: 9007199254740991, microseconds: 1000000 }, "property bag where microseconds balance into seconds > max"],
+ [{ seconds: 9007199254740991, nanoseconds: 1000000000 }, "property bag where nanoseconds balance into seconds > max"],
+ ["-PT9007199254740992S", "string with seconds < min"],
+ [{ seconds: -9007199254740992 }, "property bag with seconds < min"],
+ [{ seconds: -9007199254740991, milliseconds: -1000 }, "property bag where milliseconds balance into seconds < min"],
+ [{ seconds: -9007199254740991, microseconds: -1000000 }, "property bag where microseconds balance into seconds < min"],
+ [{ seconds: -9007199254740991, nanoseconds: -1000000000 }, "property bag where nanoseconds balance into seconds < min"],
+];
+
+for (const [arg, descr] of cases) {
+ assert.throws(RangeError, () => instance.subtract(arg), `${descr} is out of range`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-invalid-property.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-invalid-property.js
new file mode 100644
index 0000000000..21ff0edfc5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-invalid-property.js
@@ -0,0 +1,31 @@
+// |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.plainyearmonth.prototype.subtract
+description: temporalDurationLike object must contain at least one correctly spelled property
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2000, 5);
+
+assert.throws(
+ TypeError,
+ () => instance.subtract({}),
+ "Throws TypeError if no property is present"
+);
+
+assert.throws(
+ TypeError,
+ () => instance.subtract({ nonsense: true }),
+ "Throws TypeError if no recognized property is present"
+);
+
+assert.throws(
+ TypeError,
+ () => instance.subtract({ sign: 1 }),
+ "Sign property is not recognized"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-lower-units.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-lower-units.js
new file mode 100644
index 0000000000..ea3156580b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-lower-units.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.plainyearmonth.prototype.subtract
+description: Using lower units in subtract() works
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const ym = Temporal.PlainYearMonth.from("2019-11");
+
+const tests = [
+ [{ days: 1 }, 2019, 11, "M11"],
+ [{ hours: 1 }, 2019, 11, "M11"],
+ [{ minutes: 1 }, 2019, 11, "M11"],
+ [{ seconds: 1 }, 2019, 11, "M11"],
+ [{ milliseconds: 1 }, 2019, 11, "M11"],
+ [{ microseconds: 1 }, 2019, 11, "M11"],
+ [{ nanoseconds: 1 }, 2019, 11, "M11"],
+ [{ days: 29 }, 2019, 11, "M11"],
+ [{ days: 30 }, 2019, 10, "M10"],
+ [{ days: 60 }, 2019, 10, "M10"],
+ [{ days: 61 }, 2019, 9, "M09"],
+ [{ hours: 720 }, 2019, 10, "M10"],
+ [{ minutes: 43200 }, 2019, 10, "M10"],
+ [{ seconds: 2592000 }, 2019, 10, "M10"],
+ [{ milliseconds: 2592000_000 }, 2019, 10, "M10"],
+ [{ microseconds: 2592000_000_000 }, 2019, 10, "M10"],
+ [{ nanoseconds: 2592000_000_000_000 }, 2019, 10, "M10"],
+];
+
+for (const [argument, ...expected] of tests) {
+ TemporalHelpers.assertPlainYearMonth(ym.subtract(argument), ...expected, "no options");
+ TemporalHelpers.assertPlainYearMonth(ym.subtract(argument, { overflow: "constrain" }), ...expected, "constrain");
+ TemporalHelpers.assertPlainYearMonth(ym.subtract(argument, { overflow: "reject" }), ...expected, "reject");
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-mixed-sign.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-mixed-sign.js
new file mode 100644
index 0000000000..f66b8abada
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-mixed-sign.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.plainyearmonth.prototype.subtract
+description: Positive and negative values in the temporalDurationLike argument are not acceptable
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2000, 5);
+
+["constrain", "reject"].forEach((overflow) => {
+ assert.throws(
+ RangeError,
+ () => instance.subtract({ hours: 1, minutes: -30 }, { overflow }),
+ `mixed positive and negative values always throw (overflow = "${overflow}")`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-not-object.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-not-object.js
new file mode 100644
index 0000000000..f9460b024d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-not-object.js
@@ -0,0 +1,22 @@
+// |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.plainyearmonth.prototype.subtract
+description: Passing a primitive other than string to subtract() throws
+features: [Symbol, Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2000, 5);
+assert.throws(TypeError, () => instance.subtract(undefined), "undefined");
+assert.throws(TypeError, () => instance.subtract(null), "null");
+assert.throws(TypeError, () => instance.subtract(true), "boolean");
+assert.throws(RangeError, () => instance.subtract(""), "empty string");
+assert.throws(TypeError, () => instance.subtract(Symbol()), "Symbol");
+assert.throws(TypeError, () => instance.subtract(7), "number");
+assert.throws(TypeError, () => instance.subtract(7n), "bigint");
+assert.throws(TypeError, () => instance.subtract([]), "array");
+assert.throws(TypeError, () => instance.subtract(() => {}), "function");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-object.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-object.js
new file mode 100644
index 0000000000..8b7393da1d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-object.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.plainyearmonth.prototype.subtract
+description: Passing an object to subtract() works
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const ym = Temporal.PlainYearMonth.from("2019-11");
+
+const tests = [
+ [{ months: 2 }, 2019, 9, "M09"],
+ [{ years: 1 }, 2018, 11, "M11"],
+ [{ months: -2 }, 2020, 1, "M01"],
+ [{ years: -1 }, 2020, 11, "M11"],
+];
+
+for (const [argument, ...expected] of tests) {
+ TemporalHelpers.assertPlainYearMonth(ym.subtract(argument), ...expected, "no options");
+ TemporalHelpers.assertPlainYearMonth(ym.subtract(argument, { overflow: "constrain" }), ...expected, "constrain");
+ TemporalHelpers.assertPlainYearMonth(ym.subtract(argument, { overflow: "reject" }), ...expected, "reject");
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-singular-properties.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-singular-properties.js
new file mode 100644
index 0000000000..779b7dbd08
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-singular-properties.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.plainyearmonth.prototype.subtract
+description: Singular properties in the property bag are always ignored
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2000, 5);
+
+[
+ { year: 1 },
+ { month: 2 },
+ { week: 3 },
+ { day: 4 },
+ { hour: 5 },
+ { minute: 6 },
+ { second: 7 },
+ { millisecond: 8 },
+ { microsecond: 9 },
+ { nanosecond: 10 },
+].forEach((badObject) => {
+ assert.throws(TypeError, () => instance.subtract(badObject),
+ "Throw TypeError if temporalDurationLike is not valid");
+});
+
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-string-negative-fractional-units.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-string-negative-fractional-units.js
new file mode 100644
index 0000000000..b3e7c9a74c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-string-negative-fractional-units.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.plainyearmonth.prototype.subtract
+description: Strings with fractional duration units are treated with the correct sign
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2000, 5);
+
+const resultHours = instance.subtract("-PT24.567890123H");
+TemporalHelpers.assertPlainYearMonth(resultHours, 2000, 5, "M05", "negative fractional hours");
+
+const resultMinutes = instance.subtract("-PT1440.567890123M");
+TemporalHelpers.assertPlainYearMonth(resultMinutes, 2000, 5, "M05", "negative fractional minutes");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-string.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-string.js
new file mode 100644
index 0000000000..deb2820797
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-string.js
@@ -0,0 +1,16 @@
+// |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.plainyearmonth.prototype.subtract
+description: A string is parsed into the correct object when passed as the argument
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = Temporal.PlainYearMonth.from({ year: 2000, month: 5 });
+const result = instance.subtract("P3M");
+TemporalHelpers.assertPlainYearMonth(result, 2000, 2, "M02");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/branding.js
new file mode 100644
index 0000000000..badb8f195c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/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.plainyearmonth.prototype.subtract
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const subtract = Temporal.PlainYearMonth.prototype.subtract;
+
+assert.sameValue(typeof subtract, "function");
+
+const args = [new Temporal.Duration(5)];
+
+assert.throws(TypeError, () => subtract.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => subtract.apply(null, args), "null");
+assert.throws(TypeError, () => subtract.apply(true, args), "true");
+assert.throws(TypeError, () => subtract.apply("", args), "empty string");
+assert.throws(TypeError, () => subtract.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => subtract.apply(1, args), "1");
+assert.throws(TypeError, () => subtract.apply({}, args), "plain object");
+assert.throws(TypeError, () => subtract.apply(Temporal.PlainYearMonth, args), "Temporal.PlainYearMonth");
+assert.throws(TypeError, () => subtract.apply(Temporal.PlainYearMonth.prototype, args), "Temporal.PlainYearMonth.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..97b965d7d3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/builtin-calendar-no-array-iteration.js
@@ -0,0 +1,23 @@
+// |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.plainyearmonth.prototype.subtract
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.PlainYearMonth(2023, 5, "iso8601");
+instance.subtract({ years: 5, months: 2 });
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..6b2834cf80
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plainyearmonth.prototype.subtract
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dateAddOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dateAdd");
+Object.defineProperty(Temporal.Calendar.prototype, "dateAdd", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("dateAdd should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainYearMonth(2000, 5, "iso8601", 1);
+instance.subtract(new Temporal.Duration(1));
+
+Object.defineProperty(Temporal.Calendar.prototype, "dateAdd", dateAddOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/builtin.js
new file mode 100644
index 0000000000..e69c799d31
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/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.plainyearmonth.prototype.subtract
+description: >
+ Tests that Temporal.PlainYearMonth.prototype.subtract
+ 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.PlainYearMonth.prototype.subtract),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainYearMonth.prototype.subtract),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainYearMonth.prototype.subtract),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainYearMonth.prototype.subtract.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-arguments-extra-options.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-arguments-extra-options.js
new file mode 100644
index 0000000000..b636036de0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-arguments-extra-options.js
@@ -0,0 +1,53 @@
+// |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.plainyearmonth.prototype.subtract
+description: plainyearmonth.prototype.subtract should pass extra fields in copied options objects.
+info: |
+ YearMonthFromFields ( calendar, fields [ , options ] )
+
+ 5. Let yearMonth be ? Invoke(calendar, "yearMonthFromFields", « fields, options »).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const expected = [
+ // CopyDataProperties
+ "ownKeys options",
+ "getOwnPropertyDescriptor options.extra",
+ "get options.extra",
+ // Temporal.Calendar.prototype.dateAdd
+ "get options.overflow",
+ // overwriting property in custom calendar dateAdd
+ "getOwnPropertyDescriptor options.overflow",
+];
+const options = TemporalHelpers.propertyBagObserver(actual, { extra: 5 }, "options");
+
+let dateAddCalls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ dateAdd(date, duration, options) {
+ const result = super.dateAdd(date, duration, options);
+ dateAddCalls++;
+ if (dateAddCalls == 2)
+ options.overflow = 'meatloaf';
+ return result;
+ }
+ yearMonthFromFields(...args) {
+ assert.sameValue(args.length, 2, "args.length");
+ assert.sameValue(typeof args[0], "object", "args[0]");
+ assert.notSameValue(args[1], options, "args[1]");
+ return super.yearMonthFromFields(...args);
+ }
+}
+const plainYearMonth = new Temporal.PlainYearMonth(2000, 3, new CustomCalendar());
+const result = plainYearMonth.subtract({ months: 5 }, options);
+TemporalHelpers.assertPlainYearMonth(result, 1999, 10, "M10");
+assert.compareArray(actual, expected, "extra field options object order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-arguments.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-arguments.js
new file mode 100644
index 0000000000..49e74d46b8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-arguments.js
@@ -0,0 +1,58 @@
+// |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.plainyearmonth.prototype.subtract
+description: plainyearmonth.prototype.subtract should respect calendar arguments and pass copied options objects.
+info: |
+ YearMonthFromFields ( calendar, fields [ , options ] )
+
+ 5. Let yearMonth be ? Invoke(calendar, "yearMonthFromFields", « fields, options »).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const expected = [
+ // CopyDataProperties
+ "ownKeys options",
+ "getOwnPropertyDescriptor options.overflow",
+ "get options.overflow",
+ // Temporal.Calendar.prototype.dateAdd
+ "get options.overflow",
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+ // overwriting property in custom calendar dateAdd
+ "getOwnPropertyDescriptor options.overflow",
+ // Temporal.Calendar.prototype.yearMonthFromFields (toPrimitiveObserver copied but not options object)
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+];
+const options = TemporalHelpers.propertyBagObserver(actual, { overflow: "constrain" }, "options");
+
+let dateAddCalls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ dateAdd(date, duration, options) {
+ const result = super.dateAdd(date, duration, options);
+ dateAddCalls++;
+ if (dateAddCalls == 2)
+ options.overflow = 'meatloaf';
+ return result;
+ }
+ yearMonthFromFields(...args) {
+ assert.sameValue(args.length, 2, "args.length");
+ assert.sameValue(typeof args[0], "object", "args[0]");
+ assert.notSameValue(args[1], options, "args[1]");
+ return super.yearMonthFromFields(...args);
+ }
+}
+const plainYearMonth = new Temporal.PlainYearMonth(2000, 7, new CustomCalendar());
+const result = plainYearMonth.subtract({ months: 9 }, options);
+TemporalHelpers.assertPlainYearMonth(result, 1999, 10, "M10");
+assert.compareArray(actual, expected, "copied options object order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-dateadd-called-with-plaindate-instance.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-dateadd-called-with-plaindate-instance.js
new file mode 100644
index 0000000000..c788dd00cf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-dateadd-called-with-plaindate-instance.js
@@ -0,0 +1,21 @@
+// |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.plainyearmonth.prototype.subtract
+description: Duration subtraction from PlainYearMonth calls Calendar.dateAdd the right number of times
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarDateAddPlainDateInstance();
+const instance = new Temporal.PlainYearMonth(1983, 3, calendar);
+TemporalHelpers.assertPlainYearMonth(instance.subtract({weeks: 5}), 1983, 2, 'M02', "Removing 5 weeks from March in is8601 calendar")
+assert.sameValue(calendar.dateAddCallCount, 2, "dateAdd called 2 times with positive subtract");
+
+calendar.dateAddCallCount = 0;
+TemporalHelpers.assertPlainYearMonth(instance.subtract({weeks: -5}), 1983, 4, 'M04', "Removing -5 weeks from March in is8601 calendar")
+assert.sameValue(calendar.dateAddCallCount, 1, "dateAdd called once with negative subtract");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-dateadd.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-dateadd.js
new file mode 100644
index 0000000000..72455866bc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-dateadd.js
@@ -0,0 +1,34 @@
+// |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.plainyearmonth.prototype.subtract
+description: PlainYearMonth.prototype.subtract should call dateAdd with the appropriate values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ dateAdd(plainDate, duration, options) {
+ ++calls;
+ if (calls == 2) {
+ TemporalHelpers.assertPlainDate(plainDate, 2000, 3, "M03", 31, "plainDate argument");
+ TemporalHelpers.assertDuration(duration, 0, -10, 0, 0, 0, 0, 0, 0, 0, 0, "duration argument");
+ assert.sameValue(typeof options, "object", "options argument: type");
+ assert.sameValue(Object.getPrototypeOf(options), null, "options argument: prototype");
+ }
+ return super.dateAdd(plainDate, duration, options);
+ }
+}
+
+const plainYearMonth = new Temporal.PlainYearMonth(2000, 3, new CustomCalendar());
+const result = plainYearMonth.subtract({ months: 10 });
+TemporalHelpers.assertPlainYearMonth(result, 1999, 5, "M05");
+assert.sameValue(calls, 2, "should have called dateAdd 2 times");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-datefromfields-called.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-datefromfields-called.js
new file mode 100644
index 0000000000..b83c3ccd36
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-datefromfields-called.js
@@ -0,0 +1,164 @@
+// |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.plainyearmonth.prototype.subtract
+description: >
+ Calls calendar's dateFromFields method to obtain a start date for the
+ operation, based on the sign of the duration
+info: |
+ 9. Let _fields_ be ? PrepareTemporalFields(_yearMonth_, _fieldNames_, «»).
+ 10. Let _sign_ be ! DurationSign(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _balanceResult_.[[Days]], 0, 0, 0, 0, 0, 0).
+ 11. If _sign_ < 0, then
+ a. Let _dayFromCalendar_ be ? CalendarDaysInMonth(_calendar_, _yearMonth_).
+ b. Let _day_ be ? ToPositiveInteger(_dayFromCalendar_).
+ 12. Else,
+ a. Let _day_ be 1.
+ 13. Perform ! CreateDataPropertyOrThrow(_fields_, *"day"*, _day_).
+ 14. Let _date_ be ? DateFromFields(_calendar_, _fields_, *undefined*).
+includes: [deepEqual.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ this.dateFromFieldsCalls = [];
+ }
+ year(date) {
+ // years in this calendar start and end on the same day as ISO 8601 years
+ return date.getISOFields().isoYear;
+ }
+ month(date) {
+ // this calendar has 10 months of 36 days each, plus an 11th month of 5 or 6
+ const { isoYear, isoMonth, isoDay } = date.getISOFields();
+ const isoDate = new Temporal.PlainDate(isoYear, isoMonth, isoDay);
+ return Math.floor((isoDate.dayOfYear - 1) / 36) + 1;
+ }
+ monthCode(date) {
+ return "M" + this.month(date).toString().padStart(2, "0");
+ }
+ day(date) {
+ return (date.dayOfYear - 1) % 36 + 1;
+ }
+ daysInMonth(date) {
+ if (this.month(date) < 11) return 36;
+ return this.daysInYear(date) - 360;
+ }
+ _dateFromFieldsImpl({ year, month, monthCode, day }) {
+ if (year === undefined) throw new TypeError("year required");
+ if (month === undefined && monthCode === undefined) throw new TypeError("one of month or monthCode required");
+ if (month !== undefined && month < 1) throw new RangeError("month < 1");
+ if (day === undefined) throw new TypeError("day required");
+
+ if (monthCode !== undefined) {
+ const numberPart = +(monthCode.slice(1));
+ if ("M" + `${numberPart}`.padStart(2, "0") !== monthCode) throw new RangeError("invalid monthCode");
+ if (month === undefined) {
+ month = numberPart;
+ } else if (month !== numberPart) {
+ throw new RangeError("month and monthCode must match");
+ }
+ }
+
+ const isoDayOfYear = (month - 1) * 36 + day;
+ return new Temporal.PlainDate(year, 1, 1).add({ days: isoDayOfYear - 1 }).withCalendar(this);
+ }
+ dateFromFields(...args) {
+ this.dateFromFieldsCalls.push(args);
+ return this._dateFromFieldsImpl(...args);
+ }
+ yearMonthFromFields(fields, options) {
+ const { isoYear, isoMonth, isoDay } = this._dateFromFieldsImpl({ ...fields, day: 1 }, options).getISOFields();
+ return new Temporal.PlainYearMonth(isoYear, isoMonth, this, isoDay);
+ }
+ monthDayFromFields(fields, options) {
+ const { isoYear, isoMonth, isoDay } = this._dateFromFieldsImpl({ ...fields, year: 2000 }, options).getISOFields();
+ return new Temporal.PlainMonthDay(isoMonth, isoDay, this, isoYear);
+ }
+ dateAdd(date, duration, options) {
+ const {isoYear, isoMonth, isoDay} = date.getISOFields();
+ let {years, months, weeks, days} = duration;
+ let iter = new Temporal.PlainDate(isoYear + years, isoMonth, isoDay, "iso8601");
+ const monthsDays = months * 36;
+ if (iter.dayOfYear + monthsDays > iter.daysInYear || iter.dayOfYear + monthsDays < 1)
+ throw new Error("complicated addition not implemented in this test");
+ return iter.add({ weeks, days: monthsDays + days }).withCalendar(this);
+ }
+ toString() {
+ return "thirty-six";
+ }
+}
+
+const calendar = new CustomCalendar();
+const month2 = Temporal.PlainYearMonth.from({ year: 2022, month: 2, calendar });
+const lessThanOneMonth = new Temporal.Duration(0, 0, 0, 35);
+const oneMonth = new Temporal.Duration(0, 0, 0, 36);
+
+// Reference ISO dates in the custom calendar:
+// M01 = 01-01
+// M02 = 02-06
+// M03 = 03-14
+
+calendar.dateFromFieldsCalls = [];
+TemporalHelpers.assertPlainYearMonth(
+ month2.subtract(lessThanOneMonth),
+ 2022, 2, "M02",
+ "subtracting positive less than one month's worth of days yields the same month",
+ /* era = */ undefined, /* eraYear = */ undefined, /* referenceISODay = */ 6
+);
+assert.sameValue(calendar.dateFromFieldsCalls.length, 2, "dateFromFields was called twice");
+assert.deepEqual(
+ calendar.dateFromFieldsCalls[1][0],
+ { year: 2022, monthCode: "M02", day: 36 },
+ "last day of month 2 passed to dateFromFields when subtracting positive duration"
+);
+assert.sameValue(calendar.dateFromFieldsCalls[0][1], undefined, "undefined options passed");
+
+calendar.dateFromFieldsCalls = [];
+TemporalHelpers.assertPlainYearMonth(
+ month2.subtract(oneMonth),
+ 2022, 1, "M01",
+ "subtracting positive one month's worth of days yields the previous month",
+ /* era = */ undefined, /* eraYear = */ undefined, /* referenceISODay = */ 1
+);
+assert.sameValue(calendar.dateFromFieldsCalls.length, 2, "dateFromFields was called twice");
+assert.deepEqual(
+ calendar.dateFromFieldsCalls[1][0],
+ { year: 2022, monthCode: "M02", day: 36 },
+ "last day of month 2 passed to dateFromFields when subtracting positive duration"
+);
+assert.sameValue(calendar.dateFromFieldsCalls[0][1], undefined, "undefined options passed");
+
+calendar.dateFromFieldsCalls = [];
+TemporalHelpers.assertPlainYearMonth(
+ month2.subtract(lessThanOneMonth.negated()),
+ 2022, 2, "M02",
+ "subtracting negative less than one month's worth of days yields the same month",
+ /* era = */ undefined, /* eraYear = */ undefined, /* referenceISODay = */ 6
+);
+assert.sameValue(calendar.dateFromFieldsCalls.length, 1, "dateFromFields was called");
+assert.deepEqual(
+ calendar.dateFromFieldsCalls[0][0],
+ { year: 2022, monthCode: "M02", day: 1 },
+ "first day of month 2 passed to dateFromFields when subtracting negative duration"
+);
+assert.sameValue(calendar.dateFromFieldsCalls[0][1], undefined, "undefined options passed");
+
+calendar.dateFromFieldsCalls = [];
+TemporalHelpers.assertPlainYearMonth(
+ month2.subtract(oneMonth.negated()),
+ 2022, 3, "M03",
+ "subtracting negative one month's worth of days yields the following month",
+ /* era = */ undefined, /* eraYear = */ undefined, /* referenceISODay = */ 14
+);
+assert.sameValue(calendar.dateFromFieldsCalls.length, 1, "dateFromFields was called");
+assert.deepEqual(
+ calendar.dateFromFieldsCalls[0][0],
+ { year: 2022, monthCode: "M02", day: 1 },
+ "first day of month 2 passed to dateFromFields when subtracting negative duration"
+);
+assert.sameValue(calendar.dateFromFieldsCalls[0][1], undefined, "undefined options passed");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-fields-iterable.js
new file mode 100644
index 0000000000..0c5ca99437
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-fields-iterable.js
@@ -0,0 +1,30 @@
+// |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.plainyearmonth.prototype.subtract
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plainyearmonth.prototype.subtract step 8:
+ 8. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "monthCode",
+ "year",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+const yearmonth = new Temporal.PlainYearMonth(2000, 5, calendar);
+yearmonth.subtract({ months: 1 });
+
+assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-fromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-fromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..897d365a8f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-fromfields-called-with-null-prototype-fields.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.plainyearmonth.prototype.subtract
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.PlainYearMonth(2000, 5, calendar);
+instance.subtract(new Temporal.Duration(1));
+assert.sameValue(calendar.dateFromFieldsCallCount, 2, "dateFromFields should have been called twice on the calendar");
+assert.sameValue(calendar.yearMonthFromFieldsCallCount, 1, "yearMonthFromFields should have been called on the calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-yearmonthfromfields-called-with-null-prototype-options.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-yearmonthfromfields-called-with-null-prototype-options.js
new file mode 100644
index 0000000000..b3ad583339
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-yearmonthfromfields-called-with-null-prototype-options.js
@@ -0,0 +1,20 @@
+// |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.plainyearmonth.prototype.subtract
+description: >
+ Calendar.yearMonthFromFields method is called with a null-prototype object
+ as the options value when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckOptionsPrototypePollution();
+const instance = new Temporal.PlainYearMonth(2019, 6, calendar);
+const argument = new Temporal.Duration(1, 1);
+instance.subtract(argument);
+assert.sameValue(calendar.yearMonthFromFieldsCallCount, 1, "yearMonthFromFields should be called on the calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..5ee587243a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/constructor-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |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.plainyearmonth.prototype.subtract
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const ym = new Temporal.PlainYearMonth(2023, 5, calendar);
+
+assert.throws(RangeError, () => ym.subtract({days: 123}));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/custom-daysInMonth-irrelevant.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/custom-daysInMonth-irrelevant.js
new file mode 100644
index 0000000000..d611f5e50c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/custom-daysInMonth-irrelevant.js
@@ -0,0 +1,26 @@
+// |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.plainyearmonth.prototype.subtract
+description: Subtraction of positive duration to a PlainYearMonth is not influenced by the implementation of daysInMonth()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ daysInMonth(ym, ...args) {
+ return 15;
+ }
+}
+
+const customCalendar = new CustomCalendar();
+const instance = new Temporal.PlainYearMonth(2023, 3, customCalendar);
+
+TemporalHelpers.assertPlainYearMonth(instance.subtract({days: 30}), 2023, 3, 'M03', "Subtracting 30 days from calendar reimplementing daysinMonth()")
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/duplicate-calendar-fields.js
new file mode 100644
index 0000000000..9a56dc9ee7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/duplicate-calendar-fields.js
@@ -0,0 +1,18 @@
+// |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.plainyearmonth.prototype.subtract
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['monthCode'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const ym = new Temporal.PlainYearMonth(2023, 5, calendar);
+
+ assert.throws(RangeError, () => ym.subtract({days: 123}));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/end-of-month-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/end-of-month-out-of-range.js
new file mode 100644
index 0000000000..947f07589f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/end-of-month-out-of-range.js
@@ -0,0 +1,33 @@
+// |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.plainyearmonth.prototype.subtract
+description: RangeError thrown when subtracting positive duration and end of month is out of range
+features: [Temporal]
+info: |
+ AddDurationToOrSubtractDurationFromPlainYearMonth:
+ 12. If _sign_ &lt; 0, then
+ a. Let _oneMonthDuration_ be ! CreateTemporalDuration(0, 1, 0, 0, 0, 0, 0, 0, 0, 0).
+ b. Let _nextMonth_ be ? CalendarDateAdd(_calendar_, _intermediateDate_, _oneMonthDuration_, *undefined*, _dateAdd_).
+ c. Let _endOfMonthISO_ be ! AddISODate(_nextMonth_.[[ISOYear]], _nextMonth_.[[ISOMonth]], _nextMonth_.[[ISODay]], 0, 0, 0, -1, *"constrain"*).
+ d. Let _endOfMonth_ be ? CreateTemporalDate(_endOfMonthISO_.[[Year]], _endOfMonthISO_.[[Month]], _endOfMonthISO_.[[Day]], _calendar_).
+---*/
+
+// Based on a test case by André Bargull <andre.bargull@gmail.com>
+
+const duration = new Temporal.Duration(0, 0, 0, 1);
+
+// Calendar addition result is out of range
+assert.throws(RangeError, () => new Temporal.PlainYearMonth(275760, 9).subtract(duration), "Addition of 1 month to receiver out of range");
+
+// Calendar addition succeeds, but subtracting 1 day gives out of range result
+const cal = new class extends Temporal.Calendar {
+ dateAdd() {
+ return new Temporal.PlainDate(-271821, 4, 19);
+ }
+}("iso8601");
+assert.throws(RangeError, () => new Temporal.PlainYearMonth(2000, 1, cal).subtract(duration), "Subtraction of 1 day from next month out of range");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..d529ef21c1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/infinity-throws-rangeerror.js
@@ -0,0 +1,37 @@
+// |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.
+
+/*---
+description: Temporal.PlainYearMonth.prototype.subtract throws a RangeError if any value in a property bag is Infinity
+esid: sec-temporal.plainyearmonth.prototype.subtract
+features: [Temporal]
+---*/
+const overflows = ["constrain", "reject"];
+const fields = ["years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"];
+
+const instance = Temporal.PlainYearMonth.from({ year: 2000, month: 5 });
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.subtract({ [field]: Infinity }, { overflow }));
+ });
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return Infinity;
+ }
+};
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => instance.subtract({ [field]: obj }, { overflow }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/length.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/length.js
new file mode 100644
index 0000000000..7b0ef46739
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/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.plainyearmonth.prototype.subtract
+description: Temporal.PlainYearMonth.prototype.subtract.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.PlainYearMonth.prototype.subtract, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/limits.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/limits.js
new file mode 100644
index 0000000000..6489ed09a2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/limits.js
@@ -0,0 +1,16 @@
+// |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.plainyearmonth.prototype.subtract
+description: RangeError thrown when going out of range
+features: [Temporal]
+---*/
+
+const min = Temporal.PlainYearMonth.from("-271821-04");
+for (const overflow of ["reject", "constrain"]) {
+ assert.throws(RangeError, () => min.subtract({ months: 1 }, { overflow }), overflow);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/month-length.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/month-length.js
new file mode 100644
index 0000000000..ab18f8ef9a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/month-length.js
@@ -0,0 +1,31 @@
+// |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.plainyearmonth.prototype.subtract
+description: subtract() takes month length into account
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const ym = Temporal.PlainYearMonth.from("2019-11");
+
+TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from("2019-02").subtract({ days: 27 }),
+ 2019, 2, "M02");
+TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from("2019-02").subtract({ days: 28 }),
+ 2019, 1, "M01");
+TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from("2020-02").subtract({ days: 28 }),
+ 2020, 2, "M02");
+TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from("2020-02").subtract({ days: 29 }),
+ 2020, 1, "M01");
+TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from("2019-11").subtract({ days: 29 }),
+ 2019, 11, "M11");
+TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from("2019-11").subtract({ days: 30 }),
+ 2019, 10, "M10");
+TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from("2020-01").subtract({ days: 30 }),
+ 2020, 1, "M01");
+TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from("2020-01").subtract({ days: 31 }),
+ 2019, 12, "M12");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/name.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/name.js
new file mode 100644
index 0000000000..fe1133c53d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/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.plainyearmonth.prototype.subtract
+description: Temporal.PlainYearMonth.prototype.subtract.name is "subtract".
+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.PlainYearMonth.prototype.subtract, "name", {
+ value: "subtract",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/negative-infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/negative-infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..5412bb0f7e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/negative-infinity-throws-rangeerror.js
@@ -0,0 +1,38 @@
+// |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.
+
+/*---
+description: Temporal.PlainYearMonth.prototype.subtract throws a RangeError if any value in a property bag is -Infinity
+esid: sec-temporal.plainyearmonth.prototype.subtract
+features: [Temporal]
+---*/
+
+const overflows = ["constrain", "reject"];
+const fields = ["years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"];
+
+const instance = Temporal.PlainYearMonth.from({ year: 2000, month: 5 });
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.subtract({ [field]: -Infinity }, { overflow }));
+ });
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return -Infinity;
+ }
+};
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => instance.subtract({ [field]: obj }, { overflow }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/non-integer-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/non-integer-throws-rangeerror.js
new file mode 100644
index 0000000000..e5073101a8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/non-integer-throws-rangeerror.js
@@ -0,0 +1,29 @@
+// |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.plainyearmonth.prototype.subtract
+description: A non-integer value for any recognized property in the property bag, throws a RangeError
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2000, 5);
+const fields = [
+ "years",
+ "months",
+ "weeks",
+ "days",
+ "hours",
+ "minutes",
+ "seconds",
+ "milliseconds",
+ "microseconds",
+ "nanoseconds",
+];
+fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.subtract({ [field]: 1.5 }));
+ assert.throws(RangeError, () => instance.subtract({ [field]: -1.5 }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/not-a-constructor.js
new file mode 100644
index 0000000000..14d79bcdd5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/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.plainyearmonth.prototype.subtract
+description: >
+ Temporal.PlainYearMonth.prototype.subtract 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.PlainYearMonth.prototype.subtract();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainYearMonth.prototype.subtract), false,
+ "isConstructor(Temporal.PlainYearMonth.prototype.subtract)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/options-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/options-invalid.js
new file mode 100644
index 0000000000..73622a2eb3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/options-invalid.js
@@ -0,0 +1,17 @@
+// |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.plainyearmonth.prototype.subtract
+description: Invalid options throw
+features: [Temporal]
+---*/
+
+const ym = Temporal.PlainYearMonth.from("2019-11");
+const values = [null, true, "hello", Symbol("foo"), 1, 1n];
+for (const badOptions of values) {
+ assert.throws(TypeError, () => ym.subtract({ years: 1 }, badOptions));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/options-object.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/options-object.js
new file mode 100644
index 0000000000..7c8aa9302d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/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.plainyearmonth.prototype.subtract
+description: Empty or a function object may be used as options
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2019, 10);
+
+const result1 = instance.subtract({ months: 1 }, {});
+TemporalHelpers.assertPlainYearMonth(
+ result1, 2019, 9, "M09",
+ "options may be an empty plain object"
+);
+
+const result2 = instance.subtract({ months: 1 }, () => {});
+TemporalHelpers.assertPlainYearMonth(
+ result2, 2019, 9, "M09",
+ "options may be a function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/options-undefined.js
new file mode 100644
index 0000000000..fa37762e41
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/options-undefined.js
@@ -0,0 +1,37 @@
+// |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.plainyearmonth.prototype.subtract
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+// overflow option has no effect on addition in the ISO calendar, so verify this
+// with a custom calendar
+class CheckedAdd extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ this.called = 0;
+ }
+ dateAdd(date, duration, options, constructor) {
+ this.called += 1;
+ if (this.called == 2)
+ assert.notSameValue(options, undefined, "options not undefined");
+ return super.dateAdd(date, duration, options, constructor);
+ }
+}
+const calendar = new CheckedAdd();
+
+const yearmonth = new Temporal.PlainYearMonth(2000, 3, calendar);
+const duration = { months: 1 };
+
+yearmonth.subtract(duration, undefined);
+assert.sameValue(calendar.called, 2, "dateAdd should have been called twice");
+
+calendar.called = 0;
+yearmonth.subtract(duration);
+assert.sameValue(calendar.called, 2, "dateAdd should have been called twice");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/options-wrong-type.js
new file mode 100644
index 0000000000..cec4d21c4e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/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.plainyearmonth.prototype.subtract
+description: TypeError thrown when options argument is a primitive
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const badOptions = [
+ null,
+ true,
+ "some string",
+ Symbol(),
+ 1,
+ 2n,
+];
+
+const instance = new Temporal.PlainYearMonth(2019, 10);
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.subtract({ months: 1 }, value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/order-of-operations.js
new file mode 100644
index 0000000000..c12b7bd1a7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/order-of-operations.js
@@ -0,0 +1,190 @@
+// |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.plainyearmonth.prototype.subtract
+description: Properties on an object passed to subtract() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ // ToTemporalDuration
+ "get fields.days",
+ "get fields.days.valueOf",
+ "call fields.days.valueOf",
+ "get fields.hours",
+ "get fields.hours.valueOf",
+ "call fields.hours.valueOf",
+ "get fields.microseconds",
+ "get fields.microseconds.valueOf",
+ "call fields.microseconds.valueOf",
+ "get fields.milliseconds",
+ "get fields.milliseconds.valueOf",
+ "call fields.milliseconds.valueOf",
+ "get fields.minutes",
+ "get fields.minutes.valueOf",
+ "call fields.minutes.valueOf",
+ "get fields.months",
+ "get fields.months.valueOf",
+ "call fields.months.valueOf",
+ "get fields.nanoseconds",
+ "get fields.nanoseconds.valueOf",
+ "call fields.nanoseconds.valueOf",
+ "get fields.seconds",
+ "get fields.seconds.valueOf",
+ "call fields.seconds.valueOf",
+ "get fields.weeks",
+ "get fields.weeks.valueOf",
+ "call fields.weeks.valueOf",
+ "get fields.years",
+ "get fields.years.valueOf",
+ "call fields.years.valueOf",
+ // lookup
+ "get this.calendar.dateAdd",
+ "get this.calendar.dateFromFields",
+ "get this.calendar.day",
+ "get this.calendar.fields",
+ "get this.calendar.yearMonthFromFields",
+ // CalendarFields
+ "call this.calendar.fields",
+ // PrepareTemporalFields on receiver
+ "get this.calendar.monthCode",
+ "call this.calendar.monthCode",
+ "get this.calendar.year",
+ "call this.calendar.year",
+ // calculate last day of month
+ "call this.calendar.dateFromFields",
+ "call this.calendar.dateAdd",
+ "call this.calendar.day",
+ "call this.calendar.dateFromFields",
+ // CopyDataProperties
+ "ownKeys options",
+ "getOwnPropertyDescriptor options.overflow",
+ "get options.overflow",
+ // CalendarDateAdd
+ "call this.calendar.dateAdd",
+ // inside Calendar.p.dateAdd
+ "get options.overflow",
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+ // PrepareTemporalFields on added date
+ "get this.calendar.monthCode",
+ "call this.calendar.monthCode",
+ "get this.calendar.year",
+ "call this.calendar.year",
+ // CalendarYearMonthFromFields
+ "call this.calendar.yearMonthFromFields",
+ // inside Calendar.p.yearMonthFromFields
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+];
+const actual = [];
+
+const calendar = TemporalHelpers.calendarObserver(actual, "this.calendar");
+const instance = new Temporal.PlainYearMonth(2000, 5, calendar);
+// clear observable operations that occurred during the constructor call
+actual.splice(0);
+
+const fields = TemporalHelpers.propertyBagObserver(actual, {
+ years: 1,
+ months: 1,
+ weeks: 1,
+ days: 1,
+ hours: 1,
+ minutes: 1,
+ seconds: 1,
+ milliseconds: 1,
+ microseconds: 1,
+ nanoseconds: 1,
+}, "fields");
+
+const options = TemporalHelpers.propertyBagObserver(actual, { overflow: "constrain" }, "options");
+
+instance.subtract(fields, options);
+assert.compareArray(actual, expected, "order of operations");
+
+actual.splice(0); // clear
+
+const noCalendarExpected = [
+ // ToTemporalDuration
+ "get fields.days",
+ "get fields.days.valueOf",
+ "call fields.days.valueOf",
+ "get fields.hours",
+ "get fields.hours.valueOf",
+ "call fields.hours.valueOf",
+ "get fields.microseconds",
+ "get fields.microseconds.valueOf",
+ "call fields.microseconds.valueOf",
+ "get fields.milliseconds",
+ "get fields.milliseconds.valueOf",
+ "call fields.milliseconds.valueOf",
+ "get fields.minutes",
+ "get fields.minutes.valueOf",
+ "call fields.minutes.valueOf",
+ "get fields.months",
+ "get fields.nanoseconds",
+ "get fields.nanoseconds.valueOf",
+ "call fields.nanoseconds.valueOf",
+ "get fields.seconds",
+ "get fields.seconds.valueOf",
+ "call fields.seconds.valueOf",
+ "get fields.weeks",
+ "get fields.years",
+ // lookup
+ "get this.calendar.dateAdd",
+ "get this.calendar.dateFromFields",
+ "get this.calendar.day",
+ "get this.calendar.fields",
+ "get this.calendar.yearMonthFromFields",
+ // CalendarFields
+ "call this.calendar.fields",
+ // PrepareTemporalFields on receiver
+ "get this.calendar.monthCode",
+ "call this.calendar.monthCode",
+ "get this.calendar.year",
+ "call this.calendar.year",
+ // CalendarDateFromFields
+ "call this.calendar.dateFromFields",
+ // calculate last day of month
+ "call this.calendar.dateAdd",
+ "call this.calendar.day",
+ "call this.calendar.dateFromFields",
+ // SnapshotOwnProperties
+ "ownKeys options",
+ "getOwnPropertyDescriptor options.overflow",
+ "get options.overflow",
+ // AddDate
+ "get options.overflow",
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+ // PrepareTemporalFields on added date
+ "get this.calendar.monthCode",
+ "call this.calendar.monthCode",
+ "get this.calendar.year",
+ "call this.calendar.year",
+ // CalendarYearMonthFromFields
+ "call this.calendar.yearMonthFromFields",
+ // inside Calendar.p.yearMonthFromFields
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+];
+
+const noCalendarFields = TemporalHelpers.propertyBagObserver(actual, {
+ days: 1,
+ hours: 1,
+ minutes: 1,
+ seconds: 1,
+ milliseconds: 1,
+ microseconds: 1,
+ nanoseconds: 1,
+}, "fields");
+
+instance.subtract(noCalendarFields, options);
+assert.compareArray(actual, noCalendarExpected, "order of operations with no calendar units");
+
+actual.splice(0); // clear
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/overflow-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/overflow-invalid-string.js
new file mode 100644
index 0000000000..96af1e7847
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/overflow-invalid-string.js
@@ -0,0 +1,34 @@
+// |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.plainyearmonth.prototype.subtract
+description: RangeError thrown when overflow option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-isoyearmonthfromfields step 2:
+ 2. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plainyearmonth.prototype.subtract steps 13–15:
+ 13. Let _addedDate_ be ? CalendarDateAdd(_calendar_, _date_, _durationToAdd_, _options_).
+ 14. ...
+ 15. Return ? YearMonthFromFields(_calendar_, _addedDateFields_, _options_).
+features: [Temporal]
+---*/
+
+const yearmonth = new Temporal.PlainYearMonth(2000, 5);
+const duration = new Temporal.Duration(1, 1);
+
+const badOverflows = ["", "CONSTRAIN", "balance", "other string", "constra\u0131n", "reject\0"];
+for (const overflow of badOverflows) {
+ assert.throws(
+ RangeError,
+ () => yearmonth.subtract(duration, { overflow }),
+ `invalid overflow ("${overflow}")`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/overflow-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/overflow-undefined.js
new file mode 100644
index 0000000000..f87903ef12
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/overflow-undefined.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.plainyearmonth.prototype.subtract
+description: Fallback value for overflow option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-isoyearmonthfromfields step 2:
+ 2. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plainyearmonth.prototype.subtract steps 13–15:
+ 13. Let _addedDate_ be ? CalendarDateAdd(_calendar_, _date_, _durationToAdd_, _options_).
+ 14. ...
+ 15. Return ? YearMonthFromFields(_calendar_, _addedDateFields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+// In the ISO calendar, PlainYearMonth.prototype.subtract() actually ignores the
+// overflow option. There is no subtraction in the ISO calendar that we could
+// test which would actually show a difference between the 'constrain' and
+// 'reject' values.
+const yearmonth = new Temporal.PlainYearMonth(2000, 5);
+const duration = new Temporal.Duration(1, 1);
+const explicit = yearmonth.subtract(duration, { overflow: undefined });
+TemporalHelpers.assertPlainYearMonth(explicit, 1999, 4, "M04", "default overflow is constrain");
+const implicit = yearmonth.subtract(duration, {});
+TemporalHelpers.assertPlainYearMonth(implicit, 1999, 4, "M04", "default overflow is constrain");
+const lambda = yearmonth.subtract(duration, () => {});
+TemporalHelpers.assertPlainYearMonth(lambda, 1999, 4, "M04", "default overflow is constrain");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/overflow-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/overflow-wrong-type.js
new file mode 100644
index 0000000000..30a23df9cf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/overflow-wrong-type.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.plainyearmonth.prototype.subtract
+description: Type conversions for overflow option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-isoyearmonthfromfields step 2:
+ 2. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plainyearmonth.prototype.subtract steps 13–15:
+ 13. Let _addedDate_ be ? CalendarDateAdd(_calendar_, _date_, _durationToAdd_, _options_).
+ 14. ...
+ 15. Return ? YearMonthFromFields(_calendar_, _addedDateFields_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const yearmonth = new Temporal.PlainYearMonth(2000, 5);
+const duration = new Temporal.Duration(1, 1);
+
+// See TemporalHelpers.checkStringOptionWrongType(); this code path has
+// different expectations for observable calls
+
+assert.throws(RangeError, () => yearmonth.subtract(duration, { overflow: null }), "null");
+assert.throws(RangeError, () => yearmonth.subtract(duration, { overflow: true }), "true");
+assert.throws(RangeError, () => yearmonth.subtract(duration, { overflow: false }), "false");
+assert.throws(TypeError, () => yearmonth.subtract(duration, { overflow: Symbol() }), "symbol");
+assert.throws(RangeError, () => yearmonth.subtract(duration, { overflow: 2 }), "number");
+assert.throws(RangeError, () => yearmonth.subtract(duration, { overflow: 2n }), "bigint");
+assert.throws(RangeError, () => yearmonth.subtract(duration, { overflow: {} }), "plain object");
+
+// toString property is read once by Calendar.dateAdd() and then once again by
+// calendar.yearMonthFromFields().
+const expected = [
+ "get overflow.toString",
+ "call overflow.toString",
+ "get overflow.toString",
+ "call overflow.toString",
+];
+const actual = [];
+const observer = TemporalHelpers.toPrimitiveObserver(actual, "constrain", "overflow");
+const result = yearmonth.subtract(duration, { overflow: observer });
+TemporalHelpers.assertPlainYearMonth(result, 1999, 4, "M04", "object with toString");
+assert.compareArray(actual, expected, "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/prop-desc.js
new file mode 100644
index 0000000000..ed4a5ff063
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/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.plainyearmonth.prototype.subtract
+description: The "subtract" property of Temporal.PlainYearMonth.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainYearMonth.prototype.subtract,
+ "function",
+ "`typeof PlainYearMonth.prototype.subtract` is `function`"
+);
+
+verifyProperty(Temporal.PlainYearMonth.prototype, "subtract", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/proto-in-calendar-fields.js
new file mode 100644
index 0000000000..5660520393
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/proto-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |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.plainyearmonth.prototype.subtract
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const ym = new Temporal.PlainYearMonth(2023, 5, calendar);
+
+assert.throws(RangeError, () => ym.subtract({days: 123}));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/shell.js
new file mode 100644
index 0000000000..346758ebd5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/shell.js
@@ -0,0 +1,353 @@
+// GENERATED, DO NOT EDIT
+// file: deepEqual.js
+// Copyright 2019 Ron Buckton. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+description: >
+ Compare two values structurally
+defines: [assert.deepEqual]
+---*/
+
+assert.deepEqual = function(actual, expected, message) {
+ var format = assert.deepEqual.format;
+ assert(
+ assert.deepEqual._compare(actual, expected),
+ `Expected ${format(actual)} to be structurally equal to ${format(expected)}. ${(message || '')}`
+ );
+};
+
+assert.deepEqual.format = function(value, seen) {
+ switch (typeof value) {
+ case 'string':
+ return typeof JSON !== "undefined" ? JSON.stringify(value) : `"${value}"`;
+ case 'number':
+ case 'boolean':
+ case 'symbol':
+ case 'bigint':
+ return value.toString();
+ case 'undefined':
+ return 'undefined';
+ case 'function':
+ return `[Function${value.name ? `: ${value.name}` : ''}]`;
+ case 'object':
+ if (value === null) return 'null';
+ if (value instanceof Date) return `Date "${value.toISOString()}"`;
+ if (value instanceof RegExp) return value.toString();
+ if (!seen) {
+ seen = {
+ counter: 0,
+ map: new Map()
+ };
+ }
+
+ let usage = seen.map.get(value);
+ if (usage) {
+ usage.used = true;
+ return `[Ref: #${usage.id}]`;
+ }
+
+ usage = { id: ++seen.counter, used: false };
+ seen.map.set(value, usage);
+
+ if (typeof Set !== "undefined" && value instanceof Set) {
+ return `Set {${Array.from(value).map(value => assert.deepEqual.format(value, seen)).join(', ')}}${usage.used ? ` as #${usage.id}` : ''}`;
+ }
+ if (typeof Map !== "undefined" && value instanceof Map) {
+ return `Map {${Array.from(value).map(pair => `${assert.deepEqual.format(pair[0], seen)} => ${assert.deepEqual.format(pair[1], seen)}}`).join(', ')}}${usage.used ? ` as #${usage.id}` : ''}`;
+ }
+ if (Array.isArray ? Array.isArray(value) : value instanceof Array) {
+ return `[${value.map(value => assert.deepEqual.format(value, seen)).join(', ')}]${usage.used ? ` as #${usage.id}` : ''}`;
+ }
+ let tag = Symbol.toStringTag in value ? value[Symbol.toStringTag] : 'Object';
+ if (tag === 'Object' && Object.getPrototypeOf(value) === null) {
+ tag = '[Object: null prototype]';
+ }
+ return `${tag ? `${tag} ` : ''}{ ${Object.keys(value).map(key => `${key.toString()}: ${assert.deepEqual.format(value[key], seen)}`).join(', ')} }${usage.used ? ` as #${usage.id}` : ''}`;
+ default:
+ return typeof value;
+ }
+};
+
+assert.deepEqual._compare = (function () {
+ var EQUAL = 1;
+ var NOT_EQUAL = -1;
+ var UNKNOWN = 0;
+
+ function deepEqual(a, b) {
+ return compareEquality(a, b) === EQUAL;
+ }
+
+ function compareEquality(a, b, cache) {
+ return compareIf(a, b, isOptional, compareOptionality)
+ || compareIf(a, b, isPrimitiveEquatable, comparePrimitiveEquality)
+ || compareIf(a, b, isObjectEquatable, compareObjectEquality, cache)
+ || NOT_EQUAL;
+ }
+
+ function compareIf(a, b, test, compare, cache) {
+ return !test(a)
+ ? !test(b) ? UNKNOWN : NOT_EQUAL
+ : !test(b) ? NOT_EQUAL : cacheComparison(a, b, compare, cache);
+ }
+
+ function tryCompareStrictEquality(a, b) {
+ return a === b ? EQUAL : UNKNOWN;
+ }
+
+ function tryCompareTypeOfEquality(a, b) {
+ return typeof a !== typeof b ? NOT_EQUAL : UNKNOWN;
+ }
+
+ function tryCompareToStringTagEquality(a, b) {
+ var aTag = Symbol.toStringTag in a ? a[Symbol.toStringTag] : undefined;
+ var bTag = Symbol.toStringTag in b ? b[Symbol.toStringTag] : undefined;
+ return aTag !== bTag ? NOT_EQUAL : UNKNOWN;
+ }
+
+ function isOptional(value) {
+ return value === undefined
+ || value === null;
+ }
+
+ function compareOptionality(a, b) {
+ return tryCompareStrictEquality(a, b)
+ || NOT_EQUAL;
+ }
+
+ function isPrimitiveEquatable(value) {
+ switch (typeof value) {
+ case 'string':
+ case 'number':
+ case 'bigint':
+ case 'boolean':
+ case 'symbol':
+ return true;
+ default:
+ return isBoxed(value);
+ }
+ }
+
+ function comparePrimitiveEquality(a, b) {
+ if (isBoxed(a)) a = a.valueOf();
+ if (isBoxed(b)) b = b.valueOf();
+ return tryCompareStrictEquality(a, b)
+ || tryCompareTypeOfEquality(a, b)
+ || compareIf(a, b, isNaNEquatable, compareNaNEquality)
+ || NOT_EQUAL;
+ }
+
+ function isNaNEquatable(value) {
+ return typeof value === 'number';
+ }
+
+ function compareNaNEquality(a, b) {
+ return isNaN(a) && isNaN(b) ? EQUAL : NOT_EQUAL;
+ }
+
+ function isObjectEquatable(value) {
+ return typeof value === 'object';
+ }
+
+ function compareObjectEquality(a, b, cache) {
+ if (!cache) cache = new Map();
+ return getCache(cache, a, b)
+ || setCache(cache, a, b, EQUAL) // consider equal for now
+ || cacheComparison(a, b, tryCompareStrictEquality, cache)
+ || cacheComparison(a, b, tryCompareToStringTagEquality, cache)
+ || compareIf(a, b, isValueOfEquatable, compareValueOfEquality)
+ || compareIf(a, b, isToStringEquatable, compareToStringEquality)
+ || compareIf(a, b, isArrayLikeEquatable, compareArrayLikeEquality, cache)
+ || compareIf(a, b, isStructurallyEquatable, compareStructuralEquality, cache)
+ || compareIf(a, b, isIterableEquatable, compareIterableEquality, cache)
+ || cacheComparison(a, b, fail, cache);
+ }
+
+ function isBoxed(value) {
+ return value instanceof String
+ || value instanceof Number
+ || value instanceof Boolean
+ || typeof Symbol === 'function' && value instanceof Symbol
+ || typeof BigInt === 'function' && value instanceof BigInt;
+ }
+
+ function isValueOfEquatable(value) {
+ return value instanceof Date;
+ }
+
+ function compareValueOfEquality(a, b) {
+ return compareIf(a.valueOf(), b.valueOf(), isPrimitiveEquatable, comparePrimitiveEquality)
+ || NOT_EQUAL;
+ }
+
+ function isToStringEquatable(value) {
+ return value instanceof RegExp;
+ }
+
+ function compareToStringEquality(a, b) {
+ return compareIf(a.toString(), b.toString(), isPrimitiveEquatable, comparePrimitiveEquality)
+ || NOT_EQUAL;
+ }
+
+ function isArrayLikeEquatable(value) {
+ return (Array.isArray ? Array.isArray(value) : value instanceof Array)
+ || (typeof Uint8Array === 'function' && value instanceof Uint8Array)
+ || (typeof Uint8ClampedArray === 'function' && value instanceof Uint8ClampedArray)
+ || (typeof Uint16Array === 'function' && value instanceof Uint16Array)
+ || (typeof Uint32Array === 'function' && value instanceof Uint32Array)
+ || (typeof Int8Array === 'function' && value instanceof Int8Array)
+ || (typeof Int16Array === 'function' && value instanceof Int16Array)
+ || (typeof Int32Array === 'function' && value instanceof Int32Array)
+ || (typeof Float32Array === 'function' && value instanceof Float32Array)
+ || (typeof Float64Array === 'function' && value instanceof Float64Array)
+ || (typeof BigUint64Array === 'function' && value instanceof BigUint64Array)
+ || (typeof BigInt64Array === 'function' && value instanceof BigInt64Array);
+ }
+
+ function compareArrayLikeEquality(a, b, cache) {
+ if (a.length !== b.length) return NOT_EQUAL;
+ for (var i = 0; i < a.length; i++) {
+ if (compareEquality(a[i], b[i], cache) === NOT_EQUAL) {
+ return NOT_EQUAL;
+ }
+ }
+ return EQUAL;
+ }
+
+ function isStructurallyEquatable(value) {
+ return !(typeof Promise === 'function' && value instanceof Promise // only comparable by reference
+ || typeof WeakMap === 'function' && value instanceof WeakMap // only comparable by reference
+ || typeof WeakSet === 'function' && value instanceof WeakSet // only comparable by reference
+ || typeof Map === 'function' && value instanceof Map // comparable via @@iterator
+ || typeof Set === 'function' && value instanceof Set); // comparable via @@iterator
+ }
+
+ function compareStructuralEquality(a, b, cache) {
+ var aKeys = [];
+ for (var key in a) aKeys.push(key);
+
+ var bKeys = [];
+ for (var key in b) bKeys.push(key);
+
+ if (aKeys.length !== bKeys.length) {
+ return NOT_EQUAL;
+ }
+
+ aKeys.sort();
+ bKeys.sort();
+
+ for (var i = 0; i < aKeys.length; i++) {
+ var aKey = aKeys[i];
+ var bKey = bKeys[i];
+ if (compareEquality(aKey, bKey, cache) === NOT_EQUAL) {
+ return NOT_EQUAL;
+ }
+ if (compareEquality(a[aKey], b[bKey], cache) === NOT_EQUAL) {
+ return NOT_EQUAL;
+ }
+ }
+
+ return compareIf(a, b, isIterableEquatable, compareIterableEquality, cache)
+ || EQUAL;
+ }
+
+ function isIterableEquatable(value) {
+ return typeof Symbol === 'function'
+ && typeof value[Symbol.iterator] === 'function';
+ }
+
+ function compareIteratorEquality(a, b, cache) {
+ if (typeof Map === 'function' && a instanceof Map && b instanceof Map ||
+ typeof Set === 'function' && a instanceof Set && b instanceof Set) {
+ if (a.size !== b.size) return NOT_EQUAL; // exit early if we detect a difference in size
+ }
+
+ var ar, br;
+ while (true) {
+ ar = a.next();
+ br = b.next();
+ if (ar.done) {
+ if (br.done) return EQUAL;
+ if (b.return) b.return();
+ return NOT_EQUAL;
+ }
+ if (br.done) {
+ if (a.return) a.return();
+ return NOT_EQUAL;
+ }
+ if (compareEquality(ar.value, br.value, cache) === NOT_EQUAL) {
+ if (a.return) a.return();
+ if (b.return) b.return();
+ return NOT_EQUAL;
+ }
+ }
+ }
+
+ function compareIterableEquality(a, b, cache) {
+ return compareIteratorEquality(a[Symbol.iterator](), b[Symbol.iterator](), cache);
+ }
+
+ function cacheComparison(a, b, compare, cache) {
+ var result = compare(a, b, cache);
+ if (cache && (result === EQUAL || result === NOT_EQUAL)) {
+ setCache(cache, a, b, /** @type {EQUAL | NOT_EQUAL} */(result));
+ }
+ return result;
+ }
+
+ function fail() {
+ return NOT_EQUAL;
+ }
+
+ function setCache(cache, left, right, result) {
+ var otherCache;
+
+ otherCache = cache.get(left);
+ if (!otherCache) cache.set(left, otherCache = new Map());
+ otherCache.set(right, result);
+
+ otherCache = cache.get(right);
+ if (!otherCache) cache.set(right, otherCache = new Map());
+ otherCache.set(left, result);
+ }
+
+ function getCache(cache, left, right) {
+ var otherCache;
+ var result;
+
+ otherCache = cache.get(left);
+ result = otherCache && otherCache.get(right);
+ if (result) return result;
+
+ otherCache = cache.get(right);
+ result = otherCache && otherCache.get(left);
+ if (result) return result;
+
+ return UNKNOWN;
+ }
+
+ return deepEqual;
+})();
+
+// 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/PlainYearMonth/prototype/subtract/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/subclassing-ignored.js
new file mode 100644
index 0000000000..14e0e703f6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/subclassing-ignored.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.plainyearmonth.prototype.subtract
+description: Objects of a subclass are never created as return values for subtract()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.PlainYearMonth,
+ [2000, 5],
+ "subtract",
+ [{ months: 1 }],
+ (result) => TemporalHelpers.assertPlainYearMonth(result, 2000, 4, "M04"),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/branding.js
new file mode 100644
index 0000000000..01c34dadba
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/branding.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.plainyearmonth.prototype.tojson
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toJSON = Temporal.PlainYearMonth.prototype.toJSON;
+
+assert.sameValue(typeof toJSON, "function");
+
+assert.throws(TypeError, () => toJSON.call(undefined), "undefined");
+assert.throws(TypeError, () => toJSON.call(null), "null");
+assert.throws(TypeError, () => toJSON.call(true), "true");
+assert.throws(TypeError, () => toJSON.call(""), "empty string");
+assert.throws(TypeError, () => toJSON.call(Symbol()), "symbol");
+assert.throws(TypeError, () => toJSON.call(1), "1");
+assert.throws(TypeError, () => toJSON.call({}), "plain object");
+assert.throws(TypeError, () => toJSON.call(Temporal.PlainYearMonth), "Temporal.PlainYearMonth");
+assert.throws(TypeError, () => toJSON.call(Temporal.PlainYearMonth.prototype), "Temporal.PlainYearMonth.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..4468fb65e0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plainyearmonth.prototype.tojson
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const idOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "id");
+Object.defineProperty(Temporal.Calendar.prototype, "id", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("id should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainYearMonth(2000, 5, "iso8601", 1);
+instance.toJSON();
+
+Object.defineProperty(Temporal.Calendar.prototype, "id", idOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/builtin.js
new file mode 100644
index 0000000000..b33d4a9b59
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/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.plainyearmonth.prototype.tojson
+description: >
+ Tests that Temporal.PlainYearMonth.prototype.toJSON
+ 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.PlainYearMonth.prototype.toJSON),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainYearMonth.prototype.toJSON),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainYearMonth.prototype.toJSON),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainYearMonth.prototype.toJSON.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/length.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/length.js
new file mode 100644
index 0000000000..89b7904e89
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/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.plainyearmonth.prototype.tojson
+description: Temporal.PlainYearMonth.prototype.toJSON.length is 0
+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.PlainYearMonth.prototype.toJSON, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/name.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/name.js
new file mode 100644
index 0000000000..9e761d32a9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/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.plainyearmonth.prototype.tojson
+description: Temporal.PlainYearMonth.prototype.toJSON.name is "toJSON".
+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.PlainYearMonth.prototype.toJSON, "name", {
+ value: "toJSON",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/not-a-constructor.js
new file mode 100644
index 0000000000..213d7d5e7a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/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.plainyearmonth.prototype.tojson
+description: >
+ Temporal.PlainYearMonth.prototype.toJSON 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.PlainYearMonth.prototype.toJSON();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainYearMonth.prototype.toJSON), false,
+ "isConstructor(Temporal.PlainYearMonth.prototype.toJSON)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/prop-desc.js
new file mode 100644
index 0000000000..326cedaeab
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/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.plainyearmonth.prototype.tojson
+description: The "toJSON" property of Temporal.PlainYearMonth.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainYearMonth.prototype.toJSON,
+ "function",
+ "`typeof PlainYearMonth.prototype.toJSON` is `function`"
+);
+
+verifyProperty(Temporal.PlainYearMonth.prototype, "toJSON", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/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/PlainYearMonth/prototype/toJSON/year-format.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/year-format.js
new file mode 100644
index 0000000000..c988038991
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/year-format.js
@@ -0,0 +1,50 @@
+// |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.plainyearmonth.prototype.tojson
+description: Verify that the year is appropriately formatted as 4 or 6 digits
+features: [Temporal]
+---*/
+
+let instance = new Temporal.PlainYearMonth(-100000, 12);
+assert.sameValue(instance.toJSON(), "-100000-12", "large negative year formatted as 6-digit");
+
+instance = new Temporal.PlainYearMonth(-10000, 4);
+assert.sameValue(instance.toJSON(), "-010000-04", "smallest 5-digit negative year formatted as 6-digit");
+
+instance = new Temporal.PlainYearMonth(-9999, 6);
+assert.sameValue(instance.toJSON(), "-009999-06", "largest 4-digit negative year formatted as 6-digit");
+
+instance = new Temporal.PlainYearMonth(-1000, 8);
+assert.sameValue(instance.toJSON(), "-001000-08", "smallest 4-digit negative year formatted as 6-digit");
+
+instance = new Temporal.PlainYearMonth(-999, 10);
+assert.sameValue(instance.toJSON(), "-000999-10", "largest 3-digit negative year formatted as 6-digit");
+
+instance = new Temporal.PlainYearMonth(-1, 8);
+assert.sameValue(instance.toJSON(), "-000001-08", "year -1 formatted as 6-digit");
+
+instance = new Temporal.PlainYearMonth(0, 6);
+assert.sameValue(instance.toJSON(), "0000-06", "year 0 formatted as 4-digit");
+
+instance = new Temporal.PlainYearMonth(1, 4);
+assert.sameValue(instance.toJSON(), "0001-04", "year 1 formatted as 4-digit");
+
+instance = new Temporal.PlainYearMonth(999, 2);
+assert.sameValue(instance.toJSON(), "0999-02", "largest 3-digit positive year formatted as 4-digit");
+
+instance = new Temporal.PlainYearMonth(1000, 1);
+assert.sameValue(instance.toJSON(), "1000-01", "smallest 4-digit positive year formatted as 4-digit");
+
+instance = new Temporal.PlainYearMonth(9999, 4);
+assert.sameValue(instance.toJSON(), "9999-04", "largest 4-digit positive year formatted as 4-digit");
+
+instance = new Temporal.PlainYearMonth(10000, 6);
+assert.sameValue(instance.toJSON(), "+010000-06", "smallest 5-digit positive year formatted as 6-digit");
+
+instance = new Temporal.PlainYearMonth(100000, 8);
+assert.sameValue(instance.toJSON(), "+100000-08", "large positive year formatted as 6-digit");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/branding.js
new file mode 100644
index 0000000000..3effa5b45c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/branding.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.plainyearmonth.prototype.tolocalestring
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toLocaleString = Temporal.PlainYearMonth.prototype.toLocaleString;
+
+assert.sameValue(typeof toLocaleString, "function");
+
+assert.throws(TypeError, () => toLocaleString.call(undefined), "undefined");
+assert.throws(TypeError, () => toLocaleString.call(null), "null");
+assert.throws(TypeError, () => toLocaleString.call(true), "true");
+assert.throws(TypeError, () => toLocaleString.call(""), "empty string");
+assert.throws(TypeError, () => toLocaleString.call(Symbol()), "symbol");
+assert.throws(TypeError, () => toLocaleString.call(1), "1");
+assert.throws(TypeError, () => toLocaleString.call({}), "plain object");
+assert.throws(TypeError, () => toLocaleString.call(Temporal.PlainYearMonth), "Temporal.PlainYearMonth");
+assert.throws(TypeError, () => toLocaleString.call(Temporal.PlainYearMonth.prototype), "Temporal.PlainYearMonth.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..ed40d63388
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plainyearmonth.prototype.tolocalestring
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const idOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "id");
+Object.defineProperty(Temporal.Calendar.prototype, "id", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("id should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainYearMonth(2000, 5, "iso8601", 1);
+instance.toLocaleString(undefined, { calendar: "iso8601" });
+
+Object.defineProperty(Temporal.Calendar.prototype, "id", idOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/builtin.js
new file mode 100644
index 0000000000..ae655de729
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/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.plainyearmonth.prototype.tolocalestring
+description: >
+ Tests that Temporal.PlainYearMonth.prototype.toLocaleString
+ 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.PlainYearMonth.prototype.toLocaleString),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainYearMonth.prototype.toLocaleString),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainYearMonth.prototype.toLocaleString),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainYearMonth.prototype.toLocaleString.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/length.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/length.js
new file mode 100644
index 0000000000..ff34afa4b5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/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.plainyearmonth.prototype.tolocalestring
+description: Temporal.PlainYearMonth.prototype.toLocaleString.length is 0
+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.PlainYearMonth.prototype.toLocaleString, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/name.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/name.js
new file mode 100644
index 0000000000..eceae6dcd1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/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.plainyearmonth.prototype.tolocalestring
+description: Temporal.PlainYearMonth.prototype.toLocaleString.name is "toLocaleString".
+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.PlainYearMonth.prototype.toLocaleString, "name", {
+ value: "toLocaleString",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/not-a-constructor.js
new file mode 100644
index 0000000000..7cab6ecebe
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/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.plainyearmonth.prototype.tolocalestring
+description: >
+ Temporal.PlainYearMonth.prototype.toLocaleString 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.PlainYearMonth.prototype.toLocaleString();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainYearMonth.prototype.toLocaleString), false,
+ "isConstructor(Temporal.PlainYearMonth.prototype.toLocaleString)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/prop-desc.js
new file mode 100644
index 0000000000..9d35419db3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/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.plainyearmonth.prototype.tolocalestring
+description: The "toLocaleString" property of Temporal.PlainYearMonth.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainYearMonth.prototype.toLocaleString,
+ "function",
+ "`typeof PlainYearMonth.prototype.toLocaleString` is `function`"
+);
+
+verifyProperty(Temporal.PlainYearMonth.prototype, "toLocaleString", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/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/PlainYearMonth/prototype/toPlainDate/argument-not-object.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/argument-not-object.js
new file mode 100644
index 0000000000..e97cf63771
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/argument-not-object.js
@@ -0,0 +1,18 @@
+// |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.plainyearmonth.prototype.toplaindate
+description: Throws a TypeError if the argument is not an Object, before any other observable actions
+includes: [compareArray.js, temporalHelpers.js]
+features: [BigInt, Symbol, Temporal]
+---*/
+
+[null, undefined, true, 3.1416, "a string", Symbol("symbol"), 7n].forEach((primitive) => {
+ const calendar = TemporalHelpers.calendarThrowEverything();
+ const plainYearMonth = new Temporal.PlainYearMonth(2000, 5, calendar);
+ assert.throws(TypeError, () => plainYearMonth.toPlainDate(primitive));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/basic.js
new file mode 100644
index 0000000000..904e36750c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/basic.js
@@ -0,0 +1,16 @@
+// |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.plainyearmonth.prototype.toplaindate
+description: Basic check for toPlainDate()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const ym = Temporal.PlainYearMonth.from("2002-01");
+TemporalHelpers.assertPlainDate(ym.toPlainDate({ day: 22 }), 2002, 1, "M01", 22);
+assert.throws(TypeError, () => ym.toPlainDate({ something: "nothing" }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/branding.js
new file mode 100644
index 0000000000..40902ba67c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/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.plainyearmonth.prototype.toplaindate
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toPlainDate = Temporal.PlainYearMonth.prototype.toPlainDate;
+
+assert.sameValue(typeof toPlainDate, "function");
+
+const args = [{ day: 7 }];
+
+assert.throws(TypeError, () => toPlainDate.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => toPlainDate.apply(null, args), "null");
+assert.throws(TypeError, () => toPlainDate.apply(true, args), "true");
+assert.throws(TypeError, () => toPlainDate.apply("", args), "empty string");
+assert.throws(TypeError, () => toPlainDate.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => toPlainDate.apply(1, args), "1");
+assert.throws(TypeError, () => toPlainDate.apply({}, args), "plain object");
+assert.throws(TypeError, () => toPlainDate.apply(Temporal.PlainYearMonth, args), "Temporal.PlainYearMonth");
+assert.throws(TypeError, () => toPlainDate.apply(Temporal.PlainYearMonth.prototype, args), "Temporal.PlainYearMonth.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..470d1d697e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/builtin-calendar-no-array-iteration.js
@@ -0,0 +1,23 @@
+// |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.plainyearmonth.prototype.toplaindate
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.PlainYearMonth(2023, 5, "iso8601");
+instance.toPlainDate({ day: 5 });
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..f271fe42cc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,46 @@
+// |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.plainyearmonth.prototype.toplaindate
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const fieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "fields");
+Object.defineProperty(Temporal.Calendar.prototype, "fields", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("fields should not be looked up");
+ },
+});
+const mergeFieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "mergeFields");
+Object.defineProperty(Temporal.Calendar.prototype, "mergeFields", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("mergeFields should not be looked up");
+ },
+});
+const dateFromFieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dateFromFields");
+Object.defineProperty(Temporal.Calendar.prototype, "dateFromFields", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("dateFromFields should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainYearMonth(2000, 5, "iso8601", 1);
+instance.toPlainDate({ day: 12 });
+
+Object.defineProperty(Temporal.Calendar.prototype, "fields", fieldsOriginal);
+Object.defineProperty(Temporal.Calendar.prototype, "mergeFields", mergeFieldsOriginal);
+Object.defineProperty(Temporal.Calendar.prototype, "dateFromFields", dateFromFieldsOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/builtin.js
new file mode 100644
index 0000000000..4017d9b961
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/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.plainyearmonth.prototype.toplaindate
+description: >
+ Tests that Temporal.PlainYearMonth.prototype.toPlainDate
+ 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.PlainYearMonth.prototype.toPlainDate),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainYearMonth.prototype.toPlainDate),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainYearMonth.prototype.toPlainDate),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainYearMonth.prototype.toPlainDate.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/calendar-fields-iterable.js
new file mode 100644
index 0000000000..76bfe5946d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/calendar-fields-iterable.js
@@ -0,0 +1,37 @@
+// |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.plainyearmonth.prototype.toplaindate
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plainyearmonth.prototype.toplaindate step 5:
+ 5. Let _receiverFieldNames_ be ? CalendarFields(_calendar_, « *"monthCode"*, *"year"* »).
+ sec-temporal.plainyearmonth.prototype.toplaindate step 7:
+ 7. Let _inputFieldNames_ be ? CalendarFields(_calendar_, « *"day"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected1 = [
+ "monthCode",
+ "year",
+];
+const expected2 = [
+ "day",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+const yearmonth = new Temporal.PlainYearMonth(2000, 5, calendar);
+yearmonth.toPlainDate({ day: 15 });
+
+assert.sameValue(calendar.fieldsCallCount, 2, "fields() method called twice");
+assert.compareArray(calendar.fieldsCalledWith[0], expected1, "fields() method called first time with correct args");
+assert.compareArray(calendar.fieldsCalledWith[1], expected2, "fields() method called second time with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole first iterable");
+assert(calendar.iteratorExhausted[1], "iterated through the whole second iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/calendar-fromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/calendar-fromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..0830c5b67c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/calendar-fromfields-called-with-null-prototype-fields.js
@@ -0,0 +1,18 @@
+// |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.plainyearmonth.prototype.toplaindate
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.PlainYearMonth(2000, 5, calendar);
+instance.toPlainDate({ day: 24 });
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should have been called on the calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/calendar-merge-fields-returns-primitive.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/calendar-merge-fields-returns-primitive.js
new file mode 100644
index 0000000000..60f61d7a64
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/calendar-merge-fields-returns-primitive.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.plainyearmonth.prototype.toplaindate
+description: >
+ with() should throw a TypeError if mergeFields() returns a primitive,
+ without passing the value on to any other calendar methods
+includes: [compareArray.js, temporalHelpers.js]
+features: [BigInt, Symbol, Temporal]
+---*/
+
+[undefined, null, true, 3.14159, "bad value", Symbol("no"), 7n].forEach((primitive) => {
+ const calendar = TemporalHelpers.calendarMergeFieldsReturnsPrimitive(primitive);
+ const instance = new Temporal.PlainYearMonth(2000, 5, calendar);
+ assert.throws(TypeError, () => instance.toPlainDate({ day: 2 }), "bad return from mergeFields() throws");
+ assert.sameValue(calendar.dateFromFieldsCallCount, 0, "dateFromFields() never called");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/calendar-mergefields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/calendar-mergefields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..5b420c1d69
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/calendar-mergefields-called-with-null-prototype-fields.js
@@ -0,0 +1,18 @@
+// |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.plainyearmonth.prototype.toplaindate
+description: >
+ Calendar.mergeFields method is called with null-prototype fields objects
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckMergeFieldsPrototypePollution();
+const instance = new Temporal.PlainYearMonth(2000, 5, calendar);
+instance.toPlainDate({ day: 24 });
+assert.sameValue(calendar.mergeFieldsCallCount, 1, "mergeFields should have been called on the calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..0bf6ede55d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/constructor-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |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.plainyearmonth.prototype.toplaindate
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const ym = new Temporal.PlainYearMonth(2023, 5, calendar);
+
+assert.throws(RangeError, () => ym.toPlainDate({day: 1}));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/copies-merge-fields-object.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/copies-merge-fields-object.js
new file mode 100644
index 0000000000..e786b86e42
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/copies-merge-fields-object.js
@@ -0,0 +1,34 @@
+// |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.plainyearmonth.prototype.toplaindate
+description: The object returned from mergeFields() is copied before being passed to monthDayFromFields().
+info: |
+ sec-temporal.plainyearmonth.prototype.toplaindate steps 9 and 11:
+ 9. Let _mergedFields_ be ? CalendarMergeFields(_calendar_, _fields_, _inputFields_).
+ 11. Set _mergedFields_ to ? PrepareTemporalFields(_mergedFields_, _mergedFieldNames_, «»).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "get day",
+ "get day.valueOf",
+ "call day.valueOf",
+ "get monthCode",
+ "get monthCode.toString",
+ "call monthCode.toString",
+ "get year",
+ "get year.valueOf",
+ "call year.valueOf",
+];
+
+const calendar = TemporalHelpers.calendarMergeFieldsGetters();
+const yearmonth = new Temporal.PlainYearMonth(2000, 5, calendar);
+yearmonth.toPlainDate({ day: 2 });
+
+assert.compareArray(calendar.mergeFieldsReturnOperations, expected, "getters called on mergeFields return");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/default-overflow-behaviour.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/default-overflow-behaviour.js
new file mode 100644
index 0000000000..6cc15dadd2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/default-overflow-behaviour.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.plainyearmonth.prototype.toplaindate
+description: A nonexistent resulting date is constrained to an existing date
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const febCommonYear = new Temporal.PlainYearMonth(2023, 2);
+const result = febCommonYear.toPlainDate({ day: 29 });
+// 2023-02-29 does not exist because 2023 is a common year
+TemporalHelpers.assertPlainDate(result, 2023, 2, "M02", 28, "2023-02 + 29 = 2023-02-28");
+
+const juneAnyYear = new Temporal.PlainYearMonth(1998, 6);
+const result2 = juneAnyYear.toPlainDate({ day: 31 });
+// 06-31 does not exist in any year
+TemporalHelpers.assertPlainDate(result2, 1998, 6, "M06", 30, "1998-06 + 31 = 1998-06-31");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/duplicate-calendar-fields.js
new file mode 100644
index 0000000000..13243c741f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/duplicate-calendar-fields.js
@@ -0,0 +1,18 @@
+// |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.plainyearmonth.prototype.toplaindate
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['monthCode'], ['year'], ['day']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const ym = new Temporal.PlainYearMonth(2023, 5, calendar);
+
+ assert.throws(RangeError, () => ym.toPlainDate({day: 1}));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..327232c113
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/infinity-throws-rangeerror.js
@@ -0,0 +1,23 @@
+// |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.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plainyearmonth.prototype.toplaindate
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2000, 5);
+
+[Infinity, -Infinity].forEach((inf) => {
+ assert.throws(RangeError, () => instance.toPlainDate({ day: inf }), `day property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, "day");
+ assert.throws(RangeError, () => instance.toPlainDate({ day: obj }));
+ assert.compareArray(calls, ["get day.valueOf", "call day.valueOf"], "it fails after fetching the primitive value");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/length.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/length.js
new file mode 100644
index 0000000000..27fab0fa9e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/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.plainyearmonth.prototype.toplaindate
+description: Temporal.PlainYearMonth.prototype.toPlainDate.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.PlainYearMonth.prototype.toPlainDate, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/limits.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/limits.js
new file mode 100644
index 0000000000..78fc602c1e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/limits.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.plainyearmonth.prototype.toplaindate
+description: Throws a RangeError if the resulting PlainDate is out of range
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const min = Temporal.PlainYearMonth.from("-271821-04");
+assert.throws(RangeError, () => min.toPlainDate({ day: 18 }), "min");
+TemporalHelpers.assertPlainDate(min.toPlainDate({ day: 19 }),
+ -271821, 4, "M04", 19, "min");
+
+const max = Temporal.PlainYearMonth.from("+275760-09");
+assert.throws(RangeError, () => max.toPlainDate({ day: 14 }), "max");
+TemporalHelpers.assertPlainDate(max.toPlainDate({ day: 13 }),
+ 275760, 9, "M09", 13, "max");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/name.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/name.js
new file mode 100644
index 0000000000..b40f6b1e63
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/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.plainyearmonth.prototype.toplaindate
+description: Temporal.PlainYearMonth.prototype.toPlainDate.name is "toPlainDate".
+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.PlainYearMonth.prototype.toPlainDate, "name", {
+ value: "toPlainDate",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/not-a-constructor.js
new file mode 100644
index 0000000000..9c6fe3cc19
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/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.plainyearmonth.prototype.toplaindate
+description: >
+ Temporal.PlainYearMonth.prototype.toPlainDate 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.PlainYearMonth.prototype.toPlainDate();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainYearMonth.prototype.toPlainDate), false,
+ "isConstructor(Temporal.PlainYearMonth.prototype.toPlainDate)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/prop-desc.js
new file mode 100644
index 0000000000..4cbf1717e0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/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.plainyearmonth.prototype.toplaindate
+description: The "toPlainDate" property of Temporal.PlainYearMonth.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainYearMonth.prototype.toPlainDate,
+ "function",
+ "`typeof PlainYearMonth.prototype.toPlainDate` is `function`"
+);
+
+verifyProperty(Temporal.PlainYearMonth.prototype, "toPlainDate", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/proto-in-calendar-fields.js
new file mode 100644
index 0000000000..cd560586d2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/proto-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |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.plainyearmonth.prototype.toplaindate
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const ym = new Temporal.PlainYearMonth(2023, 5, calendar);
+
+assert.throws(RangeError, () => ym.toPlainDate({day: 1}));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/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/PlainYearMonth/prototype/toString/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/branding.js
new file mode 100644
index 0000000000..aea563d4a2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/branding.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.plainyearmonth.prototype.tostring
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toString = Temporal.PlainYearMonth.prototype.toString;
+
+assert.sameValue(typeof toString, "function");
+
+assert.throws(TypeError, () => toString.call(undefined), "undefined");
+assert.throws(TypeError, () => toString.call(null), "null");
+assert.throws(TypeError, () => toString.call(true), "true");
+assert.throws(TypeError, () => toString.call(""), "empty string");
+assert.throws(TypeError, () => toString.call(Symbol()), "symbol");
+assert.throws(TypeError, () => toString.call(1), "1");
+assert.throws(TypeError, () => toString.call({}), "plain object");
+assert.throws(TypeError, () => toString.call(Temporal.PlainYearMonth), "Temporal.PlainYearMonth");
+assert.throws(TypeError, () => toString.call(Temporal.PlainYearMonth.prototype), "Temporal.PlainYearMonth.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..c10fcdbef1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plainyearmonth.prototype.tostring
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const idOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "id");
+Object.defineProperty(Temporal.Calendar.prototype, "id", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("id should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainYearMonth(2000, 5, "iso8601", 1);
+instance.toString();
+
+Object.defineProperty(Temporal.Calendar.prototype, "id", idOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/builtin.js
new file mode 100644
index 0000000000..54557224d4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/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.plainyearmonth.prototype.tostring
+description: >
+ Tests that Temporal.PlainYearMonth.prototype.toString
+ 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.PlainYearMonth.prototype.toString),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainYearMonth.prototype.toString),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainYearMonth.prototype.toString),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainYearMonth.prototype.toString.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/calendar-tostring.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/calendar-tostring.js
new file mode 100644
index 0000000000..60e9b1fd9b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/calendar-tostring.js
@@ -0,0 +1,56 @@
+// |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.plainyearmonth.protoype.tostring
+description: Number of observable 'toString' calls on the calendar for each value of calendarName
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+let calls;
+const customCalendar = {
+ get id() {
+ ++calls;
+ return "custom";
+ },
+ toString() {
+ TemporalHelpers.assertUnreachable('toString should not be called');
+ },
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+const yearmonth = new Temporal.PlainYearMonth(2000, 5, customCalendar);
+[
+ ["always", "2000-05-01[u-ca=custom]", 1],
+ ["auto", "2000-05-01[u-ca=custom]", 1],
+ ["critical", "2000-05-01[!u-ca=custom]", 1],
+ ["never", "2000-05-01", 1],
+ [undefined, "2000-05-01[u-ca=custom]", 1],
+].forEach(([calendarName, expectedResult, expectedCalls]) => {
+ calls = 0;
+ const result = yearmonth.toString({ calendarName });
+ assert.sameValue(result, expectedResult, `id for calendarName = ${calendarName}`);
+ assert.sameValue(calls, expectedCalls, `calls to id getter for calendarName = ${calendarName}`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-always.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-always.js
new file mode 100644
index 0000000000..5736e96050
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-always.js
@@ -0,0 +1,48 @@
+// |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.plainyearmonth.prototype.tostring
+description: If calendarName is "always", the calendar ID should be included.
+features: [Temporal]
+---*/
+
+const calendarMethods = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+
+const tests = [
+ [[], "2000-05-01[u-ca=iso8601]", "built-in ISO"],
+ [[{ id: "custom", ...calendarMethods }], "2000-05-01[u-ca=custom]", "custom"],
+ [[{ id: "iso8601", ...calendarMethods }], "2000-05-01[u-ca=iso8601]", "custom with iso8601 id"],
+ [[{ id: "ISO8601", ...calendarMethods }], "2000-05-01[u-ca=ISO8601]", "custom with caps id"],
+ [[{ id: "\u0131so8601", ...calendarMethods }], "2000-05-01[u-ca=\u0131so8601]", "custom with dotless i id"],
+];
+
+for (const [args, expected, description] of tests) {
+ const yearmonth = new Temporal.PlainYearMonth(2000, 5, ...args);
+ const result = yearmonth.toString({ calendarName: "always" });
+ assert.sameValue(result, expected, `${description} calendar for calendarName = always`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-auto.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-auto.js
new file mode 100644
index 0000000000..30c4f108f5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-auto.js
@@ -0,0 +1,48 @@
+// |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.plainyearmonth.prototype.tostring
+description: If calendarName is "auto", "iso8601" should be omitted.
+features: [Temporal]
+---*/
+
+const calendarMethods = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+
+const tests = [
+ [[], "2000-05", "built-in ISO"],
+ [[{ id: "custom", ...calendarMethods }], "2000-05-01[u-ca=custom]", "custom"],
+ [[{ id: "iso8601", ...calendarMethods }], "2000-05", "custom with iso8601 id"],
+ [[{ id: "ISO8601", ...calendarMethods }], "2000-05-01[u-ca=ISO8601]", "custom with caps id"],
+ [[{ id: "\u0131so8601", ...calendarMethods }], "2000-05-01[u-ca=\u0131so8601]", "custom with dotless i id"],
+];
+
+for (const [args, expected, description] of tests) {
+ const yearmonth = new Temporal.PlainYearMonth(2000, 5, ...args);
+ const result = yearmonth.toString({ calendarName: "auto" });
+ assert.sameValue(result, expected, `${description} calendar for calendarName = auto`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-critical.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-critical.js
new file mode 100644
index 0000000000..108bbe3a60
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-critical.js
@@ -0,0 +1,50 @@
+// |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.plainyearmonth.prototype.tostring
+description: >
+ If calendarName is "calendar", the calendar ID should be included and prefixed
+ with "!".
+features: [Temporal]
+---*/
+
+const calendarMethods = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+
+const tests = [
+ [[], "2000-05-01[!u-ca=iso8601]", "built-in ISO"],
+ [[{ id: "custom", ...calendarMethods }], "2000-05-01[!u-ca=custom]", "custom"],
+ [[{ id: "iso8601", ...calendarMethods }], "2000-05-01[!u-ca=iso8601]", "custom with iso8601 id"],
+ [[{ id: "ISO8601", ...calendarMethods }], "2000-05-01[!u-ca=ISO8601]", "custom with caps id"],
+ [[{ id: "\u0131so8601", ...calendarMethods }], "2000-05-01[!u-ca=\u0131so8601]", "custom with dotless i id"],
+];
+
+for (const [args, expected, description] of tests) {
+ const yearmonth = new Temporal.PlainYearMonth(2000, 5, ...args);
+ const result = yearmonth.toString({ calendarName: "critical" });
+ assert.sameValue(result, expected, `${description} calendar for calendarName = critical`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-invalid-string.js
new file mode 100644
index 0000000000..9a940adaf5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-invalid-string.js
@@ -0,0 +1,29 @@
+// |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.plainyearmonth.protoype.tostring
+description: RangeError thrown when calendarName option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-toshowcalendaroption step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"calendarName"*, « *"string"* », « *"auto"*, *"always"*, *"never"*, *"critical"* », *"auto"*).
+ sec-temporal.plainyearmonth.protoype.tostring step 4:
+ 4. Let _showCalendar_ be ? ToShowCalendarOption(_options_).
+features: [Temporal]
+---*/
+
+const yearmonth = new Temporal.PlainYearMonth(2000, 5);
+const invalidValues = ["ALWAYS", "sometimes", "other string", "auto\0"];
+
+for (const calendarName of invalidValues) {
+ assert.throws(
+ RangeError,
+ () => yearmonth.toString({ calendarName }),
+ `${calendarName} is an invalid value for calendarName option`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-never.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-never.js
new file mode 100644
index 0000000000..c73c8ec551
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-never.js
@@ -0,0 +1,48 @@
+// |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.plainyearmonth.prototype.tostring
+description: If calendarName is "never", the calendar ID should be omitted.
+features: [Temporal]
+---*/
+
+const calendarMethods = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+
+const tests = [
+ [[], "2000-05", "built-in ISO"],
+ [[{ id: "custom", ...calendarMethods }], "2000-05-01", "custom"],
+ [[{ id: "iso8601", ...calendarMethods }], "2000-05", "custom with iso8601 id"],
+ [[{ id: "ISO8601", ...calendarMethods }], "2000-05-01", "custom with caps id"],
+ [[{ id: "\u0131so8601", ...calendarMethods }], "2000-05-01", "custom with dotless i id"],
+];
+
+for (const [args, expected, description] of tests) {
+ const yearmonth = new Temporal.PlainYearMonth(2000, 5, ...args);
+ const result = yearmonth.toString({ calendarName: "never" });
+ assert.sameValue(result, expected, `${description} calendar for calendarName = never`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-undefined.js
new file mode 100644
index 0000000000..af3159efb9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-undefined.js
@@ -0,0 +1,56 @@
+// |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.plainyearmonth.protoype.tostring
+description: Fallback value for calendarName option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-toshowcalendaroption step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"calendarName"*, « *"string"* », « *"auto"*, *"always"*, *"never"*, *"critical"* », *"auto"*).
+ sec-temporal.plainyearmonth.protoype.tostring step 4:
+ 4. Let _showCalendar_ be ? ToShowCalendarOption(_options_).
+features: [Temporal]
+---*/
+
+const calendarMethods = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+
+const tests = [
+ [[], "2000-05", "built-in ISO"],
+ [[{ id: "custom", ...calendarMethods }], "2000-05-01[u-ca=custom]", "custom"],
+ [[{ id: "iso8601", ...calendarMethods }], "2000-05", "custom with iso8601 id"],
+ [[{ id: "ISO8601", ...calendarMethods }], "2000-05-01[u-ca=ISO8601]", "custom with caps id"],
+ [[{ id: "\u0131so8601", ...calendarMethods }], "2000-05-01[u-ca=\u0131so8601]", "custom with dotless i id"],
+];
+
+for (const [args, expected, description] of tests) {
+ const yearmonth = new Temporal.PlainYearMonth(2000, 5, ...args);
+ const result = yearmonth.toString({ calendarName: undefined });
+ assert.sameValue(result, expected, `default calendarName option is auto with ${description} calendar`);
+ // See options-object.js for {} and options-undefined.js for absent options arg
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-wrong-type.js
new file mode 100644
index 0000000000..7370fa065f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-wrong-type.js
@@ -0,0 +1,49 @@
+// |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.plainyearmonth.protoype.tostring
+description: Type conversions for calendarName option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-toshowcalendaroption step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"calendarName"*, « *"string"* », « *"auto"*, *"always"*, *"never"*, *"critical"* », *"auto"*).
+ sec-temporal.plainyearmonth.protoype.tostring step 4:
+ 4. Let _showCalendar_ be ? ToShowCalendarOption(_options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = {
+ id: "custom",
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+const yearmonth = new Temporal.PlainYearMonth(2000, 5, calendar);
+
+TemporalHelpers.checkStringOptionWrongType("calendarName", "auto",
+ (calendarName) => yearmonth.toString({ calendarName }),
+ (result, descr) => assert.sameValue(result, "2000-05-01[u-ca=custom]", descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/length.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/length.js
new file mode 100644
index 0000000000..cd1685cbce
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/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.plainyearmonth.prototype.tostring
+description: Temporal.PlainYearMonth.prototype.toString.length is 0
+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.PlainYearMonth.prototype.toString, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/name.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/name.js
new file mode 100644
index 0000000000..1a70a1464f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/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.plainyearmonth.prototype.tostring
+description: Temporal.PlainYearMonth.prototype.toString.name is "toString".
+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.PlainYearMonth.prototype.toString, "name", {
+ value: "toString",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/not-a-constructor.js
new file mode 100644
index 0000000000..6e1e7ddf03
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/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.plainyearmonth.prototype.tostring
+description: >
+ Temporal.PlainYearMonth.prototype.toString 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.PlainYearMonth.prototype.toString();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainYearMonth.prototype.toString), false,
+ "isConstructor(Temporal.PlainYearMonth.prototype.toString)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/options-object.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/options-object.js
new file mode 100644
index 0000000000..e4d36a0f37
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/options-object.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.plainyearmonth.prototype.tostring
+description: Empty or a function object may be used as options
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2019, 10);
+
+const result1 = instance.toString({});
+assert.sameValue(
+ result1, "2019-10",
+ "options may be an empty plain object"
+);
+
+const result2 = instance.toString(() => {});
+assert.sameValue(
+ result2, "2019-10",
+ "options may be a function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/options-undefined.js
new file mode 100644
index 0000000000..13b358188c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/options-undefined.js
@@ -0,0 +1,48 @@
+// |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.plainyearmonth.prototype.tostring
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const calendar = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "custom",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+const yearmonth1 = new Temporal.PlainYearMonth(2000, 5);
+const yearmonth2 = new Temporal.PlainYearMonth(2000, 5, calendar);
+
+[
+ [yearmonth1, "2000-05"],
+ [yearmonth2, "2000-05-01[u-ca=custom]"],
+].forEach(([yearmonth, expected]) => {
+ const explicit = yearmonth.toString(undefined);
+ assert.sameValue(explicit, expected, "default calendarName option is auto");
+
+ const implicit = yearmonth.toString();
+ assert.sameValue(implicit, expected, "default calendarName option is auto");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/options-wrong-type.js
new file mode 100644
index 0000000000..0c698aa19e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/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.plainyearmonth.prototype.tostring
+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.PlainYearMonth(2019, 10);
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.toString(value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/order-of-operations.js
new file mode 100644
index 0000000000..3dd5392f8e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/order-of-operations.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.plainyearmonth.prototype.tostring
+description: Properties on an object passed to toString() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "get options.calendarName",
+ "get options.calendarName.toString",
+ "call options.calendarName.toString",
+ "get this.calendar.id",
+];
+const actual = [];
+
+const calendar = TemporalHelpers.calendarObserver(actual, "this.calendar");
+const instance = new Temporal.PlainYearMonth(2000, 5, calendar);
+// clear observable operations that occurred during the constructor call
+actual.splice(0);
+
+const options = TemporalHelpers.propertyBagObserver(actual, {
+ calendarName: "auto",
+}, "options");
+
+instance.toString(options);
+assert.compareArray(actual, expected, "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/prop-desc.js
new file mode 100644
index 0000000000..c741e7538b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/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.plainyearmonth.prototype.tostring
+description: The "toString" property of Temporal.PlainYearMonth.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainYearMonth.prototype.toString,
+ "function",
+ "`typeof PlainYearMonth.prototype.toString` is `function`"
+);
+
+verifyProperty(Temporal.PlainYearMonth.prototype, "toString", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/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/PlainYearMonth/prototype/toString/year-format.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/year-format.js
new file mode 100644
index 0000000000..f9c1876054
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/year-format.js
@@ -0,0 +1,50 @@
+// |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.plainyearmonth.prototype.tostring
+description: Verify that the year is appropriately formatted as 4 or 6 digits
+features: [Temporal]
+---*/
+
+let instance = new Temporal.PlainYearMonth(-100000, 12);
+assert.sameValue(instance.toString(), "-100000-12", "large negative year formatted as 6-digit");
+
+instance = new Temporal.PlainYearMonth(-10000, 4);
+assert.sameValue(instance.toString(), "-010000-04", "smallest 5-digit negative year formatted as 6-digit");
+
+instance = new Temporal.PlainYearMonth(-9999, 6);
+assert.sameValue(instance.toString(), "-009999-06", "largest 4-digit negative year formatted as 6-digit");
+
+instance = new Temporal.PlainYearMonth(-1000, 8);
+assert.sameValue(instance.toString(), "-001000-08", "smallest 4-digit negative year formatted as 6-digit");
+
+instance = new Temporal.PlainYearMonth(-999, 10);
+assert.sameValue(instance.toString(), "-000999-10", "largest 3-digit negative year formatted as 6-digit");
+
+instance = new Temporal.PlainYearMonth(-1, 8);
+assert.sameValue(instance.toString(), "-000001-08", "year -1 formatted as 6-digit");
+
+instance = new Temporal.PlainYearMonth(0, 6);
+assert.sameValue(instance.toString(), "0000-06", "year 0 formatted as 4-digit");
+
+instance = new Temporal.PlainYearMonth(1, 4);
+assert.sameValue(instance.toString(), "0001-04", "year 1 formatted as 4-digit");
+
+instance = new Temporal.PlainYearMonth(999, 2);
+assert.sameValue(instance.toString(), "0999-02", "largest 3-digit positive year formatted as 4-digit");
+
+instance = new Temporal.PlainYearMonth(1000, 1);
+assert.sameValue(instance.toString(), "1000-01", "smallest 4-digit positive year formatted as 4-digit");
+
+instance = new Temporal.PlainYearMonth(9999, 4);
+assert.sameValue(instance.toString(), "9999-04", "largest 4-digit positive year formatted as 4-digit");
+
+instance = new Temporal.PlainYearMonth(10000, 6);
+assert.sameValue(instance.toString(), "+010000-06", "smallest 5-digit positive year formatted as 6-digit");
+
+instance = new Temporal.PlainYearMonth(100000, 8);
+assert.sameValue(instance.toString(), "+100000-08", "large positive year formatted as 6-digit");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toStringTag/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toStringTag/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toStringTag/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toStringTag/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toStringTag/prop-desc.js
new file mode 100644
index 0000000000..12c51f56d0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toStringTag/prop-desc.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.plainyearmonth.prototype-@@tostringtag
+description: The @@toStringTag property of Temporal.PlainYearMonth
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainYearMonth.prototype, Symbol.toStringTag, {
+ value: "Temporal.PlainYearMonth",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toStringTag/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toStringTag/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toStringTag/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..071b4b5311
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-builtin-calendar-no-array-iteration.js
@@ -0,0 +1,24 @@
+// |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.plainyearmonth.prototype.until
+description: >
+ Calling the method with a property bag argument with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.PlainYearMonth(2000, 5);
+const arg = { year: 2000, month: 5, calendar: "iso8601" };
+instance.until(arg);
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-casting.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-casting.js
new file mode 100644
index 0000000000..1762b1bf2b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-casting.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.plainyearmonth.prototype.until
+description: Calls to PYM.until cast arguments.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const nov94 = new Temporal.PlainYearMonth(1994, 11);
+const jun13 = new Temporal.PlainYearMonth(2013, 6);
+const diff = nov94.until(jun13);
+
+TemporalHelpers.assertDurationsEqual(nov94.until({ year: 2013, month: 6 }), diff, "Casts object argument");
+TemporalHelpers.assertDurationsEqual(nov94.until("2013-06"), diff, "Casts string argument");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-number.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-number.js
new file mode 100644
index 0000000000..765d9b3208
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/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.plainyearmonth.prototype.until
+description: A number is invalid in place of an ISO string for Temporal.PlainYearMonth
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2019, 6);
+
+const numbers = [
+ 1,
+ 201906,
+ -201906,
+ 1234567,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => instance.until(arg),
+ `A number (${arg}) is not a valid ISO string for PlainYearMonth`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-propertybag-calendar-case-insensitive.js
new file mode 100644
index 0000000000..607c1a84d6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-propertybag-calendar-case-insensitive.js
@@ -0,0 +1,20 @@
+// |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.plainyearmonth.prototype.until
+description: The calendar name is case-insensitive
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2019, 6);
+
+const calendar = "IsO8601";
+
+const arg = { year: 2019, monthCode: "M06", calendar };
+const result = instance.until(arg);
+TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-propertybag-calendar-leap-second.js
new file mode 100644
index 0000000000..4293ad5c60
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-propertybag-calendar-leap-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.plainyearmonth.prototype.until
+description: Leap second is a valid ISO string for a calendar in a property bag
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2019, 6);
+
+const calendar = "2016-12-31T23:59:60";
+
+const arg = { year: 2019, monthCode: "M06", calendar };
+const result = instance.until(arg);
+TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "leap second is a valid ISO string for calendar"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-propertybag-calendar-number.js
new file mode 100644
index 0000000000..363d158d7a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-propertybag-calendar-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.plainyearmonth.prototype.until
+description: A number as calendar in a property bag is not accepted
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(1976, 11, 18);
+
+const numbers = [
+ 1,
+ 19970327,
+ -19970327,
+ 1234567890,
+];
+for (const calendar of numbers) {
+ const arg = { year: 2019, monthCode: "M06", calendar };
+ assert.throws(
+ TypeError,
+ () => instance.until(arg),
+ "Numbers cannot be used as a calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-propertybag-calendar-string.js
new file mode 100644
index 0000000000..2254ed6c2b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-propertybag-calendar-string.js
@@ -0,0 +1,20 @@
+// |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.plainyearmonth.prototype.until
+description: A calendar ID is valid input for Calendar
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2019, 6);
+
+const calendar = "iso8601";
+
+const arg = { year: 2019, monthCode: "M06", calendar };
+const result = instance.until(arg);
+TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, `Calendar created from string "${calendar}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-propertybag-calendar-wrong-type.js
new file mode 100644
index 0000000000..b575729f01
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-propertybag-calendar-wrong-type.js
@@ -0,0 +1,46 @@
+// |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.plainyearmonth.prototype.until
+description: >
+ Appropriate error thrown when a calendar property from a property bag cannot
+ be converted to a calendar object or string
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.PlainYearMonth(2000, 5);
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [calendar, description] of primitiveTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ typeof calendar === 'string' ? RangeError : TypeError,
+ () => instance.until(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+ [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields()
+];
+
+for (const [calendar, description] of typeErrorTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(TypeError, () => instance.until(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/PlainYearMonth/prototype/until/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-propertybag-calendar-year-zero.js
new file mode 100644
index 0000000000..71400ef76d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-propertybag-calendar-year-zero.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.plainyearmonth.prototype.until
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T17:45",
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+01:00",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+const instance = new Temporal.PlainYearMonth(2000, 5);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.until(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..a354acab55
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-calendar-annotation.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.plainyearmonth.prototype.until
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2019-12-15T15:23[u-ca=iso8601]", "without time zone"],
+ ["2019-12-15T15:23[UTC][u-ca=iso8601]", "with time zone"],
+ ["2019-12-15T15:23[!u-ca=iso8601]", "with ! and no time zone"],
+ ["2019-12-15T15:23[UTC][!u-ca=iso8601]", "with ! and time zone"],
+ ["2019-12-15T15:23[u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+const instance = new Temporal.PlainYearMonth(2019, 12);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.until(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/PlainYearMonth/prototype/until/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..64cb1aa2c7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-critical-unknown-annotation.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.plainyearmonth.prototype.until
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "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.PlainYearMonth(2000, 5);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.until(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..0f07321125
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-date-with-utc-offset.js
@@ -0,0 +1,56 @@
+// |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.plainyearmonth.prototype.until
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2019, 12);
+
+const validStrings = [
+ "2019-12[Africa/Abidjan]",
+ "2019-12[!Africa/Abidjan]",
+ "2019-12[u-ca=iso8601]",
+ "2019-12[Africa/Abidjan][u-ca=iso8601]",
+ "2019-12-15T00+00:00",
+ "2019-12-15T00+00:00[UTC]",
+ "2019-12-15T00+00:00[!UTC]",
+ "2019-12-15T00-02:30[America/St_Johns]",
+];
+
+for (const arg of validStrings) {
+ const result = instance.until(arg);
+
+ TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ `"${arg}" is a valid UTC offset with time for PlainYearMonth`
+ );
+}
+
+const invalidStrings = [
+ "2022-09[u-ca=hebrew]",
+ "2022-09Z",
+ "2022-09+01:00",
+ "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.until(arg),
+ `"${arg}" UTC offset without time is not valid for PlainYearMonth`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-invalid.js
new file mode 100644
index 0000000000..9a744d64fd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-invalid.js
@@ -0,0 +1,18 @@
+// |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.plainyearmonth.prototype.until
+description: An invalid ISO string is never supported
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(1976, 11);
+
+for (const arg of TemporalHelpers.ISO.plainYearMonthStringsInvalid()) {
+ assert.throws(RangeError, () => instance.until(arg), `"${arg}" is not a valid PlainYearMonth string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..2b54b2263f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/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.plainyearmonth.prototype.until
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[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.PlainYearMonth(2000, 5);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.until(arg),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..29a0d1c74c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-multiple-time-zone.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.plainyearmonth.prototype.until
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "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.PlainYearMonth(2000, 5);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.until(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-time-separators.js
new file mode 100644
index 0000000000..40e31b9143
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-time-separators.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.plainyearmonth.prototype.until
+description: Time separator in string argument can vary
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2019-12-15T15:23", "uppercase T"],
+ ["2019-12-15t15:23", "lowercase T"],
+ ["2019-12-15 15:23", "space between date and time"],
+];
+
+const instance = new Temporal.PlainYearMonth(2019, 12);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.until(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/PlainYearMonth/prototype/until/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..f1c5a1b4fd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-time-zone-annotation.js
@@ -0,0 +1,35 @@
+// |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.plainyearmonth.prototype.until
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2019-12-15T15:23[Asia/Kolkata]", "named, with no offset"],
+ ["2019-12-15T15:23[!Europe/Vienna]", "named, with ! and no offset"],
+ ["2019-12-15T15:23[+00:00]", "numeric, with no offset"],
+ ["2019-12-15T15:23[!-02:30]", "numeric, with ! and no offset"],
+ ["2019-12-15T15:23+00:00[UTC]", "named, with offset"],
+ ["2019-12-15T15:23+00:00[!Africa/Abidjan]", "named, with offset and !"],
+ ["2019-12-15T15:23+00:00[+01:00]", "numeric, with offset"],
+ ["2019-12-15T15:23+00:00[!-08:00]", "numeric, with offset and !"],
+];
+
+const instance = new Temporal.PlainYearMonth(2019, 12);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.until(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/PlainYearMonth/prototype/until/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..c31afa03a8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-unknown-annotation.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.plainyearmonth.prototype.until
+description: Various forms of unknown annotation
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2019-12-15T15:23[foo=bar]", "alone"],
+ ["2019-12-15T15:23[UTC][foo=bar]", "with time zone"],
+ ["2019-12-15T15:23[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["2019-12-15T15:23[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["2019-12-15T15:23[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+const instance = new Temporal.PlainYearMonth(2019, 12);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.until(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/PlainYearMonth/prototype/until/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..f45f28a365
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-with-utc-designator.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.plainyearmonth.prototype.until
+description: RangeError thrown if a string with UTC designator is used as a PlainYearMonth
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "2019-10-01T09:00:00Z",
+ "2019-10-01T09:00:00Z[UTC]",
+];
+const instance = new Temporal.PlainYearMonth(2000, 5);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.until(arg),
+ "String with UTC designator should not be valid as a PlainYearMonth"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string.js
new file mode 100644
index 0000000000..e0cf058da2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string.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.plainyearmonth.prototype.until
+description: A string argument is parsed into a PlainYearMonth
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(1976, 11);
+for (const arg of TemporalHelpers.ISO.plainYearMonthStringsValid()) {
+ const result = instance.until(arg);
+ TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, `"${arg}" is a valid PlainYearMonth string`);
+}
+
+const instanceNegativeYear = new Temporal.PlainYearMonth(-9999, 11);
+for (const arg of TemporalHelpers.ISO.plainYearMonthStringsValidNegativeYear()) {
+ const result = instanceNegativeYear.until(arg);
+ TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, `"${arg}" is a valid PlainYearMonth string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-wrong-type.js
new file mode 100644
index 0000000000..65dde25fd0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/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.plainyearmonth.prototype.until
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainYearMonth
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2000, 5);
+
+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.until(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.PlainYearMonth, "Temporal.PlainYearMonth, object"],
+ [Temporal.PlainYearMonth.prototype, "Temporal.PlainYearMonth.prototype, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.until(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/PlainYearMonth/prototype/until/arguments-missing-throws.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/arguments-missing-throws.js
new file mode 100644
index 0000000000..f7ae571fbd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/arguments-missing-throws.js
@@ -0,0 +1,16 @@
+// |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.plainyearmonth.prototype.until
+description: Calls to PYM.until throw when missing required arguments.
+features: [Temporal]
+---*/
+
+const jun13 = new Temporal.PlainYearMonth(2013, 6);
+
+assert.throws(TypeError, () => jun13.until({ year: 1994 }), 'Throws when missing required month');
+assert.throws(TypeError, () => jun13.until({ month: 11 }), 'Throws when missing required year');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/branding.js
new file mode 100644
index 0000000000..4fa0323443
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/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.plainyearmonth.prototype.until
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const until = Temporal.PlainYearMonth.prototype.until;
+
+assert.sameValue(typeof until, "function");
+
+const args = [new Temporal.PlainYearMonth(2022, 6)];
+
+assert.throws(TypeError, () => until.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => until.apply(null, args), "null");
+assert.throws(TypeError, () => until.apply(true, args), "true");
+assert.throws(TypeError, () => until.apply("", args), "empty string");
+assert.throws(TypeError, () => until.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => until.apply(1, args), "1");
+assert.throws(TypeError, () => until.apply({}, args), "plain object");
+assert.throws(TypeError, () => until.apply(Temporal.PlainYearMonth, args), "Temporal.PlainYearMonth");
+assert.throws(TypeError, () => until.apply(Temporal.PlainYearMonth.prototype, args), "Temporal.PlainYearMonth.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..15839e5969
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/builtin-calendar-no-array-iteration.js
@@ -0,0 +1,23 @@
+// |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.plainyearmonth.prototype.until
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.PlainYearMonth(2023, 5, "iso8601");
+instance.until({ year: 2070, month: 7 });
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..da8a43e025
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plainyearmonth.prototype.until
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dateUntilOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dateUntil");
+Object.defineProperty(Temporal.Calendar.prototype, "dateUntil", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("dateUntil should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainYearMonth(2000, 5, "iso8601", 1);
+instance.until(new Temporal.PlainYearMonth(2001, 6));
+
+Object.defineProperty(Temporal.Calendar.prototype, "dateUntil", dateUntilOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/builtin.js
new file mode 100644
index 0000000000..3ce2efa436
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/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.plainyearmonth.prototype.until
+description: >
+ Tests that Temporal.PlainYearMonth.prototype.until
+ 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.PlainYearMonth.prototype.until),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainYearMonth.prototype.until),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainYearMonth.prototype.until),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainYearMonth.prototype.until.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-dateadd-called-with-plaindate-instance.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-dateadd-called-with-plaindate-instance.js
new file mode 100644
index 0000000000..0442eba080
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-dateadd-called-with-plaindate-instance.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.plainyearmonth.prototype.until
+description: >
+ relativeTo parameters that are not ZonedDateTime or undefined, are always
+ converted to PlainDate for observable calendar calls
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarDateAddPlainDateInstance();
+const instance = new Temporal.PlainYearMonth(1970, 1, calendar);
+instance.until(new Temporal.PlainYearMonth(2000, 5, calendar), { smallestUnit: "year" });
+assert(calendar.dateAddCallCount > 0, "assertions in calendar.dateAdd() should have been tested");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-datefromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..532ef791a0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-datefromfields-called-with-null-prototype-fields.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.plainyearmonth.prototype.until
+description: >
+ Calendar.yearMonthFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.PlainYearMonth(2000, 5);
+const arg = { year: 2000, month: 5, calendar };
+instance.until(arg);
+assert.sameValue(calendar.yearMonthFromFieldsCallCount, 1, "yearMonthFromFields should be called on the property bag's calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-datefromfields-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-datefromfields-called-with-options-undefined.js
new file mode 100644
index 0000000000..5e362ee8ad
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-datefromfields-called-with-options-undefined.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.plainyearmonth.prototype.until
+description: >
+ Calendar.dateFromFields method is called with undefined as the options value
+ when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
+const instance = new Temporal.PlainYearMonth(2000, 5, calendar);
+instance.until({ year: 2000, month: 6, day: 3, calendar });
+assert.sameValue(calendar.dateFromFieldsCallCount, 2);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-dateuntil-called-with-null-prototype-options.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-dateuntil-called-with-null-prototype-options.js
new file mode 100644
index 0000000000..aa70bb2d12
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-dateuntil-called-with-null-prototype-options.js
@@ -0,0 +1,20 @@
+// |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.plainyearmonth.prototype.until
+description: >
+ Calendar.dateUntil method is called with a null-prototype object as the
+ options value when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckOptionsPrototypePollution();
+const instance = new Temporal.PlainYearMonth(2000, 5, calendar);
+const argument = new Temporal.PlainYearMonth(2022, 6, calendar);
+instance.until(argument);
+assert.sameValue(calendar.dateUntilCallCount, 1, "dateUntil should have been called on the calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-dateuntil-called-with-singular-largestunit.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-dateuntil-called-with-singular-largestunit.js
new file mode 100644
index 0000000000..3d969c6ad6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-dateuntil-called-with-singular-largestunit.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.plainyearmonth.prototype.until
+description: The options object passed to calendar.dateUntil has a largestUnit property with its value in the singular form
+info: |
+ sec-temporal.plainyearmonth.prototype.until steps 20–21:
+ 20. Let _untilOptions_ be ? MergeLargestUnitOption(_options_, _largestUnit_).
+ 21. Let _result_ be ? CalendarDateUntil(_calendar_, _thisDate_, _otherDate_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkCalendarDateUntilLargestUnitSingular(
+ (calendar, largestUnit) => {
+ const earlier = new Temporal.PlainYearMonth(2000, 5, calendar);
+ const later = new Temporal.PlainYearMonth(2001, 6, calendar);
+ earlier.until(later, { largestUnit });
+ },
+ {
+ years: ["year"],
+ months: ["month"]
+ }
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-fields-iterable.js
new file mode 100644
index 0000000000..bc123e6c33
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-fields-iterable.js
@@ -0,0 +1,43 @@
+// |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.plainyearmonth.prototype.until
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plainyearmonth.prototype.until step 3:
+ 3. Set _other_ to ? ToTemporalYearMonth(_other_).
+ sec-temporal.plainyearmonth.prototype.until step 13:
+ 13. Let fieldNames be ? CalendarFields(_calendar_, « *"monthCode"*, *"year"* »).
+ sec-temporal-totemporalyearmonth step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected1 = [
+ "monthCode",
+ "year",
+];
+const expected2 = [
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const yearmonth = new Temporal.PlainYearMonth(2000, 5, calendar1);
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+yearmonth.until({ year: 2005, month: 6, calendar: calendar2 });
+
+assert.sameValue(calendar1.fieldsCallCount, 1, "fields() method not called");
+assert.compareArray(calendar1.fieldsCalledWith[0], expected1, "fields() method called with correct args");
+assert(calendar1.iteratorExhausted[0], "iterated through the whole iterable");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected2, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-fromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-fromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..d95e655a47
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-fromfields-called-with-null-prototype-fields.js
@@ -0,0 +1,18 @@
+// |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.plainyearmonth.prototype.until
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.PlainYearMonth(2000, 5, calendar);
+instance.until(new Temporal.PlainYearMonth(2019, 2));
+assert.sameValue(calendar.dateFromFieldsCallCount, 2, "dateFromFields should have been called on the calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-temporal-object.js
new file mode 100644
index 0000000000..7e5adc104d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-temporal-object.js
@@ -0,0 +1,29 @@
+// |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.plainyearmonth.prototype.until
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plainyearmonth.prototype.until step 3:
+ 3. Set _other_ to ? ToTemporalYearMonth(_other_).
+ sec-temporal-totemporalyearmonth step 2.b:
+ b. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const yearmonth = new Temporal.PlainYearMonth(2000, 5, temporalObject);
+ yearmonth.until({ year: 2005, month: 6, calendar: temporalObject });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-yearmonthfromfields-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-yearmonthfromfields-called-with-options-undefined.js
new file mode 100644
index 0000000000..50658a49d0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-yearmonthfromfields-called-with-options-undefined.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.plainyearmonth.prototype.until
+description: >
+ Calendar.yearMonthFromFields method is called with undefined as the options
+ value when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+let calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
+let instance = new Temporal.PlainYearMonth(2000, 5, calendar);
+instance.until({ year: 2000, month: 6, calendar });
+assert.sameValue(calendar.yearMonthFromFieldsCallCount, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..85e8b20103
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/constructor-in-calendar-fields.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.plainyearmonth.prototype.until
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.PlainYearMonth(2000, 5, calendar);
+
+assert.throws(RangeError, () => instance.until(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/duplicate-calendar-fields.js
new file mode 100644
index 0000000000..5d3fafbaf7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/duplicate-calendar-fields.js
@@ -0,0 +1,19 @@
+// |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.plainyearmonth.prototype.until
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['month'], ['monthCode'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar };
+ const instance = new Temporal.PlainYearMonth(2000, 5, calendar);
+
+ assert.throws(RangeError, () => instance.until(arg));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..f738387d6e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/infinity-throws-rangeerror.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.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plainyearmonth.prototype.until
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2000, 5);
+const base = { year: 2000, month: 5 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.until({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.until({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-auto.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-auto.js
new file mode 100644
index 0000000000..afbff2bee6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-auto.js
@@ -0,0 +1,20 @@
+// |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.plainyearmonth.prototype.until
+description: auto value for largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+
+TemporalHelpers.assertDuration(earlier.until(later, { largestUnit: "auto" }),
+ 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, "auto largestUnit is year (pos)");
+TemporalHelpers.assertDuration(later.until(earlier, { largestUnit: "auto" }),
+ -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, "auto largestUnit is year (neg)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-disallowed-units.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-disallowed-units.js
new file mode 100644
index 0000000000..c55e4c4c77
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-disallowed-units.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.plainyearmonth.prototype.until
+description: Until throws on to0-small largestUnit
+features: [Temporal, arrow-function]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2019, 1);
+const later = new Temporal.PlainYearMonth(2021, 9);
+
+[
+ "weeks",
+ "days",
+ "hours",
+ "minutes",
+ "seconds",
+ "milliseconds",
+ "microseconds",
+ "nanoseconds"
+].forEach((largestUnit) => {
+ assert.throws(RangeError, () => earlier.until(later, { largestUnit }),
+ `throws on disallowed or invalid largestUnit: ${largestUnit}`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-invalid-string.js
new file mode 100644
index 0000000000..02ab42568f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-invalid-string.js
@@ -0,0 +1,45 @@
+// |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.plainyearmonth.prototype.until
+description: RangeError thrown when largestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+const badValues = [
+ "era",
+ "eraYear",
+ "week",
+ "day",
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+ "month\0",
+ "YEAR",
+ "eras",
+ "eraYears",
+ "weeks",
+ "days",
+ "hours",
+ "minutes",
+ "seconds",
+ "milliseconds",
+ "microseconds",
+ "nanoseconds",
+ "months\0",
+ "YEARS",
+ "other string"
+];
+for (const largestUnit of badValues) {
+ assert.throws(RangeError, () => earlier.until(later, { largestUnit }),
+ `"${largestUnit}" is not a valid value for largestUnit`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-months.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-months.js
new file mode 100644
index 0000000000..711dd53b1d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-months.js
@@ -0,0 +1,20 @@
+// |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.plainyearmonth.prototype.until
+description: months value for largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+
+TemporalHelpers.assertDuration(earlier.until(later, { largestUnit: "months" }),
+ 0, 13, 0, 0, 0, 0, 0, 0, 0, 0, "largestUnit is months (pos)");
+TemporalHelpers.assertDuration(later.until(earlier, { largestUnit: "months" }),
+ 0, -13, 0, 0, 0, 0, 0, 0, 0, 0, "largestUnit is months (neg)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-plurals-accepted.js
new file mode 100644
index 0000000000..d84a89e5f6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-plurals-accepted.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.plainyearmonth.prototype.until
+description: Plural units are accepted as well for the largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+const validUnits = [
+ "year",
+ "month",
+];
+TemporalHelpers.checkPluralUnitsAccepted((largestUnit) => earlier.until(later, { largestUnit }), validUnits);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-smallestunit-mismatch.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-smallestunit-mismatch.js
new file mode 100644
index 0000000000..e5f3042e75
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/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.plainyearmonth.prototype.until
+description: RangeError thrown when smallestUnit is larger than largestUnit
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+const units = ["years", "months"];
+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, () => earlier.until(later, { largestUnit, smallestUnit }));
+ }
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-undefined.js
new file mode 100644
index 0000000000..6a8ae1753f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-undefined.js
@@ -0,0 +1,30 @@
+// |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.plainyearmonth.prototype.until
+description: Fallback value for largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+
+TemporalHelpers.assertDuration(earlier.until(later, { largestUnit: undefined }),
+ 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, "default largestUnit is year (explicit, pos)");
+TemporalHelpers.assertDuration(later.until(earlier, { largestUnit: undefined }),
+ -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, "default largestUnit is year (explicit, neg)");
+
+TemporalHelpers.assertDuration(earlier.until(later, {}),
+ 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, "default largestUnit is year (implicit, pos)");
+TemporalHelpers.assertDuration(later.until(earlier, {}),
+ -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, "default largestUnit is year (implicit, neg)");
+
+TemporalHelpers.assertDuration(earlier.until(later, () => {}),
+ 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, "default largestUnit is year (arrow function, pos)");
+TemporalHelpers.assertDuration(later.until(earlier, () => {}),
+ -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, "default largestUnit is year (arrow function, neg)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-wrong-type.js
new file mode 100644
index 0000000000..cc87b1025f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/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.plainyearmonth.prototype.until
+description: Type conversions for largestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+TemporalHelpers.checkStringOptionWrongType("largestUnit", "month",
+ (largestUnit) => earlier.until(later, { largestUnit }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 13, 0, 0, 0, 0, 0, 0, 0, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-years.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-years.js
new file mode 100644
index 0000000000..249eb4b32b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-years.js
@@ -0,0 +1,20 @@
+// |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.plainyearmonth.prototype.until
+description: years value for largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+
+TemporalHelpers.assertDuration(earlier.until(later, { largestUnit: "years" }),
+ 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, "largestUnit is years (pos)");
+TemporalHelpers.assertDuration(later.until(earlier, { largestUnit: "years" }),
+ -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, "largestUnit is years (neg)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/leap-second.js
new file mode 100644
index 0000000000..46b09c470c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/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.plainyearmonth.prototype.until
+description: Leap second is a valid ISO string for PlainYearMonth
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2016, 12);
+
+let arg = "2016-12-31T23:59:60";
+const result1 = instance.until(arg);
+TemporalHelpers.assertDuration(
+ result1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "leap second is a valid ISO string for PlainYearMonth"
+);
+
+arg = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+const result2 = instance.until(arg);
+TemporalHelpers.assertDuration(
+ result2,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "second: 60 is ignored in property bag for PlainYearMonth"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/length.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/length.js
new file mode 100644
index 0000000000..883f548bfc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/length.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.plainyearmonth.prototype.until
+description: Temporal.PlainYearMonth.prototype.until.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.PlainYearMonth.prototype.until, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/mixed-calendar-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/mixed-calendar-invalid.js
new file mode 100644
index 0000000000..c3e00f3d99
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/mixed-calendar-invalid.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.plainyearmonth.prototype.until
+description: Mixed calendars throw as invalid
+features: [Temporal]
+---*/
+
+class customCal extends Temporal.Calendar {
+ constructor () {
+ super('iso8601');
+ }
+
+ get id() {
+ return "I am a secret cal.";
+ }
+}
+
+const ym1 = new Temporal.PlainYearMonth(2000, 1);
+const ym2 = new Temporal.PlainYearMonth(2000, 1, new customCal());
+
+assert.throws(RangeError, () => ym1.until(ym2), 'until throws with different calendars');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/name.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/name.js
new file mode 100644
index 0000000000..191af91c8d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/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.plainyearmonth.prototype.until
+description: Temporal.PlainYearMonth.prototype.until.name is "until".
+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.PlainYearMonth.prototype.until, "name", {
+ value: "until",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/not-a-constructor.js
new file mode 100644
index 0000000000..be0a4a674e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/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.plainyearmonth.prototype.until
+description: >
+ Temporal.PlainYearMonth.prototype.until 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.PlainYearMonth.prototype.until();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainYearMonth.prototype.until), false,
+ "isConstructor(Temporal.PlainYearMonth.prototype.until)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/options-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/options-invalid.js
new file mode 100644
index 0000000000..c29a89d3ce
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/options-invalid.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.plainyearmonth.prototype.until
+description: Verify that invalid options are handled correctly.
+features: [Temporal]
+---*/
+
+const feb20 = new Temporal.PlainYearMonth(2020, 2);
+const feb21 = new Temporal.PlainYearMonth(2021, 2);
+
+[
+ null,
+ 1,
+ "hello",
+ true,
+ Symbol("foo"),
+ 1n
+].forEach((badOption) =>
+ assert.throws(TypeError, () => feb20.until(feb21, badOption), `${String(badOption)} throws TypeError`)
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/options-object.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/options-object.js
new file mode 100644
index 0000000000..ba9bd522ac
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/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.plainyearmonth.prototype.until
+description: Empty or a function object may be used as options
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2019, 10);
+
+const result1 = instance.until(new Temporal.PlainYearMonth(1976, 11), {});
+TemporalHelpers.assertDuration(
+ result1, -42, -11, 0, 0, 0, 0, 0, 0, 0, 0,
+ "options may be an empty plain object"
+);
+
+const result2 = instance.until(new Temporal.PlainYearMonth(1976, 11), () => {});
+TemporalHelpers.assertDuration(
+ result2, -42, -11, 0, 0, 0, 0, 0, 0, 0, 0,
+ "options may be a function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/options-undefined.js
new file mode 100644
index 0000000000..293aeffcbe
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/options-undefined.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.plainyearmonth.prototype.until
+description: Verify that undefined options are handled correctly.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2002, 12);
+
+TemporalHelpers.assertDuration(earlier.until(later, undefined),
+ 2, 7, 0, 0, 0, 0, 0, 0, 0, 0, "default largestUnit is year (explicit, pos)");
+TemporalHelpers.assertDuration(later.until(earlier, undefined),
+ -2, -7, 0, 0, 0, 0, 0, 0, 0, 0, "default largestUnit is year (explicit, neg)");
+
+TemporalHelpers.assertDuration(earlier.until(later),
+ 2, 7, 0, 0, 0, 0, 0, 0, 0, 0, "default largestUnit is year (implicit, pos)");
+TemporalHelpers.assertDuration(later.until(earlier),
+ -2, -7, 0, 0, 0, 0, 0, 0, 0, 0, "default largestUnit is year (implicit, neg)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/options-wrong-type.js
new file mode 100644
index 0000000000..d8d91f94f3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/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.plainyearmonth.prototype.until
+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.PlainYearMonth(2019, 10);
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.until(new Temporal.PlainYearMonth(1976, 11), value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/order-of-operations.js
new file mode 100644
index 0000000000..bbc4f29916
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/order-of-operations.js
@@ -0,0 +1,193 @@
+// |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.plainyearmonth.prototype.until
+description: Properties on objects passed to until() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expectedMinimal = [
+ // ToTemporalYearMonth
+ "get other.calendar",
+ "has other.calendar.dateAdd",
+ "has other.calendar.dateFromFields",
+ "has other.calendar.dateUntil",
+ "has other.calendar.day",
+ "has other.calendar.dayOfWeek",
+ "has other.calendar.dayOfYear",
+ "has other.calendar.daysInMonth",
+ "has other.calendar.daysInWeek",
+ "has other.calendar.daysInYear",
+ "has other.calendar.fields",
+ "has other.calendar.id",
+ "has other.calendar.inLeapYear",
+ "has other.calendar.mergeFields",
+ "has other.calendar.month",
+ "has other.calendar.monthCode",
+ "has other.calendar.monthDayFromFields",
+ "has other.calendar.monthsInYear",
+ "has other.calendar.weekOfYear",
+ "has other.calendar.year",
+ "has other.calendar.yearMonthFromFields",
+ "has other.calendar.yearOfWeek",
+ "get other.calendar.fields",
+ "get other.calendar.yearMonthFromFields",
+ "call other.calendar.fields",
+ "get other.month",
+ "get other.month.valueOf",
+ "call other.month.valueOf",
+ "get other.monthCode",
+ "get other.monthCode.toString",
+ "call other.monthCode.toString",
+ "get other.year",
+ "get other.year.valueOf",
+ "call other.year.valueOf",
+ "call other.calendar.yearMonthFromFields",
+ // CalendarEquals
+ "get this.calendar.id",
+ "get other.calendar.id",
+ // 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 expected = expectedMinimal.concat([
+ // lookup
+ "get this.calendar.dateAdd",
+ "get this.calendar.dateFromFields",
+ "get this.calendar.dateUntil",
+ "get this.calendar.fields",
+ // CalendarFields
+ "call this.calendar.fields",
+ // PrepareTemporalFields / CalendarDateFromFields (receiver)
+ "get this.calendar.monthCode",
+ "call this.calendar.monthCode",
+ "get this.calendar.year",
+ "call this.calendar.year",
+ "call this.calendar.dateFromFields",
+ // PrepareTemporalFields / CalendarDateFromFields (argument)
+ "get other.calendar.monthCode",
+ "call other.calendar.monthCode",
+ "get other.calendar.year",
+ "call other.calendar.year",
+ "call this.calendar.dateFromFields",
+ // CalendarDateUntil
+ "call this.calendar.dateUntil",
+]);
+const actual = [];
+
+const ownCalendar = TemporalHelpers.calendarObserver(actual, "this.calendar");
+const instance = new Temporal.PlainYearMonth(2000, 5, ownCalendar, 1);
+
+const otherYearMonthPropertyBag = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2001,
+ month: 6,
+ monthCode: "M06",
+ calendar: TemporalHelpers.calendarObserver(actual, "other.calendar"),
+}, "other");
+
+function createOptionsObserver({ smallestUnit = "months", largestUnit = "auto", roundingMode = "halfExpand", roundingIncrement = 1 } = {}) {
+ return TemporalHelpers.propertyBagObserver(actual, {
+ // order is significant, due to iterating through properties in order to
+ // copy them to an internal null-prototype object:
+ roundingIncrement,
+ roundingMode,
+ largestUnit,
+ smallestUnit,
+ additional: "property",
+ }, "options");
+}
+
+// clear any observable things that happened while constructing the objects
+actual.splice(0);
+
+// code path that skips RoundDuration:
+instance.since(otherYearMonthPropertyBag, createOptionsObserver({ smallestUnit: "months", roundingIncrement: 1 }));
+assert.compareArray(actual, expected, "order of operations with no rounding");
+actual.splice(0); // clear
+
+// short-circuit for identical objects:
+const identicalPropertyBag = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2000,
+ month: 5,
+ monthCode: "M05",
+ calendar: TemporalHelpers.calendarObserver(actual, "other.calendar"),
+}, "other");
+
+instance.until(identicalPropertyBag, createOptionsObserver());
+assert.compareArray(actual, expectedMinimal, "order of operations with identical year-months");
+actual.splice(0); // clear
+
+// code path through RoundDuration that rounds to the nearest year:
+const expectedOpsForYearRounding = expected.concat([
+ // RoundDuration
+ "call this.calendar.dateAdd", // 7.e
+ "call this.calendar.dateAdd", // 7.g
+ "call this.calendar.dateUntil", // 7.o
+ "call this.calendar.dateAdd", // 7.y MoveRelativeDate
+ // (7.s not called because other units can't add up to >1 year at this point)
+ // BalanceDateDurationRelative
+ "call this.calendar.dateAdd", // 9.c
+ "call this.calendar.dateUntil" // 9.d
+]);
+instance.until(otherYearMonthPropertyBag, createOptionsObserver({ smallestUnit: "years" }));
+assert.compareArray(actual, expectedOpsForYearRounding, "order of operations with smallestUnit = years");
+actual.splice(0); // clear
+
+// code path through RoundDuration that rounds to the nearest year and skips a DateUntil call
+const otherYearMonthPropertyBagSameMonth = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2001,
+ month: 5,
+ monthCode: "M05",
+ calendar: TemporalHelpers.calendarObserver(actual, "other.calendar"),
+}, "other");
+const expectedOpsForYearRoundingSameMonth = expected.concat([
+ "call this.calendar.dateAdd", // 7.e
+ "call this.calendar.dateAdd", // 7.g
+ "call this.calendar.dateAdd", // 7.y MoveRelativeDate
+ // (7.o not called because months and weeks == 0)
+ // BalanceDateDurationRelative
+ "call this.calendar.dateAdd", // 9.c
+ "call this.calendar.dateUntil" // 9.d
+]);
+instance.until(otherYearMonthPropertyBagSameMonth, createOptionsObserver({ smallestUnit: "years" }));
+assert.compareArray(actual, expectedOpsForYearRoundingSameMonth, "order of operations with smallestUnit = years and no excess months");
+actual.splice(0); // clear
+
+// code path through RoundDuration that rounds to the nearest month:
+const expectedOpsForMonthRounding = expected.concat([
+ // RoundDuration
+ "call this.calendar.dateAdd", // 10.c
+ "call this.calendar.dateAdd", // 10.e
+ "call this.calendar.dateAdd", // 10.k MoveRelativeDate
+ // (10.n.iii MoveRelativeDate not called because weeks == 0)
+ // BalanceDateDurationRelative
+ "call this.calendar.dateAdd", // 10.d
+ "call this.calendar.dateUntil" // 10.e
+]);
+instance.until(otherYearMonthPropertyBag, createOptionsObserver({ smallestUnit: "months", roundingIncrement: 2 }));
+assert.compareArray(actual, expectedOpsForMonthRounding, "order of operations with smallestUnit = months");
+actual.splice(0); // clear
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/prop-desc.js
new file mode 100644
index 0000000000..ca70d809b2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/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.plainyearmonth.prototype.until
+description: The "until" property of Temporal.PlainYearMonth.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainYearMonth.prototype.until,
+ "function",
+ "`typeof PlainYearMonth.prototype.until` is `function`"
+);
+
+verifyProperty(Temporal.PlainYearMonth.prototype, "until", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/proto-in-calendar-fields.js
new file mode 100644
index 0000000000..cebfe1a96b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/proto-in-calendar-fields.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.plainyearmonth.prototype.until
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.PlainYearMonth(2000, 5, calendar);
+
+assert.throws(RangeError, () => instance.until(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/round-cross-unit-boundary.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/round-cross-unit-boundary.js
new file mode 100644
index 0000000000..f570eed198
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/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.plainyearmonth.prototype.until
+description: Rounding can cross unit boundaries up to largestUnit
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2022, 1);
+const later = new Temporal.PlainYearMonth(2023, 12);
+const duration = earlier.until(later, { largestUnit: "years", smallestUnit: "months", roundingIncrement: 3, roundingMode: "expand" });
+TemporalHelpers.assertDuration(duration, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, "1 year 12 months balances to 2 years");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/rounding-zero-year-month-length.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/rounding-zero-year-month-length.js
new file mode 100644
index 0000000000..b714ddca6f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/rounding-zero-year-month-length.js
@@ -0,0 +1,33 @@
+// |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.plainyearmonth.prototype.until
+description: >
+ A malicious calendar resulting in a year, month, or week length of zero is
+ handled correctly
+info: |
+ RoundDuration
+ 10.z. If _oneYearDays_ = 0, throw a *RangeError* exception.
+ ...
+ 11.z. If _oneMonthDays_ = 0, throw a *RangeError* exception.
+ ...
+ 12.s. If _oneWeekDays_ = 0, throw a *RangeError* exception.
+features: [Temporal]
+---*/
+
+const cal = new class extends Temporal.Calendar {
+ dateAdd(date, duration, options) {
+ // Called several times, last call sets oneYear/Month/WeekDays to 0
+ return new Temporal.PlainDate(1970, 1, 1);
+ }
+}("iso8601");
+
+const ym1 = new Temporal.PlainYearMonth(1970, 1, cal);
+const ym2 = new Temporal.PlainYearMonth(1971, 1, cal);
+
+assert.throws(RangeError, () => ym1.until(ym2, { smallestUnit: "years" }), "zero year length handled correctly");
+assert.throws(RangeError, () => ym1.until(ym2, { smallestUnit: "months", roundingIncrement: 2 }), "zero month length handled correctly");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingincrement-as-expected.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingincrement-as-expected.js
new file mode 100644
index 0000000000..e9430e2d33
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingincrement-as-expected.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.plainyearmonth.prototype.until
+description: Until rounding increments work as expected
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2019, 1);
+const later = new Temporal.PlainYearMonth(2021, 9);
+
+const laterSinceYear = earlier.until(later, { smallestUnit: "years", roundingIncrement: 4, roundingMode: "halfExpand" });
+TemporalHelpers.assertDuration(laterSinceYear,
+ /* years = */ 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, "rounds to an increment of years");
+
+const laterSinceMixed = earlier.until(later, { smallestUnit: "months", roundingIncrement: 5 });
+TemporalHelpers.assertDuration(laterSinceMixed,
+ /* years = */ 2, /* months = */ 5, 0, 0, 0, 0, 0, 0, 0, 0, "rounds to an increment of months mixed with years");
+
+const laterSinceMonth = earlier.until(later, { largestUnit: "months", smallestUnit: "months", roundingIncrement: 10 });
+TemporalHelpers.assertDuration(laterSinceMonth,
+ 0, /* months = */ 30, 0, 0, 0, 0, 0, 0, 0, 0, "rounds to an increment of pure months");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingincrement-nan.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingincrement-nan.js
new file mode 100644
index 0000000000..d1f0aa1100
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/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.plainyearmonth.prototype.until
+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.plainyearmonth.prototype.until step 12:
+ 12. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, *undefined*, *false*).
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: NaN }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingincrement-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingincrement-non-integer.js
new file mode 100644
index 0000000000..743e0b0c87
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingincrement-non-integer.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.plainyearmonth.prototype.until
+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.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2000, 10);
+const result = earlier.until(later, { roundingIncrement: 2.5, roundingMode: "trunc" });
+TemporalHelpers.assertDuration(result, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, "roundingIncrement 2.5 truncates to 2");
+// Cannot test the upper bound of 1e9 + 0.5 here, because the duration is
+// rounded relative to the receiver PlainYearMonth, and 1e9 months is outside of
+// the PlainYearMonth range.
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingincrement-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingincrement-out-of-range.js
new file mode 100644
index 0000000000..e2540546a7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/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.plainyearmonth.prototype.until
+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.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2000, 10);
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: -Infinity }));
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: -1 }));
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: 0 }));
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: 0.9 }));
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: 1e9 + 1 }));
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: Infinity }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingincrement-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingincrement-undefined.js
new file mode 100644
index 0000000000..8179e49442
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/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.plainyearmonth.prototype.until
+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.plainyearmonth.prototype.until step 12:
+ 12. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, *undefined*, *false*).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+
+const explicit = earlier.until(later, { roundingIncrement: undefined });
+TemporalHelpers.assertDuration(explicit, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, "default roundingIncrement is 1");
+
+const implicit = earlier.until(later, {});
+TemporalHelpers.assertDuration(implicit, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, "default roundingIncrement is 1");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingincrement-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingincrement-wrong-type.js
new file mode 100644
index 0000000000..cca7ee7078
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/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.plainyearmonth.prototype.until
+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.plainyearmonth.prototype.until step 12:
+ 12. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, *undefined*, *false*).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+
+TemporalHelpers.checkRoundingIncrementOptionWrongType(
+ (roundingIncrement) => earlier.until(later, { roundingIncrement }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, descr),
+ (result, descr) => TemporalHelpers.assertDuration(result, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-ceil.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-ceil.js
new file mode 100644
index 0000000000..baa45e6263
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-ceil.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.plainyearmonth.prototype.until
+description: Tests calculations with roundingMode "ceil".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2019, 1);
+const later = new Temporal.PlainYearMonth(2021, 9);
+
+const expected = [
+ ["years", [3], [-2]],
+ ["months", [2, 8], [-2, -8]],
+];
+
+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(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { 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/PlainYearMonth/prototype/until/roundingmode-expand.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-expand.js
new file mode 100644
index 0000000000..24a911a328
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-expand.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.plainyearmonth.prototype.until
+description: Tests calculations with roundingMode "expand".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2019, 1);
+const later = new Temporal.PlainYearMonth(2021, 9);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [2, 8], [-2, -8]],
+];
+
+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(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { 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/PlainYearMonth/prototype/until/roundingmode-floor.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-floor.js
new file mode 100644
index 0000000000..dbc6cc1343
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-floor.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.plainyearmonth.prototype.until
+description: Tests calculations with roundingMode "floor".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2019, 1);
+const later = new Temporal.PlainYearMonth(2021, 9);
+
+const expected = [
+ ["years", [2], [-3]],
+ ["months", [2, 8], [-2, -8]],
+];
+
+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(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { 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/PlainYearMonth/prototype/until/roundingmode-halfCeil.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-halfCeil.js
new file mode 100644
index 0000000000..d07eebdc1c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-halfCeil.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.plainyearmonth.prototype.until
+description: Tests calculations with roundingMode "halfCeil".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2019, 1);
+const later = new Temporal.PlainYearMonth(2021, 9);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [2, 8], [-2, -8]],
+];
+
+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(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { 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/PlainYearMonth/prototype/until/roundingmode-halfEven.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-halfEven.js
new file mode 100644
index 0000000000..9ec8af04ca
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-halfEven.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.plainyearmonth.prototype.until
+description: Tests calculations with roundingMode "halfEven".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2019, 1);
+const later = new Temporal.PlainYearMonth(2021, 9);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [2, 8], [-2, -8]],
+];
+
+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(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { 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/PlainYearMonth/prototype/until/roundingmode-halfExpand.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-halfExpand.js
new file mode 100644
index 0000000000..989b59e5ce
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-halfExpand.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.plainyearmonth.prototype.until
+description: Tests calculations with roundingMode "halfExpand".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2019, 1);
+const later = new Temporal.PlainYearMonth(2021, 9);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [2, 8], [-2, -8]],
+];
+
+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(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { 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/PlainYearMonth/prototype/until/roundingmode-halfFloor.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-halfFloor.js
new file mode 100644
index 0000000000..2bd83d6f2f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-halfFloor.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.plainyearmonth.prototype.until
+description: Tests calculations with roundingMode "halfFloor".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2019, 1);
+const later = new Temporal.PlainYearMonth(2021, 9);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [2, 8], [-2, -8]],
+];
+
+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(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { 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/PlainYearMonth/prototype/until/roundingmode-halfTrunc.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-halfTrunc.js
new file mode 100644
index 0000000000..bed39ed9e4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-halfTrunc.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.plainyearmonth.prototype.until
+description: Tests calculations with roundingMode "halfTrunc".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2019, 1);
+const later = new Temporal.PlainYearMonth(2021, 9);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [2, 8], [-2, -8]],
+];
+
+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(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { 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/PlainYearMonth/prototype/until/roundingmode-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-invalid-string.js
new file mode 100644
index 0000000000..8e80a0c598
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/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.plainyearmonth.prototype.until
+description: RangeError thrown when roundingMode option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+for (const roundingMode of ["other string", "cile", "CEIL", "ce\u0131l", "auto", "halfexpand", "floor\0"]) {
+ assert.throws(RangeError, () => earlier.until(later, { smallestUnit: "microsecond", roundingMode }));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-trunc.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-trunc.js
new file mode 100644
index 0000000000..06dd0e1964
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-trunc.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.plainyearmonth.prototype.until
+description: Tests calculations with roundingMode "trunc".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2019, 1);
+const later = new Temporal.PlainYearMonth(2021, 9);
+
+const expected = [
+ ["years", [2], [-2]],
+ ["months", [2, 8], [-2, -8]],
+];
+
+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(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { 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/PlainYearMonth/prototype/until/roundingmode-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-undefined.js
new file mode 100644
index 0000000000..1c73dfc3ca
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-undefined.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.plainyearmonth.prototype.until
+description: Fallback value for roundingMode option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 1);
+
+const later1 = new Temporal.PlainYearMonth(2005, 2);
+const explicit1 = earlier.until(later1, { smallestUnit: "year", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit1, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, "default roundingMode is trunc");
+const implicit1 = earlier.until(later1, { smallestUnit: "year" });
+TemporalHelpers.assertDuration(implicit1, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, "default roundingMode is trunc");
+
+const later2 = new Temporal.PlainYearMonth(2005, 12);
+const explicit2 = earlier.until(later2, { smallestUnit: "year", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit2, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, "default roundingMode is trunc");
+const implicit2 = earlier.until(later2, { smallestUnit: "year" });
+TemporalHelpers.assertDuration(implicit2, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, "default roundingMode is trunc");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-wrong-type.js
new file mode 100644
index 0000000000..4f2b3b3d8e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/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.plainyearmonth.prototype.until
+description: Type conversions for roundingMode option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+TemporalHelpers.checkStringOptionWrongType("roundingMode", "trunc",
+ (roundingMode) => earlier.until(later, { smallestUnit: "year", roundingMode }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/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/PlainYearMonth/prototype/until/smallestunit-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/smallestunit-invalid-string.js
new file mode 100644
index 0000000000..00a17e14d4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/smallestunit-invalid-string.js
@@ -0,0 +1,45 @@
+// |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.plainyearmonth.prototype.until
+description: RangeError thrown when smallestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+const badValues = [
+ "era",
+ "eraYear",
+ "week",
+ "day",
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+ "month\0",
+ "YEAR",
+ "eras",
+ "eraYears",
+ "weeks",
+ "days",
+ "hours",
+ "minutes",
+ "seconds",
+ "milliseconds",
+ "microseconds",
+ "nanoseconds",
+ "months\0",
+ "YEARS",
+ "other string",
+];
+for (const smallestUnit of badValues) {
+ assert.throws(RangeError, () => earlier.until(later, { smallestUnit }),
+ `"${smallestUnit}" is not a valid value for smallest unit`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/smallestunit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/smallestunit-plurals-accepted.js
new file mode 100644
index 0000000000..ae5c26042b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/smallestunit-plurals-accepted.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.plainyearmonth.prototype.until
+description: Plural units are accepted as well for the smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+const validUnits = [
+ "year",
+ "month",
+];
+TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => earlier.until(later, { smallestUnit }), validUnits);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/smallestunit-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/smallestunit-undefined.js
new file mode 100644
index 0000000000..d2f70d5b1d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/smallestunit-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.plainyearmonth.prototype.until
+description: Fallback value for smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+
+const explicit = earlier.until(later, { smallestUnit: undefined });
+TemporalHelpers.assertDuration(explicit, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, "default smallestUnit is month");
+const implicit = earlier.until(later, {});
+TemporalHelpers.assertDuration(implicit, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, "default smallestUnit is month");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/smallestunit-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/smallestunit-wrong-type.js
new file mode 100644
index 0000000000..fddccb6ae7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/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.plainyearmonth.prototype.until
+description: Type conversions for smallestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+TemporalHelpers.checkStringOptionWrongType("smallestUnit", "year",
+ (smallestUnit) => earlier.until(later, { smallestUnit }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/year-zero.js
new file mode 100644
index 0000000000..20ce83cdb0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/year-zero.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.plainyearmonth.prototype.until
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-06",
+ "-000000-06-24",
+ "-000000-06-24T15:43:27",
+ "-000000-06-24T15:43:27+01:00",
+ "-000000-06-24T15:43:27+00:00[UTC]",
+];
+const instance = new Temporal.PlainYearMonth(2000, 5);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.until(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/basic.js
new file mode 100644
index 0000000000..6ce4e91289
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/basic.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.plainyearmonth.prototype.valueof
+description: Basic tests for valueOf().
+features: [Temporal]
+---*/
+
+const plainYearMonth = Temporal.PlainYearMonth.from("1963-02");
+const plainYearMonth2 = Temporal.PlainYearMonth.from("1963-02");
+
+assert.throws(TypeError, () => plainYearMonth.valueOf(), "valueOf");
+assert.throws(TypeError, () => plainYearMonth < plainYearMonth, "<");
+assert.throws(TypeError, () => plainYearMonth <= plainYearMonth, "<=");
+assert.throws(TypeError, () => plainYearMonth > plainYearMonth, ">");
+assert.throws(TypeError, () => plainYearMonth >= plainYearMonth, ">=");
+assert.sameValue(plainYearMonth === plainYearMonth, true, "===");
+assert.sameValue(plainYearMonth === plainYearMonth2, false, "===");
+assert.sameValue(plainYearMonth !== plainYearMonth, false, "!==");
+assert.sameValue(plainYearMonth !== plainYearMonth2, true, "!==");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/branding.js
new file mode 100644
index 0000000000..c566f848c8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/branding.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.plainyearmonth.prototype.valueof
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const valueOf = Temporal.PlainYearMonth.prototype.valueOf;
+
+assert.sameValue(typeof valueOf, "function");
+
+assert.throws(TypeError, () => valueOf.call(undefined), "undefined");
+assert.throws(TypeError, () => valueOf.call(null), "null");
+assert.throws(TypeError, () => valueOf.call(true), "true");
+assert.throws(TypeError, () => valueOf.call(""), "empty string");
+assert.throws(TypeError, () => valueOf.call(Symbol()), "symbol");
+assert.throws(TypeError, () => valueOf.call(1), "1");
+assert.throws(TypeError, () => valueOf.call({}), "plain object");
+assert.throws(TypeError, () => valueOf.call(Temporal.PlainYearMonth), "Temporal.PlainYearMonth");
+assert.throws(TypeError, () => valueOf.call(Temporal.PlainYearMonth.prototype), "Temporal.PlainYearMonth.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/builtin.js
new file mode 100644
index 0000000000..12d63be064
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/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.plainyearmonth.prototype.valueof
+description: >
+ Tests that Temporal.PlainYearMonth.prototype.valueOf
+ 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.PlainYearMonth.prototype.valueOf),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainYearMonth.prototype.valueOf),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainYearMonth.prototype.valueOf),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainYearMonth.prototype.valueOf.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/length.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/length.js
new file mode 100644
index 0000000000..bf0edd10ae
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/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.plainyearmonth.prototype.valueof
+description: Temporal.PlainYearMonth.prototype.valueOf.length is 0
+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.PlainYearMonth.prototype.valueOf, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/name.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/name.js
new file mode 100644
index 0000000000..44179950c7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/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.plainyearmonth.prototype.valueof
+description: Temporal.PlainYearMonth.prototype.valueOf.name is "valueOf".
+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.PlainYearMonth.prototype.valueOf, "name", {
+ value: "valueOf",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/not-a-constructor.js
new file mode 100644
index 0000000000..11d2048fe9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/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.plainyearmonth.prototype.valueof
+description: >
+ Temporal.PlainYearMonth.prototype.valueOf 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.PlainYearMonth.prototype.valueOf();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainYearMonth.prototype.valueOf), false,
+ "isConstructor(Temporal.PlainYearMonth.prototype.valueOf)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/prop-desc.js
new file mode 100644
index 0000000000..142e78030d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/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.plainyearmonth.prototype.valueof
+description: The "valueOf" property of Temporal.PlainYearMonth.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainYearMonth.prototype.valueOf,
+ "function",
+ "`typeof PlainYearMonth.prototype.valueOf` is `function`"
+);
+
+verifyProperty(Temporal.PlainYearMonth.prototype, "valueOf", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/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/PlainYearMonth/prototype/with/argument-calendar-field.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/argument-calendar-field.js
new file mode 100644
index 0000000000..a51a817c86
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/argument-calendar-field.js
@@ -0,0 +1,14 @@
+// |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.plainyearmonth.prototype.with
+description: Throw if the argument has a calendar field
+features: [Temporal]
+---*/
+
+const ym = Temporal.PlainYearMonth.from("2019-10");
+assert.throws(TypeError, () => ym.with({ year: 2021, calendar: "iso8601" }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/argument-missing-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/argument-missing-fields.js
new file mode 100644
index 0000000000..fa12608d71
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/argument-missing-fields.js
@@ -0,0 +1,15 @@
+// |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.plainyearmonth.prototype.with
+description: TypeError thrown when argument doesn't contain any of the supported properties
+features: [Temporal]
+---*/
+
+const ym = Temporal.PlainYearMonth.from("2019-10");
+assert.throws(TypeError, () => ym.with({}), "No properties");
+assert.throws(TypeError, () => ym.with({ months: 12 }), "Only plural 'months' property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/argument-timezone-field.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/argument-timezone-field.js
new file mode 100644
index 0000000000..1c502ac58f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/argument-timezone-field.js
@@ -0,0 +1,14 @@
+// |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.plainyearmonth.prototype.with
+description: Throw if the argument has a timeZone field
+features: [Temporal]
+---*/
+
+const ym = Temporal.PlainYearMonth.from("2019-10");
+assert.throws(TypeError, () => ym.with({ year: 2021, timeZone: "UTC" }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/basic.js
new file mode 100644
index 0000000000..23e536c3ec
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/basic.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.plainyearmonth.prototype.with
+description: Basic tests for with
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const ym = Temporal.PlainYearMonth.from("2019-10");
+
+TemporalHelpers.assertPlainYearMonth(ym.with({ year: 2020 }), 2020, 10, "M10", "year");
+TemporalHelpers.assertPlainYearMonth(ym.with({ month: 9 }), 2019, 9, "M09", "month");
+TemporalHelpers.assertPlainYearMonth(ym.with({ monthCode: "M09" }), 2019, 9, "M09", "monthCode");
+
+assert.throws(RangeError, () => ym.with({ month: 9, monthCode: "M10" }), "month/monthCode mismatch");
+TemporalHelpers.assertPlainYearMonth(ym.with({ month: 1, years: 2020 }), 2019, 1, "M01", "plural 'years'");
+
+const withDay = ym.with({ year: 2019, get day() { throw new Test262Error("should not read the day property") } });
+TemporalHelpers.assertPlainYearMonth(withDay, 2019, 10, "M10", "day property");
+assert.sameValue(withDay.getISOFields().isoDay, 1);
+
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/branding.js
new file mode 100644
index 0000000000..cf57a3a840
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/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.plainyearmonth.prototype.with
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const with_ = Temporal.PlainYearMonth.prototype.with;
+
+assert.sameValue(typeof with_, "function");
+
+const args = [{ year: 2022 }];
+
+assert.throws(TypeError, () => with_.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => with_.apply(null, args), "null");
+assert.throws(TypeError, () => with_.apply(true, args), "true");
+assert.throws(TypeError, () => with_.apply("", args), "empty string");
+assert.throws(TypeError, () => with_.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => with_.apply(1, args), "1");
+assert.throws(TypeError, () => with_.apply({}, args), "plain object");
+assert.throws(TypeError, () => with_.apply(Temporal.PlainYearMonth, args), "Temporal.PlainYearMonth");
+assert.throws(TypeError, () => with_.apply(Temporal.PlainYearMonth.prototype, args), "Temporal.PlainYearMonth.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..68911e8e03
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/builtin-calendar-no-array-iteration.js
@@ -0,0 +1,23 @@
+// |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.plainyearmonth.prototype.with
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.PlainYearMonth(2023, 5, "iso8601");
+instance.with({ month: 4 });
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..61550c948d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,46 @@
+// |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.plainyearmonth.prototype.with
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const fieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "fields");
+Object.defineProperty(Temporal.Calendar.prototype, "fields", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("fields should not be looked up");
+ },
+});
+const mergeFieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "mergeFields");
+Object.defineProperty(Temporal.Calendar.prototype, "mergeFields", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("mergeFields should not be looked up");
+ },
+});
+const yearMonthFromFieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "yearMonthFromFields");
+Object.defineProperty(Temporal.Calendar.prototype, "yearMonthFromFields", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("yearMonthFromFields should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainYearMonth(2000, 5, "iso8601", 1);
+instance.with({ year: 2001 });
+
+Object.defineProperty(Temporal.Calendar.prototype, "fields", fieldsOriginal);
+Object.defineProperty(Temporal.Calendar.prototype, "mergeFields", mergeFieldsOriginal);
+Object.defineProperty(Temporal.Calendar.prototype, "yearMonthFromFields", yearMonthFromFieldsOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/builtin.js
new file mode 100644
index 0000000000..f4352832ce
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/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.plainyearmonth.prototype.with
+description: >
+ Tests that Temporal.PlainYearMonth.prototype.with
+ 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.PlainYearMonth.prototype.with),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainYearMonth.prototype.with),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainYearMonth.prototype.with),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainYearMonth.prototype.with.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/calendar-arguments.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/calendar-arguments.js
new file mode 100644
index 0000000000..c7ed6aa754
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/calendar-arguments.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.plainyearmonth.prototype.with
+description: Correct options value is passed to calendar method
+info: |
+ YearMonthFromFields ( calendar, fields [ , options ] )
+
+ 5. Let yearMonth be ? Invoke(calendar, "yearMonthFromFields", « fields, options »).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const options = {
+ extra: "property",
+};
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ yearMonthFromFields(...args) {
+ assert.sameValue(args.length, 2, "args.length");
+ assert.sameValue(typeof args[0], "object", "args[0]");
+ assert.notSameValue(args[1], options, "args[1] is a copy of options");
+ assert.sameValue(args[1].extra, "property", "All properties are copied");
+ assert.sameValue(Object.getPrototypeOf(args[1]), null, "Copy has null prototype");
+ return super.yearMonthFromFields(...args);
+ }
+}
+const plainYearMonth = new Temporal.PlainYearMonth(2000, 7, new CustomCalendar());
+const result = plainYearMonth.with({ month: 5 }, options);
+TemporalHelpers.assertPlainYearMonth(result, 2000, 5, "M05");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/calendar-fields-iterable.js
new file mode 100644
index 0000000000..cddf4d51e6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/calendar-fields-iterable.js
@@ -0,0 +1,31 @@
+// |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.plainyearmonth.prototype.with
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plainyearmonth.prototype.with step 9:
+ 9. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+const yearmonth = new Temporal.PlainYearMonth(2000, 5, calendar);
+yearmonth.with({ year: 2005 });
+
+assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/calendar-fromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/calendar-fromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..4cc5cd6348
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/calendar-fromfields-called-with-null-prototype-fields.js
@@ -0,0 +1,18 @@
+// |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.plainyearmonth.prototype.with
+description: >
+ Calendar.yearMonthFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.PlainYearMonth(2000, 5, calendar);
+instance.with({ year: 2019 });
+assert.sameValue(calendar.yearMonthFromFieldsCallCount, 1, "yearMonthFromFields should have been called on the calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/calendar-merge-fields-returns-primitive.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/calendar-merge-fields-returns-primitive.js
new file mode 100644
index 0000000000..142b9f4be0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/calendar-merge-fields-returns-primitive.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.plainyearmonth.prototype.with
+description: >
+ with() should throw a TypeError if mergeFields() returns a primitive,
+ without passing the value on to any other calendar methods
+includes: [compareArray.js, temporalHelpers.js]
+features: [BigInt, Symbol, Temporal]
+---*/
+
+[undefined, null, true, 3.14159, "bad value", Symbol("no"), 7n].forEach((primitive) => {
+ const calendar = TemporalHelpers.calendarMergeFieldsReturnsPrimitive(primitive);
+ const instance = new Temporal.PlainYearMonth(2000, 5, calendar);
+ assert.throws(TypeError, () => instance.with({ year: 2005 }), "bad return from mergeFields() throws");
+ assert.sameValue(calendar.yearMonthFromFieldsCallCount, 0, "yearMonthFromFields() never called");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/calendar-mergefields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/calendar-mergefields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..23f00d76a3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/calendar-mergefields-called-with-null-prototype-fields.js
@@ -0,0 +1,18 @@
+// |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.plainyearmonth.prototype.with
+description: >
+ Calendar.mergeFields method is called with null-prototype fields objects
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckMergeFieldsPrototypePollution();
+const instance = new Temporal.PlainYearMonth(2000, 5, calendar);
+instance.with({ year: 2019 });
+assert.sameValue(calendar.mergeFieldsCallCount, 1, "mergeFields should have been called on the calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..11b0ae41f6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/constructor-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |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.plainyearmonth.prototype.with
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const ym = new Temporal.PlainYearMonth(2023, 5, calendar);
+
+assert.throws(RangeError, () => ym.with({month: 1}));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/copies-merge-fields-object.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/copies-merge-fields-object.js
new file mode 100644
index 0000000000..795e2f4778
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/copies-merge-fields-object.js
@@ -0,0 +1,35 @@
+// |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.plainyearmonth.prototype.with
+description: The object returned from mergeFields() is copied before being passed to monthDayFromFields().
+info: |
+ sec-temporal.plainyearmonth.prototype.with steps 13–15:
+ 13. Set _fields_ to ? CalendarMergeFields(_calendar_, _fields_, _partialYearMonth_).
+ 14. Set _fields_ to ? PrepareTemporalFields(_fields_, _fieldNames_, «»).
+ 15. Return ? YearMonthFromFields(_calendar_, _fields_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "get month",
+ "get month.valueOf",
+ "call month.valueOf",
+ "get monthCode",
+ "get monthCode.toString",
+ "call monthCode.toString",
+ "get year",
+ "get year.valueOf",
+ "call year.valueOf",
+];
+
+const calendar = TemporalHelpers.calendarMergeFieldsGetters();
+const yearmonth = new Temporal.PlainYearMonth(2000, 5, calendar);
+yearmonth.with({ year: 2004 });
+
+assert.compareArray(calendar.mergeFieldsReturnOperations, expected, "getters called on mergeFields return");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/copy-properties-not-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/copy-properties-not-undefined.js
new file mode 100644
index 0000000000..ada7ba2178
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/copy-properties-not-undefined.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.plainyearmonth.prototype.with
+description: PreparePartialTemporalFields copies only defined properties of source object
+info: |
+ 4. For each value _property_ of _fieldNames_, do
+ a. Let _value_ be ? Get(_fields_, _property_).
+ b. If _value_ is not *undefined*, then
+ ...
+ iii. Perform ! CreateDataPropertyOrThrow(_result_, _property_, _value_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainYearMonth = new Temporal.PlainYearMonth(2001, 9);
+
+TemporalHelpers.assertPlainYearMonth(plainYearMonth.with({ month: 11, year: undefined }),
+ 2001, 11, "M11",
+ "only the properties that are present and defined in the plain object are copied"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/duplicate-calendar-fields.js
new file mode 100644
index 0000000000..4b11d1d737
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/duplicate-calendar-fields.js
@@ -0,0 +1,18 @@
+// |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.plainyearmonth.prototype.with
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['monthCode'], ['month'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const ym = new Temporal.PlainYearMonth(2023, 5, calendar);
+
+ assert.throws(RangeError, () => ym.with({month: 1}));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..1b056a39c4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/infinity-throws-rangeerror.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.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plainyearmonth.prototype.with
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2000, 5);
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month"].forEach((prop) => {
+ ["constrain", "reject"].forEach((overflow) => {
+ assert.throws(RangeError, () => instance.with({ [prop]: inf }, { overflow }), `${prop} property cannot be ${inf} (overflow ${overflow}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.with({ [prop]: obj }, { overflow }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/length.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/length.js
new file mode 100644
index 0000000000..7f8f2278ca
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/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.plainyearmonth.prototype.with
+description: Temporal.PlainYearMonth.prototype.with.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.PlainYearMonth.prototype.with, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/name.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/name.js
new file mode 100644
index 0000000000..052ec04c0d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/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.plainyearmonth.prototype.with
+description: Temporal.PlainYearMonth.prototype.with.name is "with".
+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.PlainYearMonth.prototype.with, "name", {
+ value: "with",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/not-a-constructor.js
new file mode 100644
index 0000000000..7c8699125d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/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.plainyearmonth.prototype.with
+description: >
+ Temporal.PlainYearMonth.prototype.with 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.PlainYearMonth.prototype.with();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainYearMonth.prototype.with), false,
+ "isConstructor(Temporal.PlainYearMonth.prototype.with)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/options-object.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/options-object.js
new file mode 100644
index 0000000000..bc0a3991e3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/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.plainyearmonth.prototype.with
+description: Empty or a function object may be used as options
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2019, 10);
+
+const result1 = instance.with({ year: 2020 }, {});
+TemporalHelpers.assertPlainYearMonth(
+ result1, 2020, 10, "M10",
+ "options may be an empty plain object"
+);
+
+const result2 = instance.with({ year: 2020 }, () => {});
+TemporalHelpers.assertPlainYearMonth(
+ result2, 2020, 10, "M10",
+ "options may be a function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/options-undefined.js
new file mode 100644
index 0000000000..5544585218
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/options-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.plainyearmonth.prototype.with
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const yearmonth = new Temporal.PlainYearMonth(2000, 2);
+const fields = { month: 13 };
+
+const explicit = yearmonth.with(fields, undefined);
+assert.sameValue(explicit.month, 12, "default overflow is constrain");
+
+const implicit = yearmonth.with(fields);
+assert.sameValue(implicit.month, 12, "default overflow is constrain");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/options-wrong-type.js
new file mode 100644
index 0000000000..9c4072a17a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/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.plainyearmonth.prototype.with
+description: TypeError thrown when options argument is a primitive
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const badOptions = [
+ null,
+ true,
+ "2021-01",
+ Symbol(),
+ 1,
+ 2n,
+];
+
+const instance = new Temporal.PlainYearMonth(2019, 10);
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.with({ year: 2020 }, value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/order-of-operations.js
new file mode 100644
index 0000000000..de2a5849d4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/order-of-operations.js
@@ -0,0 +1,74 @@
+// |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.plainyearmonth.prototype.with
+description: Properties on an object passed to with() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ // RejectObjectWithCalendarOrTimeZone
+ "get fields.calendar",
+ "get fields.timeZone",
+ // CopyDataProperties
+ "ownKeys options",
+ "getOwnPropertyDescriptor options.overflow",
+ "get options.overflow",
+ "getOwnPropertyDescriptor options.extra",
+ "get options.extra",
+ // lookup
+ "get this.calendar.fields",
+ "get this.calendar.mergeFields",
+ "get this.calendar.yearMonthFromFields",
+ // CalendarFields
+ "call this.calendar.fields",
+ // PrepareTemporalFields on receiver
+ "get this.calendar.month",
+ "call this.calendar.month",
+ "get this.calendar.monthCode",
+ "call this.calendar.monthCode",
+ "get this.calendar.year",
+ "call this.calendar.year",
+ // PrepareTemporalFields on argument
+ "get fields.month",
+ "get fields.month.valueOf",
+ "call fields.month.valueOf",
+ "get fields.monthCode",
+ "get fields.monthCode.toString",
+ "call fields.monthCode.toString",
+ "get fields.year",
+ "get fields.year.valueOf",
+ "call fields.year.valueOf",
+ // CalendarMergeFields
+ "call this.calendar.mergeFields",
+ // CalendarYearMonthFromFields
+ "call this.calendar.yearMonthFromFields",
+ // inside Calendar.p.yearMonthFromFields
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+];
+const actual = [];
+
+const calendar = TemporalHelpers.calendarObserver(actual, "this.calendar");
+const instance = new Temporal.PlainYearMonth(2000, 5, calendar);
+// clear observable operations that occurred during the constructor call
+actual.splice(0);
+
+const fields = TemporalHelpers.propertyBagObserver(actual, {
+ year: 1.7,
+ month: 1.7,
+ monthCode: "M01",
+}, "fields");
+
+const options = TemporalHelpers.propertyBagObserver(actual, {
+ overflow: "constrain",
+ extra: "property",
+}, "options");
+
+instance.with(fields, options);
+assert.compareArray(actual, expected, "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/overflow-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/overflow-invalid-string.js
new file mode 100644
index 0000000000..0b0fe17833
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/overflow-invalid-string.js
@@ -0,0 +1,31 @@
+// |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.plainyearmonth.prototype.with
+description: RangeError thrown when overflow option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-isoyearmonthfromfields step 2:
+ 2. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plainyearmonth.prototype.with step 16:
+ 16. Return ? YearMonthFromFields(_calendar_, _fields_, _options_).
+features: [Temporal]
+---*/
+
+const yearmonth = new Temporal.PlainYearMonth(2000, 5);
+
+const badOverflows = ["", "CONSTRAIN", "balance", "other string", "constra\u0131n", "reject\0"];
+for (const overflow of badOverflows) {
+ assert.throws(
+ RangeError,
+ () => yearmonth.with({ month: 8 }, { overflow }),
+ `invalid overflow ("${overflow}")`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/overflow-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/overflow-undefined.js
new file mode 100644
index 0000000000..a4060fe96a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/overflow-undefined.js
@@ -0,0 +1,29 @@
+// |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.plainyearmonth.prototype.with
+description: Fallback value for overflow option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-isoyearmonthfromfields step 2:
+ 2. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plainyearmonth.prototype.with step 16:
+ 16. Return ? YearMonthFromFields(_calendar_, _fields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const yearmonth = new Temporal.PlainYearMonth(2000, 5);
+const explicit = yearmonth.with({ month: 15 }, { overflow: undefined });
+TemporalHelpers.assertPlainYearMonth(explicit, 2000, 12, "M12", "default overflow is constrain");
+const implicit = yearmonth.with({ month: 15 }, {});
+TemporalHelpers.assertPlainYearMonth(implicit, 2000, 12, "M12", "default overflow is constrain");
+const lambda = yearmonth.with({ month: 15 }, () => {});
+TemporalHelpers.assertPlainYearMonth(lambda, 2000, 12, "M12", "default overflow is constrain");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/overflow-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/overflow-wrong-type.js
new file mode 100644
index 0000000000..6003269773
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/overflow-wrong-type.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.plainyearmonth.prototype.with
+description: Type conversions for overflow option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-isoyearmonthfromfields step 2:
+ 2. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plainyearmonth.prototype.with step 16:
+ 16. Return ? YearMonthFromFields(_calendar_, _fields_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const yearmonth = new Temporal.PlainYearMonth(2000, 5);
+TemporalHelpers.checkStringOptionWrongType("overflow", "constrain",
+ (overflow) => yearmonth.with({ month: 8 }, { overflow }),
+ (result, descr) => TemporalHelpers.assertPlainYearMonth(result, 2000, 8, "M08", descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/prop-desc.js
new file mode 100644
index 0000000000..4fb0f4541b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/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.plainyearmonth.prototype.with
+description: The "with" property of Temporal.PlainYearMonth.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainYearMonth.prototype.with,
+ "function",
+ "`typeof PlainYearMonth.prototype.with` is `function`"
+);
+
+verifyProperty(Temporal.PlainYearMonth.prototype, "with", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/proto-in-calendar-fields.js
new file mode 100644
index 0000000000..b24f66a0f0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/proto-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |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.plainyearmonth.prototype.with
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const ym = new Temporal.PlainYearMonth(2023, 5, calendar);
+
+assert.throws(RangeError, () => ym.with({month: 1}));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/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/PlainYearMonth/prototype/with/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/subclassing-ignored.js
new file mode 100644
index 0000000000..1a7e7899da
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/subclassing-ignored.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.plainyearmonth.prototype.with
+description: Objects of a subclass are never created as return values for with()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.PlainYearMonth,
+ [2000, 5],
+ "with",
+ [{ month: 11 }],
+ (result) => TemporalHelpers.assertPlainYearMonth(result, 2000, 11, "M11"),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/year/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/year/branding.js
new file mode 100644
index 0000000000..7edf70a33b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/year/branding.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-get-temporal.plainyearmonth.prototype.year
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const year = Object.getOwnPropertyDescriptor(Temporal.PlainYearMonth.prototype, "year").get;
+
+assert.sameValue(typeof year, "function");
+
+assert.throws(TypeError, () => year.call(undefined), "undefined");
+assert.throws(TypeError, () => year.call(null), "null");
+assert.throws(TypeError, () => year.call(true), "true");
+assert.throws(TypeError, () => year.call(""), "empty string");
+assert.throws(TypeError, () => year.call(Symbol()), "symbol");
+assert.throws(TypeError, () => year.call(1), "1");
+assert.throws(TypeError, () => year.call({}), "plain object");
+assert.throws(TypeError, () => year.call(Temporal.PlainYearMonth), "Temporal.PlainYearMonth");
+assert.throws(TypeError, () => year.call(Temporal.PlainYearMonth.prototype), "Temporal.PlainYearMonth.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/year/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/year/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/year/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/year/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/year/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..94ffaec418
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/year/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.plainyearmonth.prototype.year
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const yearOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "year");
+Object.defineProperty(Temporal.Calendar.prototype, "year", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("year should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainYearMonth(2000, 5, "iso8601", 1);
+instance.year;
+
+Object.defineProperty(Temporal.Calendar.prototype, "year", yearOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/year/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/year/custom.js
new file mode 100644
index 0000000000..35d879d280
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/year/custom.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-get-temporal.plainyearmonth.prototype.year
+description: Custom calendar tests for year().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ year(...args) {
+ ++calls;
+ assert.compareArray(args, [instance], "year arguments");
+ return 7;
+ }
+}
+
+const calendar = new CustomCalendar();
+const instance = new Temporal.PlainYearMonth(1830, 8, calendar);
+const result = instance.year;
+assert.sameValue(result, 7, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/year/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/year/prop-desc.js
new file mode 100644
index 0000000000..01acf1b0ae
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/year/prop-desc.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-get-temporal.plainyearmonth.prototype.year
+description: The "year" property of Temporal.PlainYearMonth.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainYearMonth.prototype, "year");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/year/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/year/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/year/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/year/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/year/validate-calendar-value.js
new file mode 100644
index 0000000000..81ee25a1af
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/year/validate-calendar-value.js
@@ -0,0 +1,54 @@
+// |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-get-temporal.plainyearmonth.prototype.year
+description: Validate result returned from calendar year() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [Infinity, RangeError],
+ [-Infinity, RangeError],
+ [Symbol("foo"), TypeError],
+ [7n, TypeError],
+ [NaN, RangeError],
+ ["string", TypeError],
+ [{}, TypeError],
+ [null, TypeError],
+ [true, TypeError],
+ [false, TypeError],
+ [7.1, RangeError],
+ [-0.1, RangeError],
+ ["7", TypeError],
+ ["7.5", TypeError],
+ [{valueOf() { return 7; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ year() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainYearMonth(1981, 12, calendar);
+ assert.throws(error, () => instance.year, `${typeof result} ${String(result)} not converted to integer`);
+});
+
+const preservedResults = [
+ -7,
+];
+
+preservedResults.forEach(result => {
+ const calendar = new class extends Temporal.Calendar {
+ year() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainYearMonth(1981, 12, calendar);
+ assert.sameValue(instance.year, result, `${typeof result} ${String(result)} preserved`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/refisoday-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/refisoday-undefined.js
new file mode 100644
index 0000000000..57b7d16f12
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/refisoday-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.plainyearmonth
+description: referenceISODay argument defaults to 1 if not given
+features: [Temporal]
+---*/
+
+const calendar = new Temporal.Calendar("iso8601");
+const args = [2000, 5, calendar];
+
+const dateExplicit = new Temporal.PlainYearMonth(...args, undefined);
+assert.sameValue(dateExplicit.getISOFields().isoDay, 1, "default referenceISODay is 1");
+
+const dateImplicit = new Temporal.PlainYearMonth(...args);
+assert.sameValue(dateImplicit.getISOFields().isoDay, 1, "default referenceISODay is 1");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/subclass.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/subclass.js
new file mode 100644
index 0000000000..6b7dc1d850
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/subclass.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.plainyearmonth
+description: Test for Temporal.PlainYearMonth subclassing.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+class CustomPlainYearMonth extends Temporal.PlainYearMonth {
+}
+
+const instance = new CustomPlainYearMonth(2000, 5);
+TemporalHelpers.assertPlainYearMonth(instance, 2000, 5, "M05");
+assert.sameValue(Object.getPrototypeOf(instance), CustomPlainYearMonth.prototype, "Instance of CustomPlainYearMonth");
+assert(instance instanceof CustomPlainYearMonth, "Instance of CustomPlainYearMonth");
+assert(instance instanceof Temporal.PlainYearMonth, "Instance of Temporal.PlainYearMonth");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/argument-wrong-type.js
new file mode 100644
index 0000000000..f6f315918b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/argument-wrong-type.js
@@ -0,0 +1,33 @@
+// |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.timezone
+description: RangeError thrown when constructor invoked with the wrong type
+features: [Temporal]
+---*/
+
+const tests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [19761118, "number that would convert to a valid ISO string in other contexts"],
+ [1n, "bigint"],
+ [Symbol(), "symbol"],
+ [{}, "object not implementing any protocol"],
+ [new Temporal.Calendar("iso8601"), "calendar instance"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.ZonedDateTime.from("2020-01-01T00:00Z[UTC]"), "ZonedDateTime instance"],
+];
+
+for (const [arg, description] of tests) {
+ assert.throws(
+ typeof (arg) === "string" ? RangeError : TypeError,
+ () => new Temporal.TimeZone(arg),
+ `${description} is not accepted by this constructor`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/basic.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/basic.js
new file mode 100644
index 0000000000..8caadfe244
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/basic.js
@@ -0,0 +1,33 @@
+// |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.timezone
+description: Basic tests for the Temporal.TimeZone constructor.
+features: [Temporal]
+---*/
+
+const valid = [
+ ["+01:00"],
+ ["-01:00"],
+ ["+0330", "+03:30"],
+ ["-0650", "-06:50"],
+ ["-08", "-08:00"],
+ ["\u221201:00", "-01:00"],
+ ["\u22120650", "-06:50"],
+ ["\u221208", "-08:00"],
+ ["UTC"],
+];
+for (const [zone, id = zone] of valid) {
+ const result = new Temporal.TimeZone(zone);
+ assert.sameValue(typeof result, "object", `object should be created for ${zone}`);
+ assert.sameValue(result.id, id, `id for ${zone} should be ${id}`);
+}
+
+const invalid = ["+00:01.1", "-01.1", "+01:00:00", "-010000", "+03:30:00.000000001", "-033000.1"];
+for (const zone of invalid) {
+ assert.throws(RangeError, () => new Temporal.TimeZone(zone), `should throw for ${zone}`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/browser.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/builtin.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/builtin.js
new file mode 100644
index 0000000000..170c2c37a1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/builtin.js
@@ -0,0 +1,30 @@
+// |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.timezone
+description: Tests that Temporal.TimeZone meets the requirements for built-in objects
+info: |
+ 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.TimeZone),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.TimeZone),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.TimeZone),
+ Function.prototype, "prototype");
+
+assert.sameValue(typeof Temporal.TimeZone.prototype,
+ "object", "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/constructor.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/constructor.js
new file mode 100644
index 0000000000..d5d3954946
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/constructor.js
@@ -0,0 +1,15 @@
+// |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.timezone
+description: Temporal.TimeZone constructor cannot be called as a function
+info: |
+ 1. If NewTarget is undefined, throw a TypeError exception.
+features: [Temporal]
+---*/
+
+assert.throws(TypeError, () => Temporal.TimeZone("UTC"));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/from/argument-object.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/from/argument-object.js
new file mode 100644
index 0000000000..ced8f00180
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/from/argument-object.js
@@ -0,0 +1,40 @@
+// |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.timezone.from
+description: An object is returned unchanged
+features: [Temporal]
+---*/
+
+class CustomTimeZone extends Temporal.TimeZone {}
+
+const objects = [
+ new Temporal.TimeZone("UTC"),
+ new CustomTimeZone("UTC"),
+ { id: "Etc/Custom", getPossibleInstantsFor: null, getOffsetNanosecondsFor: null },
+];
+
+const thisValues = [
+ Temporal.TimeZone,
+ CustomTimeZone,
+ {},
+ null,
+ undefined,
+ 7,
+];
+
+for (const thisValue of thisValues) {
+ for (const object of objects) {
+ const result = Temporal.TimeZone.from.call(thisValue, object);
+ assert.sameValue(result, object);
+ }
+
+ const zdt = new Temporal.ZonedDateTime(0n, "UTC");
+ const fromZdt = Temporal.TimeZone.from.call(thisValue, zdt);
+ assert.notSameValue(fromZdt, zdt.getTimeZone(), "from() creates a new object from a string slot value");
+ assert.sameValue(fromZdt.id, "UTC");
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/from/argument-primitive.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/from/argument-primitive.js
new file mode 100644
index 0000000000..d48206b3ad
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/from/argument-primitive.js
@@ -0,0 +1,52 @@
+// |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.timezone.from
+description: RangeError thrown if a value is passed that converts to an invalid string
+features: [Temporal]
+---*/
+
+class CustomTimeZone extends Temporal.TimeZone {}
+
+const primitives = [
+ undefined,
+ null,
+ true,
+ "string",
+ "local",
+ "Z",
+ "-00:00[UTC]",
+ "+00:01.1",
+ "-01.1",
+ "1994-11-05T08:15:30+25:00",
+ "1994-11-05T13:15:30-25:00",
+ "+01:00:00",
+ "-010000",
+ "+03:30:00.000000001",
+ "-033000.1",
+ 7,
+ 4.2,
+ 12n,
+];
+
+const thisValues = [
+ Temporal.TimeZone,
+ CustomTimeZone,
+ {},
+ null,
+ undefined,
+ 7,
+];
+
+for (const thisValue of thisValues) {
+ for (const primitive of primitives) {
+ assert.throws(typeof primitive === 'string' ? RangeError : TypeError, () => Temporal.TimeZone.from.call(thisValue, primitive));
+ }
+
+ const symbol = Symbol();
+ assert.throws(TypeError, () => Temporal.TimeZone.from.call(thisValue, symbol));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/from/argument-valid.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/from/argument-valid.js
new file mode 100644
index 0000000000..b00924b070
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/from/argument-valid.js
@@ -0,0 +1,33 @@
+// |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.timezone.from
+description: Built-in time zones are parsed correctly out of valid strings
+features: [Temporal]
+---*/
+
+const valids = [
+ ["+01:00"],
+ ["-01:00"],
+ ["+0330", "+03:30"],
+ ["-0650", "-06:50"],
+ ["-08", "-08:00"],
+ ["\u221201:00", "-01:00"],
+ ["\u22120650", "-06:50"],
+ ["\u221208", "-08:00"],
+ ["UTC"],
+ ["1994-11-05T08:15:30-05:00", "-05:00"],
+ ["1994-11-05T08:15:30\u221205:00", "-05:00"],
+ ["1994-11-05T13:15:30Z", "UTC"],
+];
+
+for (const [valid, canonical = valid] of valids) {
+ const result = Temporal.TimeZone.from(valid);
+ assert.sameValue(Object.getPrototypeOf(result), Temporal.TimeZone.prototype);
+ assert.sameValue(result.id, canonical);
+ assert.sameValue(result.toString(), canonical);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/from/browser.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/from/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/from/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/from/builtin.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/from/builtin.js
new file mode 100644
index 0000000000..567578a79c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/from/builtin.js
@@ -0,0 +1,33 @@
+// |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.timezone.from
+description: Tests that Temporal.TimeZone.from meets the requirements for built-in objects
+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.TimeZone.from),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.TimeZone.from),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.TimeZone.from),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.TimeZone.from.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/from/length.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/from/length.js
new file mode 100644
index 0000000000..96cacf33df
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/from/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.timezone.from
+description: Temporal.TimeZone.from.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.TimeZone.from, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/from/name.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/from/name.js
new file mode 100644
index 0000000000..c91213d08a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/from/name.js
@@ -0,0 +1,26 @@
+// |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.timezone.from
+description: Temporal.TimeZone.from.name is "from"
+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.TimeZone.from, "name", {
+ value: "from",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/from/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/from/not-a-constructor.js
new file mode 100644
index 0000000000..06b96c1478
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/from/not-a-constructor.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.timezone.from
+description: Temporal.TimeZone.from 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.TimeZone.from();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.TimeZone.from), false,
+ "isConstructor(Temporal.TimeZone.from)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/from/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/from/prop-desc.js
new file mode 100644
index 0000000000..37670748e3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/from/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.timezone.from
+description: The "from" property of Temporal.TimeZone
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.TimeZone.from,
+ "function",
+ "`typeof TimeZone.from` is `function`"
+);
+
+verifyProperty(Temporal.TimeZone, "from", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/from/shell.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/from/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/from/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/TimeZone/from/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/from/subclassing-ignored.js
new file mode 100644
index 0000000000..30f3366772
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/from/subclassing-ignored.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.timezone.from
+description: The receiver is never called when calling from()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnoredStatic(
+ Temporal.TimeZone,
+ "from",
+ ["UTC"],
+ (result) => {
+ assert.sameValue(result.id, "UTC", "id property of result");
+ assert.sameValue(result.toString(), "UTC", "toString() of result");
+ },
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/from/timezone-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/from/timezone-case-insensitive.js
new file mode 100644
index 0000000000..7467ed06ce
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/from/timezone-case-insensitive.js
@@ -0,0 +1,15 @@
+// |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.timezone.from
+description: Time zone names are case insensitive
+features: [Temporal]
+---*/
+
+const timeZone = 'UtC';
+const result = Temporal.TimeZone.from(timeZone);
+assert.sameValue(result.id, 'UTC', `Time zone created from string "${timeZone}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/from/timezone-string-datetime.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/from/timezone-string-datetime.js
new file mode 100644
index 0000000000..79d149683f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/from/timezone-string-datetime.js
@@ -0,0 +1,63 @@
+// |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.timezone.from
+description: Conversion of ISO date-time strings to Temporal.TimeZone instances
+features: [Temporal]
+---*/
+
+let timeZone = "2021-08-19T17:30";
+assert.throws(RangeError, () => Temporal.TimeZone.from(timeZone), "bare date-time string is not a time zone");
+
+[
+ "2021-08-19T17:30-07:00:01",
+ "2021-08-19T17:30-07:00:00",
+ "2021-08-19T17:30-07:00:00.1",
+ "2021-08-19T17:30-07:00:00.0",
+ "2021-08-19T17:30-07:00:00.01",
+ "2021-08-19T17:30-07:00:00.00",
+ "2021-08-19T17:30-07:00:00.001",
+ "2021-08-19T17:30-07:00:00.000",
+ "2021-08-19T17:30-07:00:00.0001",
+ "2021-08-19T17:30-07:00:00.0000",
+ "2021-08-19T17:30-07:00:00.00001",
+ "2021-08-19T17:30-07:00:00.00000",
+ "2021-08-19T17:30-07:00:00.000001",
+ "2021-08-19T17:30-07:00:00.000000",
+ "2021-08-19T17:30-07:00:00.0000001",
+ "2021-08-19T17:30-07:00:00.0000000",
+ "2021-08-19T17:30-07:00:00.00000001",
+ "2021-08-19T17:30-07:00:00.00000000",
+ "2021-08-19T17:30-07:00:00.000000001",
+ "2021-08-19T17:30-07:00:00.000000000",
+].forEach((timeZone) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.TimeZone.from(timeZone),
+ `ISO string ${timeZone} with a sub-minute offset is not a valid time zone`
+ );
+});
+
+timeZone = "2021-08-19T17:30Z";
+const result1 = Temporal.TimeZone.from(timeZone);
+assert.sameValue(result1.id, "UTC", "date-time + Z is UTC time zone");
+
+timeZone = "2021-08-19T17:30-07:00";
+const result2 = Temporal.TimeZone.from(timeZone);
+assert.sameValue(result2.id, "-07:00", "date-time + offset is the offset time zone");
+
+timeZone = "2021-08-19T17:30[UTC]";
+const result3 = Temporal.TimeZone.from(timeZone);
+assert.sameValue(result3.id, "UTC", "date-time + IANA annotation is the IANA time zone");
+
+timeZone = "2021-08-19T17:30Z[UTC]";
+const result4 = Temporal.TimeZone.from(timeZone);
+assert.sameValue(result4.id, "UTC", "date-time + Z + IANA annotation is the IANA time zone");
+
+timeZone = "2021-08-19T17:30-07:00[UTC]";
+const result5 = Temporal.TimeZone.from(timeZone);
+assert.sameValue(result5.id, "UTC", "date-time + offset + IANA annotation is the IANA time zone");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/from/timezone-string-leap-second.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/from/timezone-string-leap-second.js
new file mode 100644
index 0000000000..c298d338c4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/from/timezone-string-leap-second.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.timezone.from
+description: Leap second is a valid ISO string for TimeZone
+features: [Temporal]
+---*/
+
+let timeZone = "2016-12-31T23:59:60+00:00[UTC]";
+
+const result = Temporal.TimeZone.from(timeZone);
+assert.sameValue(result.id, "UTC", "leap second is a valid ISO string for TimeZone");
+
+timeZone = "2021-08-19T17:30:45.123456789+23:59[+23:59:60]";
+assert.throws(RangeError, () => Temporal.TimeZone.from(timeZone), "leap second in time zone name not valid");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/from/timezone-string-multiple-offsets.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/from/timezone-string-multiple-offsets.js
new file mode 100644
index 0000000000..d99aaaed8c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/from/timezone-string-multiple-offsets.js
@@ -0,0 +1,16 @@
+// |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.timezone.from
+description: Time zone parsing from ISO strings uses the bracketed offset, not the ISO string offset
+features: [Temporal]
+---*/
+
+const timeZone = "2021-08-19T17:30:45.123456789-12:12[+01:46]";
+
+const result = Temporal.TimeZone.from(timeZone);
+assert.sameValue(result.id, "+01:46", "Time zone string determined from bracket name");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/from/timezone-string-year-zero.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/from/timezone-string-year-zero.js
new file mode 100644
index 0000000000..b07b9ced68
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/from/timezone-string-year-zero.js
@@ -0,0 +1,23 @@
+// |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.timezone.from
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+invalidStrings.forEach((timeZone) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.TimeZone.from(timeZone),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/from/timezone-string.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/from/timezone-string.js
new file mode 100644
index 0000000000..54c744fe5a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/from/timezone-string.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.timezone.from
+description: Time zone IDs are valid input for a time zone
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getPossibleInstantsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getPossibleInstantsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be looked up");
+ },
+});
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+
+["UTC", "+01:30"].forEach((timeZone) => {
+ const result = Temporal.TimeZone.from(timeZone);
+ assert.sameValue(result.id, timeZone, `Time zone created from string "${timeZone}"`);
+});
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", getPossibleInstantsForOriginal);
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/from/timezone-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/from/timezone-wrong-type.js
new file mode 100644
index 0000000000..6ee0463840
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/from/timezone-wrong-type.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.timezone.from
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or object for TimeZone
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [19761118, "number that would convert to a valid ISO string in other contexts"],
+ [1n, "bigint"],
+];
+
+for (const [timeZone, description] of primitiveTests) {
+ assert.throws(
+ typeof timeZone === 'string' ? RangeError : TypeError,
+ () => Temporal.TimeZone.from(timeZone),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "object not implementing time zone protocol"],
+ [new Temporal.Calendar("iso8601"), "calendar instance"],
+];
+
+for (const [timeZone, description] of typeErrorTests) {
+ assert.throws(TypeError, () => Temporal.TimeZone.from(timeZone), `${description} is not a valid object and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/length.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/length.js
new file mode 100644
index 0000000000..673712afbb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/length.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.timezone
+description: Temporal.TimeZone.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.TimeZone, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/missing-arguments.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/missing-arguments.js
new file mode 100644
index 0000000000..338b1e0711
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/missing-arguments.js
@@ -0,0 +1,14 @@
+// |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.timezone
+description: TypeError thrown when constructor invoked with no argument
+features: [Temporal]
+---*/
+
+assert.throws(TypeError, () => new Temporal.TimeZone());
+assert.throws(TypeError, () => new Temporal.TimeZone(undefined));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/name.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/name.js
new file mode 100644
index 0000000000..7837265e52
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/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.timezone
+description: Temporal.TimeZone.name is "TimeZone"
+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.TimeZone, "name", {
+ value: "TimeZone",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prop-desc.js
new file mode 100644
index 0000000000..cccc5d5bad
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/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.timezone
+description: The "TimeZone" property of Temporal
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.TimeZone,
+ "function",
+ "`typeof TimeZone` is `function`"
+);
+
+verifyProperty(Temporal, "TimeZone", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/browser.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/constructor.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/constructor.js
new file mode 100644
index 0000000000..b84be5c78b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/constructor.js
@@ -0,0 +1,20 @@
+// |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.timezone.prototype.constructor
+description: Test for Temporal.TimeZone.prototype.constructor.
+info: The initial value of Temporal.TimeZone.prototype.constructor is %Temporal.TimeZone%.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.TimeZone.prototype, "constructor", {
+ value: Temporal.TimeZone,
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/equals/argument-object.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/equals/argument-object.js
new file mode 100644
index 0000000000..196f32b1a9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/equals/argument-object.js
@@ -0,0 +1,106 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Justin Grant. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.equals
+description: Tests that objects can be compared for equality
+features: [Temporal]
+---*/
+
+class CustomTimeZone extends Temporal.TimeZone {
+ constructor(id) {
+ super("UTC");
+ this._id = id;
+ }
+ get id() {
+ return this._id;
+ }
+}
+
+const objectsEqualUTC = [
+ new Temporal.TimeZone("UTC"),
+ new CustomTimeZone("UTC"),
+ { id: "UTC", getPossibleInstantsFor: null, getOffsetNanosecondsFor: null },
+ new Temporal.ZonedDateTime(0n, "UTC")
+];
+
+const tzUTC = new Temporal.TimeZone("UTC");
+
+for (const object of objectsEqualUTC) {
+ const result = tzUTC.equals(object);
+ assert.sameValue(result, true, `Receiver ${tzUTC.id} should equal argument ${object.id}`);
+}
+
+const objectsEqual0000 = [
+ new Temporal.TimeZone("+00:00"),
+ new Temporal.TimeZone("+0000"),
+ new Temporal.TimeZone("+00"),
+ new CustomTimeZone("+00:00"),
+ new CustomTimeZone("+0000"),
+ new CustomTimeZone("+00"),
+ { id: "+00:00", getPossibleInstantsFor: null, getOffsetNanosecondsFor: null },
+ { id: "+0000", getPossibleInstantsFor: null, getOffsetNanosecondsFor: null },
+ { id: "+00", getPossibleInstantsFor: null, getOffsetNanosecondsFor: null },
+ new Temporal.ZonedDateTime(0n, "+00:00"),
+ new Temporal.ZonedDateTime(0n, "+0000"),
+ new Temporal.ZonedDateTime(0n, "+00"),
+ "+00:00",
+ "+0000",
+ "+00"
+];
+
+const tz0000ToTest = [
+ new Temporal.TimeZone("+00:00"),
+ new Temporal.TimeZone("+0000"),
+ new Temporal.TimeZone("+00"),
+ new CustomTimeZone("+00:00"),
+ new CustomTimeZone("+0000"),
+ new CustomTimeZone("+00")
+];
+
+for (const arg of objectsEqual0000) {
+ for (const receiver of tz0000ToTest) {
+ const result = receiver.equals(arg);
+ assert.sameValue(result, true, `Receiver ${receiver.id} should equal argument ${arg.id ?? arg}`);
+ }
+}
+
+const objectsNotEqual = [
+ new Temporal.TimeZone("+00:00"),
+ new CustomTimeZone("+00:00"),
+ new CustomTimeZone("Etc/Custom"),
+ { id: "+00:00", getPossibleInstantsFor: null, getOffsetNanosecondsFor: null },
+ { id: "Etc/Custom", getPossibleInstantsFor: null, getOffsetNanosecondsFor: null },
+ new Temporal.ZonedDateTime(0n, "+00:00"),
+ "UTC"
+];
+
+const customObjectsToTest = [tzUTC, new CustomTimeZone("YouTeeSee"), new CustomTimeZone("+01:00")];
+
+for (const arg of objectsNotEqual) {
+ for (const receiver of customObjectsToTest) {
+ if (arg === "UTC" && receiver === tzUTC) continue;
+ const result = receiver.equals(arg);
+ assert.sameValue(result, false, `Receiver ${receiver.id} should not equal argument ${arg.id ?? arg}`);
+ }
+}
+
+// Custom object IDs are compared case-sensitively
+const classInstanceCustomId = new CustomTimeZone("Moon/Cheese");
+const classInstanceSameCaseCustomId = new CustomTimeZone("Moon/Cheese");
+const classInstanceDifferentCaseCustomId = new CustomTimeZone("MoOn/CHEESe");
+
+const plainObjectSameCaseCustomId = { id: "Moon/Cheese", getPossibleInstantsFor: null, getOffsetNanosecondsFor: null };
+const plainObjectDifferentCaseCustomId = {
+ id: "MoOn/CHEESe",
+ getPossibleInstantsFor: null,
+ getOffsetNanosecondsFor: null
+};
+
+assert.sameValue(classInstanceCustomId.equals(classInstanceSameCaseCustomId), true);
+assert.sameValue(classInstanceCustomId.equals(classInstanceDifferentCaseCustomId), false);
+assert.sameValue(classInstanceCustomId.equals(plainObjectSameCaseCustomId), true);
+assert.sameValue(classInstanceCustomId.equals(plainObjectDifferentCaseCustomId), false);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/equals/argument-primitive.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/equals/argument-primitive.js
new file mode 100644
index 0000000000..023206985d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/equals/argument-primitive.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Justin Grant. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.from
+description: Exceptions thrown if a value is passed that converts to an invalid string
+features: [Temporal]
+---*/
+
+const primitives = [
+ undefined,
+ null,
+ true,
+ "string",
+ "local",
+ "Z",
+ "-00:00[UTC]",
+ "+00:01.1",
+ "-01.1",
+ "1994-11-05T08:15:30+25:00",
+ "1994-11-05T13:15:30-25:00",
+ 7,
+ 4.2,
+ 12n
+];
+
+const tzUTC = new Temporal.TimeZone("UTC");
+for (const primitive of primitives) {
+ assert.throws(typeof primitive === "string" ? RangeError : TypeError, () => tzUTC.equals(primitive));
+}
+
+const symbol = Symbol();
+assert.throws(TypeError, () => tzUTC.equals(symbol));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/equals/argument-valid.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/equals/argument-valid.js
new file mode 100644
index 0000000000..79af622fe4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/equals/argument-valid.js
@@ -0,0 +1,48 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Justin Grant. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.from
+description: Built-in time zones are compared correctly out of valid strings
+features: [Temporal]
+---*/
+
+const validsEqual = [
+ ["+0330", "+03:30"],
+ ["-0650", "-06:50"],
+ ["-08", "-08:00"],
+ ["\u221201:00", "-01:00"],
+ ["\u22120650", "-06:50"],
+ ["\u221208", "-08:00"],
+ ["1994-11-05T08:15:30-05:00", "-05:00"],
+ ["1994-11-05T08:15:30\u221205:00", "-05:00"],
+ ["1994-11-05T13:15:30Z", "UTC"]
+];
+
+for (const [valid, canonical] of validsEqual) {
+ const tzValid = Temporal.TimeZone.from(valid);
+ const tzCanonical = Temporal.TimeZone.from(canonical);
+ assert.sameValue(tzValid.equals(canonical), true);
+ assert.sameValue(tzCanonical.equals(valid), true);
+}
+
+const validsNotEqual = [
+ ["+0330", "+03:31"],
+ ["-0650", "-06:51"],
+ ["-08", "-08:01"],
+ ["\u221201:00", "-01:01"],
+ ["\u22120650", "-06:51"],
+ ["\u221208", "-08:01"],
+ ["1994-11-05T08:15:30-05:00", "-05:01"],
+ ["1994-11-05T08:15:30\u221205:00", "-05:01"]
+];
+
+for (const [valid, canonical] of validsNotEqual) {
+ const tzValid = Temporal.TimeZone.from(valid);
+ const tzCanonical = Temporal.TimeZone.from(canonical);
+ assert.sameValue(tzValid.equals(canonical), false);
+ assert.sameValue(tzCanonical.equals(valid), false);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/equals/branding.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/equals/branding.js
new file mode 100644
index 0000000000..5673b192e4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/equals/branding.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Justin Grant. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.equals
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const equals = Temporal.TimeZone.prototype.equals;
+
+assert.sameValue(typeof equals, "function");
+
+const args = ["UTC"];
+
+assert.throws(TypeError, () => equals.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => equals.apply(null, args), "null");
+assert.throws(TypeError, () => equals.apply(true, args), "true");
+assert.throws(TypeError, () => equals.apply("", args), "empty string");
+assert.throws(TypeError, () => equals.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => equals.apply(1, args), "1");
+assert.throws(TypeError, () => equals.apply({}, args), "plain object");
+assert.throws(TypeError, () => equals.apply(Temporal.TimeZone, args), "Temporal.TimeZone");
+assert.throws(TypeError, () => equals.apply(Temporal.TimeZone.prototype, args), "Temporal.TimeZone.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/equals/browser.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/equals/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/equals/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/equals/builtin.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/equals/builtin.js
new file mode 100644
index 0000000000..02c46c0d3b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/equals/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Justin Grant. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.equals
+description: >
+ Tests that Temporal.TimeZone.prototype.equals
+ 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.TimeZone.prototype.equals),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.TimeZone.prototype.equals),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.TimeZone.prototype.equals),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.TimeZone.prototype.equals.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/equals/id-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/equals/id-wrong-type.js
new file mode 100644
index 0000000000..b8fa629020
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/equals/id-wrong-type.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Justin Grant. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.equals
+description: TypeError thrown if time zone reports an id that is not a String
+features: [Temporal]
+---*/
+
+class CustomTimeZone extends Temporal.TimeZone {
+ constructor(id) {
+ super("UTC");
+ this._id = id;
+ }
+ get id() {
+ return this._id;
+ }
+}
+
+[
+ undefined,
+ null,
+ true,
+ -1000,
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ {
+ valueOf() {
+ return 3600_000_000_000;
+ }
+ }
+].forEach((wrongId) => {
+ const timeZoneWrong = new CustomTimeZone(wrongId);
+ const timeZoneOK = new Temporal.TimeZone('UTC');
+ assert.throws(TypeError, () => timeZoneWrong.equals(timeZoneOK));
+ assert.throws(TypeError, () => timeZoneOK.equals(timeZoneWrong));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/equals/length.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/equals/length.js
new file mode 100644
index 0000000000..61c65195ff
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/equals/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Justin Grant. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.equals
+description: Temporal.TimeZone.prototype.equals.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.TimeZone.prototype.equals, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/equals/name.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/equals/name.js
new file mode 100644
index 0000000000..df1828acc7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/equals/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Justin Grant. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.equals
+description: Temporal.TimeZone.prototype.equals.name is "equals".
+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.TimeZone.prototype.equals, "name", {
+ value: "equals",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/equals/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/equals/not-a-constructor.js
new file mode 100644
index 0000000000..e01067b313
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/equals/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Justin Grant. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.equals
+description: >
+ Temporal.TimeZone.prototype.equals 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.TimeZone.prototype.equals();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.TimeZone.prototype.equals), false,
+ "isConstructor(Temporal.TimeZone.prototype.equals)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/equals/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/equals/prop-desc.js
new file mode 100644
index 0000000000..deb16a6e1c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/equals/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Justin Grant. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.equals
+description: The "equals" property of Temporal.TimeZone.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.TimeZone.prototype.equals,
+ "function",
+ "`typeof TimeZone.prototype.equals` is `function`"
+);
+
+verifyProperty(Temporal.TimeZone.prototype, "equals", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/equals/shell.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/equals/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/equals/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/TimeZone/prototype/equals/timezone-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/equals/timezone-case-insensitive.js
new file mode 100644
index 0000000000..28d4a0de6f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/equals/timezone-case-insensitive.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Justin Grant. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.equals
+description: Time zone names are case insensitive
+features: [Temporal]
+---*/
+
+const timeZone = 'UtC';
+const result = Temporal.TimeZone.from(timeZone);
+assert.sameValue(result.equals(timeZone), true);
+assert.sameValue(result.equals("+00:00"), false);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/equals/timezone-string-datetime.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/equals/timezone-string-datetime.js
new file mode 100644
index 0000000000..56cafca99f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/equals/timezone-string-datetime.js
@@ -0,0 +1,64 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Justin Grant. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.equals
+description: Conversion of ISO date-time strings to the argument of Temporal.TimeZone.prototype.equals
+features: [Temporal]
+---*/
+
+let tzUTC = Temporal.TimeZone.from("UTC");
+let arg = "2021-08-19T17:30";
+assert.throws(RangeError, () => tzUTC.equals(arg), "bare date-time string is not a time zone");
+
+[
+ "2021-08-19T17:30-07:00:01",
+ "2021-08-19T17:30-07:00:00",
+ "2021-08-19T17:30-07:00:00.1",
+ "2021-08-19T17:30-07:00:00.0",
+ "2021-08-19T17:30-07:00:00.01",
+ "2021-08-19T17:30-07:00:00.00",
+ "2021-08-19T17:30-07:00:00.001",
+ "2021-08-19T17:30-07:00:00.000",
+ "2021-08-19T17:30-07:00:00.0001",
+ "2021-08-19T17:30-07:00:00.0000",
+ "2021-08-19T17:30-07:00:00.00001",
+ "2021-08-19T17:30-07:00:00.00000",
+ "2021-08-19T17:30-07:00:00.000001",
+ "2021-08-19T17:30-07:00:00.000000",
+ "2021-08-19T17:30-07:00:00.0000001",
+ "2021-08-19T17:30-07:00:00.0000000",
+ "2021-08-19T17:30-07:00:00.00000001",
+ "2021-08-19T17:30-07:00:00.00000000",
+ "2021-08-19T17:30-07:00:00.000000001",
+ "2021-08-19T17:30-07:00:00.000000000",
+].forEach((timeZone) => {
+ assert.throws(
+ RangeError,
+ () => tzUTC.equals(timeZone),
+ `ISO string ${timeZone} with a sub-minute offset is not a valid time zone`
+ );
+});
+
+arg = "2021-08-19T17:30Z";
+tzUTC = Temporal.TimeZone.from(arg);
+assert.sameValue(tzUTC.equals(arg), true, "date-time + Z is UTC time zone");
+
+arg = "2021-08-19T17:30-07:00";
+tzUTC = Temporal.TimeZone.from(arg);
+assert.sameValue(tzUTC.equals(arg), true, "date-time + offset is the offset time zone");
+
+arg = "2021-08-19T17:30[UTC]";
+tzUTC = Temporal.TimeZone.from(arg);
+assert.sameValue(tzUTC.equals(arg), true, "date-time + IANA annotation is the IANA time zone");
+
+arg = "2021-08-19T17:30Z[UTC]";
+tzUTC = Temporal.TimeZone.from(arg);
+assert.sameValue(tzUTC.equals(arg), true, "date-time + Z + IANA annotation is the IANA time zone");
+
+arg = "2021-08-19T17:30-07:00[UTC]";
+tzUTC = Temporal.TimeZone.from(arg);
+assert.sameValue(tzUTC.equals(arg), true, "date-time + offset + IANA annotation is the IANA time zone");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/equals/timezone-string-multiple-offsets.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/equals/timezone-string-multiple-offsets.js
new file mode 100644
index 0000000000..2ac68213de
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/equals/timezone-string-multiple-offsets.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Justin Grant. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.from
+description: Time zone strings with UTC offset fractional part are not confused with time fractional part
+features: [Temporal]
+---*/
+
+const timeZone = "2021-08-19T17:30:45.123456789-12:12[+01:46]";
+
+const result = Temporal.TimeZone.from(timeZone);
+assert.sameValue(result.equals("+01:46"), true, "Time zone string determined from bracket name");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/equals/timezone-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/equals/timezone-wrong-type.js
new file mode 100644
index 0000000000..a0031d3629
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/equals/timezone-wrong-type.js
@@ -0,0 +1,46 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Justin Grant. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.from
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or object for TimeZone
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const rangeErrorTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [19761118, "number that would convert to a valid ISO string in other contexts"],
+ [1n, "bigint"]
+];
+
+const tzUTC = new Temporal.TimeZone("UTC");
+
+for (const [timeZone, description] of rangeErrorTests) {
+ assert.throws(
+ typeof timeZone === "string" ? RangeError : TypeError,
+ () => tzUTC.equals(timeZone),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "object not implementing time zone protocol"],
+ [new Temporal.Calendar("iso8601"), "calendar instance"]
+];
+
+for (const [timeZone, description] of typeErrorTests) {
+ assert.throws(
+ TypeError,
+ () => tzUTC.equals(timeZone),
+ `${description} is not a valid object and does not convert to a string`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..86aeca4e6f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-builtin-calendar-no-array-iteration.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.timezone.prototype.getinstantfor
+description: >
+ Calling the method with a property bag argument with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.TimeZone("UTC");
+// Patch getPossibleInstantsFor to allow the spec-mandated observable array
+// iteration in the GetPossibleInstantsFor AO.
+instance.getPossibleInstantsFor = function (...args) {
+ const instants = Temporal.TimeZone.prototype.getPossibleInstantsFor.apply(this, args);
+ instants[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+ return instants;
+}
+
+const arg = { year: 2000, month: 5, day: 2, hour: 21, minute: 43, second: 5, calendar: "iso8601" };
+instance.getInstantFor(arg);
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-not-datetime.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-not-datetime.js
new file mode 100644
index 0000000000..25129f6bbd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-not-datetime.js
@@ -0,0 +1,21 @@
+// |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.timezone.prototype.getinstantfor
+description: Appropriate error thrown when argument cannot be converted to Temporal.PlainDateTime
+features: [Temporal]
+---*/
+
+const timeZone = Temporal.TimeZone.from("UTC");
+assert.throws(TypeError, () => timeZone.getInstantFor(undefined), "undefined");
+assert.throws(TypeError, () => timeZone.getInstantFor(null), "null");
+assert.throws(TypeError, () => timeZone.getInstantFor(true), "boolean");
+assert.throws(RangeError, () => timeZone.getInstantFor(""), "empty string");
+assert.throws(TypeError, () => timeZone.getInstantFor(Symbol()), "Symbol");
+assert.throws(TypeError, () => timeZone.getInstantFor(5), "number");
+assert.throws(TypeError, () => timeZone.getInstantFor(5n), "bigint");
+assert.throws(TypeError, () => timeZone.getInstantFor({ year: 2020 }), "plain object");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-number.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-number.js
new file mode 100644
index 0000000000..77e4ea585a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/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.timezone.prototype.getinstantfor
+description: A number cannot be used in place of a Temporal.PlainDateTime
+features: [Temporal]
+---*/
+
+const instance = new Temporal.TimeZone("UTC");
+
+const numbers = [
+ 1,
+ 19761118,
+ -19761118,
+ 1234567890,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => instance.getInstantFor(arg),
+ `A number (${arg}) is not a valid ISO string for PlainDateTime`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-plaindate.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-plaindate.js
new file mode 100644
index 0000000000..5feb991897
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-plaindate.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.timezone.prototype.getinstantfor
+description: Fast path for converting Temporal.PlainDate to Temporal.PlainDateTime by reading internal slots
+info: |
+ sec-temporal.timezone.prototype.getinstantfor step 3:
+ 3. Set _dateTime_ to ? ToTemporalDateTime(_dateTime_).
+ sec-temporal-totemporaldatetime step 2.b:
+ b. If _item_ has an [[InitializedTemporalDate]] internal slot, then
+ i. Return ? CreateTemporalDateTime(_item_.[[ISOYear]], _item_.[[ISOMonth]], _item_.[[ISODay]], 0, 0, 0, 0, 0, 0, _item_.[[Calendar]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalPlainDateTimeFastPath((date) => {
+ const timezone = new Temporal.TimeZone("UTC");
+ const result = timezone.getInstantFor(date);
+ assert.sameValue(result.epochNanoseconds, 957_225_600_000_000_000n, "epochNanoseconds result");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-propertybag-calendar-case-insensitive.js
new file mode 100644
index 0000000000..baa227029c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-propertybag-calendar-case-insensitive.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.timezone.prototype.getinstantfor
+description: The calendar name is case-insensitive
+features: [Temporal]
+---*/
+
+const instance = new Temporal.TimeZone("UTC");
+
+const calendar = "IsO8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.getInstantFor(arg);
+assert.sameValue(result.epochNanoseconds, 217_123_200_000_000_000n, "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-propertybag-calendar-leap-second.js
new file mode 100644
index 0000000000..8dbefc0285
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-propertybag-calendar-leap-second.js
@@ -0,0 +1,23 @@
+// |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.timezone.prototype.getinstantfor
+description: Leap second is a valid ISO string for a calendar in a property bag
+features: [Temporal]
+---*/
+
+const instance = new Temporal.TimeZone("UTC");
+
+const calendar = "2016-12-31T23:59:60";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.getInstantFor(arg);
+assert.sameValue(
+ result.epochNanoseconds,
+ 217_123_200_000_000_000n,
+ "leap second is a valid ISO string for calendar"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-propertybag-calendar-number.js
new file mode 100644
index 0000000000..5c799c9c6c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-propertybag-calendar-number.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.timezone.prototype.getinstantfor
+description: A number as calendar in a property bag is not accepted
+features: [Temporal]
+---*/
+
+const instance = new Temporal.TimeZone("UTC");
+
+const numbers = [
+ 1,
+ 19970327,
+ -19970327,
+ 1234567890,
+];
+
+for (const calendar of numbers) {
+ const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+ assert.throws(
+ TypeError,
+ () => instance.getInstantFor(arg),
+ "Numbers cannot be used as a calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-propertybag-calendar-string.js
new file mode 100644
index 0000000000..050000cc46
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-propertybag-calendar-string.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.timezone.prototype.getinstantfor
+description: A calendar ID is valid input for Calendar
+features: [Temporal]
+---*/
+
+const instance = new Temporal.TimeZone("UTC");
+
+const calendar = "iso8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.getInstantFor(arg);
+assert.sameValue(result.epochNanoseconds, 217_123_200_000_000_000n, `Calendar created from string "${calendar}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-propertybag-calendar-wrong-type.js
new file mode 100644
index 0000000000..d07432774c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-propertybag-calendar-wrong-type.js
@@ -0,0 +1,46 @@
+// |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.timezone.prototype.getinstantfor
+description: >
+ Appropriate error thrown when a calendar property from a property bag cannot
+ be converted to a calendar object or string
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.TimeZone("UTC");
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [calendar, description] of primitiveTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ typeof calendar === 'string' ? RangeError : TypeError,
+ () => instance.getInstantFor(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+ [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields()
+];
+
+for (const [calendar, description] of typeErrorTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(TypeError, () => instance.getInstantFor(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/TimeZone/prototype/getInstantFor/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-propertybag-calendar-year-zero.js
new file mode 100644
index 0000000000..8065dd55fe
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-propertybag-calendar-year-zero.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.timezone.prototype.getinstantfor
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T17:45",
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+01:00",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+const instance = new Temporal.TimeZone("UTC");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.getInstantFor(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..1bb66f7648
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-string-calendar-annotation.js
@@ -0,0 +1,31 @@
+// |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.timezone.prototype.getinstantfor
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["1976-11-18T15:23[u-ca=iso8601]", "without time zone"],
+ ["1976-11-18T15:23[UTC][u-ca=iso8601]", "with time zone"],
+ ["1976-11-18T15:23[!u-ca=iso8601]", "with ! and no time zone"],
+ ["1976-11-18T15:23[UTC][!u-ca=iso8601]", "with ! and time zone"],
+ ["1976-11-18T15:23[u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+const instance = new Temporal.TimeZone("UTC");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.getInstantFor(arg);
+
+ assert.sameValue(
+ result.epochNanoseconds,
+ 217_178_580_000_000_000n,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..afc68a8834
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-string-critical-unknown-annotation.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.timezone.prototype.getinstantfor
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "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.TimeZone("UTC");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.getInstantFor(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..647003b0b1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-string-date-with-utc-offset.js
@@ -0,0 +1,48 @@
+// |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.timezone.prototype.getinstantfor
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+---*/
+
+const instance = new Temporal.TimeZone("UTC");
+
+const validStrings = [
+ "1976-11-18T15:23+00:00",
+ "1976-11-18T15:23+00:00[UTC]",
+ "1976-11-18T15:23+00:00[!UTC]",
+ "1976-11-18T15:23-02:30[America/St_Johns]",
+];
+
+for (const arg of validStrings) {
+ const result = instance.getInstantFor(arg);
+
+ assert.sameValue(
+ result.epochNanoseconds,
+ 217_178_580_000_000_000n,
+ `"${arg}" is a valid UTC offset with time for PlainDateTime`
+ );
+}
+
+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.getInstantFor(arg),
+ `"${arg}" UTC offset without time is not valid for PlainDateTime`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..caa6556187
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/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.timezone.prototype.getinstantfor
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[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.TimeZone("UTC");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.getInstantFor(arg),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..94425d1a21
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-string-multiple-time-zone.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.timezone.prototype.getinstantfor
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "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.TimeZone("UTC");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.getInstantFor(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-string-time-separators.js
new file mode 100644
index 0000000000..dc5b4da151
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-string-time-separators.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.timezone.prototype.getinstantfor
+description: Time separator in string argument can vary
+features: [Temporal]
+---*/
+
+const tests = [
+ ["1976-11-18T15:23", "uppercase T"],
+ ["1976-11-18t15:23", "lowercase T"],
+ ["1976-11-18 15:23", "space between date and time"],
+];
+
+const instance = new Temporal.TimeZone("UTC");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.getInstantFor(arg);
+
+ assert.sameValue(
+ result.epochNanoseconds,
+ 217_178_580_000_000_000n,
+ `variant time separators (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..c9bbd70bf1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-string-time-zone-annotation.js
@@ -0,0 +1,34 @@
+// |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.timezone.prototype.getinstantfor
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["1976-11-18T15:23[Asia/Kolkata]", "named, with no offset"],
+ ["1976-11-18T15:23[!Europe/Vienna]", "named, with ! and no offset"],
+ ["1976-11-18T15:23[+00:00]", "numeric, with no offset"],
+ ["1976-11-18T15:23[!-02:30]", "numeric, with ! and no offset"],
+ ["1976-11-18T15:23+00:00[UTC]", "named, with offset"],
+ ["1976-11-18T15:23+00:00[!Africa/Abidjan]", "named, with offset and !"],
+ ["1976-11-18T15:23+00:00[+01:00]", "numeric, with offset"],
+ ["1976-11-18T15:23+00:00[!-08:00]", "numeric, with offset and !"],
+];
+
+const instance = new Temporal.TimeZone("UTC");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.getInstantFor(arg);
+
+ assert.sameValue(
+ result.epochNanoseconds,
+ 217_178_580_000_000_000n,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..7c8a57e644
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-string-unknown-annotation.js
@@ -0,0 +1,31 @@
+// |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.timezone.prototype.getinstantfor
+description: Various forms of unknown annotation
+features: [Temporal]
+---*/
+
+const tests = [
+ ["1976-11-18T15:23[foo=bar]", "alone"],
+ ["1976-11-18T15:23[UTC][foo=bar]", "with time zone"],
+ ["1976-11-18T15:23[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["1976-11-18T15:23[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["1976-11-18T15:23[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+const instance = new Temporal.TimeZone("UTC");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.getInstantFor(arg);
+
+ assert.sameValue(
+ result.epochNanoseconds,
+ 217_178_580_000_000_000n,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..8361128f7e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-string-with-utc-designator.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.timezone.prototype.getinstantfor
+description: RangeError thrown if a string with UTC designator is used as a PlainDateTime
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "2019-10-01T09:00:00Z",
+ "2019-10-01T09:00:00Z[UTC]",
+];
+const instance = new Temporal.TimeZone("UTC");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.getInstantFor(arg),
+ "String with UTC designator should not be valid as a PlainDateTime"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-wrong-type.js
new file mode 100644
index 0000000000..dbc90d6ccd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/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.timezone.prototype.getinstantfor
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainDateTime
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.TimeZone("UTC");
+
+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.getInstantFor(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.PlainDateTime, "Temporal.PlainDateTime, object"],
+ [Temporal.PlainDateTime.prototype, "Temporal.PlainDateTime.prototype, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.getInstantFor(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/TimeZone/prototype/getInstantFor/argument-zoneddatetime-balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-zoneddatetime-balance-negative-time-units.js
new file mode 100644
index 0000000000..fc47684fd3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-zoneddatetime-balance-negative-time-units.js
@@ -0,0 +1,47 @@
+// |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.timezone.prototype.getinstantfor
+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-totemporaldatetime step 3.b:
+ b. If _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ ...
+ ii. 1. Return ? BuiltinTimeZoneGetPlainDateTimeFor(_item_.[[TimeZone]], _instant_, _item_.[[Calendar]]).
+ sec-temporal.timezone.prototype.getinstantfor step 3:
+ 3. Set _dateTime_ ? ToTemporalDateTime(_dateTime_).
+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 conversionTimeZone = new Temporal.TimeZone("UTC"); // should not be used to interpret the argument
+const instant = conversionTimeZone.getInstantFor(datetime);
+
+assert.sameValue(instant.epochNanoseconds, 3661_001_000_999n);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-zoneddatetime-negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-zoneddatetime-negative-epochnanoseconds.js
new file mode 100644
index 0000000000..25e42c9193
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-zoneddatetime-negative-epochnanoseconds.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.timezone.prototype.getinstantfor
+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]
+---*/
+
+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.TimeZone("UTC");
+const result = instance.getInstantFor(datetime);
+assert.sameValue(result.epochNanoseconds, -13849764_999_999_999n);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..33e6af18f2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/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.timezone.prototype.getinstantfor
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const builtinTimeZone = new Temporal.TimeZone("UTC");
+ assert.throws(RangeError, () => builtinTimeZone.getInstantFor(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..d58be69ae8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/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.timezone.prototype.getinstantfor
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const builtinTimeZone = new Temporal.TimeZone("UTC");
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => builtinTimeZone.getInstantFor(datetime),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..2a9b963652
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/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.timezone.prototype.getinstantfor
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const builtinTimeZone = new Temporal.TimeZone("UTC");
+ assert.throws(RangeError, () => builtinTimeZone.getInstantFor(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..3aaeec2514
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/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.timezone.prototype.getinstantfor
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const builtinTimeZone = new Temporal.TimeZone("UTC");
+ assert.throws(TypeError, () => builtinTimeZone.getInstantFor(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/balance-negative-time-units.js
new file mode 100644
index 0000000000..f8613922bb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/balance-negative-time-units.js
@@ -0,0 +1,48 @@
+// |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.timezone.prototype.getinstantfor
+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-addtime step 8:
+ 8. Return ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_).
+ sec-temporal-adddatetime step 1:
+ 1. Let _timeResult_ be ? AddTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_, _hours_, _minutes_, _seconds_, _milliseconds_, _microseconds_, _nanoseconds_).
+ sec-temporal-builtintimezonegetinstantfor step 13.a:
+ a. Let _earlier_ be ? AddDateTime(_dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], _dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], 0, 0, 0, 0, 0, 0, 0, 0, 0, −_nanoseconds_, *"constrain"*).
+ sec-temporal.timezone.prototype.getinstantfor step 6:
+ 6. Return ? BuiltinTimeZoneGetInstantFor(_timeZone_, _dateTime_, _disambiguation_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const shiftInstant = new Temporal.Instant(3661_001_001_001n);
+const tz = TemporalHelpers.oneShiftTimeZone(shiftInstant, 2);
+const datetime = new Temporal.PlainDateTime(1970, 1, 1, 1, 1, 1, 1, 1, 1);
+
+// This code path is encountered if disambiguation is `earlier` and the shift is
+// a spring-forward change
+tz.getInstantFor(datetime, { disambiguation: "earlier" });
+
+const expected = [
+ "1970-01-01T01:01:01.001001001",
+ "1970-01-01T01:01:01.001000999",
+];
+assert.compareArray(tz.getPossibleInstantsForCalledWith, expected);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/branding.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/branding.js
new file mode 100644
index 0000000000..d5ea3f0c76
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/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.timezone.prototype.getinstantfor
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const getInstantFor = Temporal.TimeZone.prototype.getInstantFor;
+
+assert.sameValue(typeof getInstantFor, "function");
+
+const args = [new Temporal.PlainDateTime(2022, 6, 22)];
+
+assert.throws(TypeError, () => getInstantFor.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => getInstantFor.apply(null, args), "null");
+assert.throws(TypeError, () => getInstantFor.apply(true, args), "true");
+assert.throws(TypeError, () => getInstantFor.apply("", args), "empty string");
+assert.throws(TypeError, () => getInstantFor.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => getInstantFor.apply(1, args), "1");
+assert.throws(TypeError, () => getInstantFor.apply({}, args), "plain object");
+assert.throws(TypeError, () => getInstantFor.apply(Temporal.TimeZone, args), "Temporal.TimeZone");
+assert.throws(TypeError, () => getInstantFor.apply(Temporal.TimeZone.prototype, args), "Temporal.TimeZone.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/browser.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/builtin.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/builtin.js
new file mode 100644
index 0000000000..709f4d3c30
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/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.timezone.prototype.getinstantfor
+description: >
+ Tests that Temporal.TimeZone.prototype.getInstantFor
+ 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.TimeZone.prototype.getInstantFor),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.TimeZone.prototype.getInstantFor),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.TimeZone.prototype.getInstantFor),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.TimeZone.prototype.getInstantFor.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/calendar-datefromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..b3650982ca
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/calendar-datefromfields-called-with-null-prototype-fields.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.timezone.prototype.getinstantfor
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.TimeZone("UTC");
+const arg = { year: 2000, month: 5, day: 2, calendar };
+instance.getInstantFor(arg);
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/calendar-fields-iterable.js
new file mode 100644
index 0000000000..c4a2b8bc1f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/calendar-fields-iterable.js
@@ -0,0 +1,34 @@
+// |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.timezone.prototype.getinstantfor
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.timezone.prototype.getinstantfor step 3:
+ 3. Set _dateTime_ to ? ToTemporalDateTime(_dateTime_).
+ sec-temporal-totemporaldatetime step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"hour"*, *"microsecond"*, *"millisecond"*, *"minute"*, *"month"*, *"monthCode"*, *"nanosecond"*, *"second"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+const timeZone = new Temporal.TimeZone("UTC");
+timeZone.getInstantFor({ year: 2000, month: 5, day: 2, calendar });
+
+assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/calendar-temporal-object.js
new file mode 100644
index 0000000000..54f820bd2f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/calendar-temporal-object.js
@@ -0,0 +1,29 @@
+// |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.timezone.prototype.getinstantfor
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.timezone.prototype.getinstantfor step 3:
+ 3. Set _dateTime_ to ? ToTemporalDateTime(_dateTime_).
+ sec-temporal-totemporaldatetime step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const timeZone = new Temporal.TimeZone("UTC");
+ timeZone.getInstantFor({ year: 2000, month: 5, day: 2, calendar: temporalObject });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..0404044e4d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/constructor-in-calendar-fields.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.timezone.prototype.getinstantfor
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.TimeZone("UTC");
+
+assert.throws(RangeError, () => instance.getInstantFor(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/disambiguation-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/disambiguation-invalid-string.js
new file mode 100644
index 0000000000..2f1c3f3fd2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/disambiguation-invalid-string.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.timezone.prototype.getinstantfor
+description: RangeError thrown when disambiguation option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaldisambiguation step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"disambiguation"*, « String », « *"compatible"*, *"earlier"*, *"later"*, *"reject"* », *"compatible"*).
+ sec-temporal.timezone.prototype.getinstantfor step 5:
+ 5. Let _disambiguation_ be ? ToTemporalDisambiguation(_options_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2001, 9, 9, 1, 46, 40, 987, 654, 321);
+const timeZone = new Temporal.TimeZone("UTC");
+assert.throws(RangeError, () => timeZone.getInstantFor(datetime, { disambiguation: "other string" }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/disambiguation-undefined.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/disambiguation-undefined.js
new file mode 100644
index 0000000000..f8874c7227
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/disambiguation-undefined.js
@@ -0,0 +1,35 @@
+// |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.timezone.prototype.getinstantfor
+description: Fallback value for disambiguation option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaldisambiguation step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"disambiguation"*, « String », « *"compatible"*, *"earlier"*, *"later"*, *"reject"* », *"compatible"*).
+ sec-temporal.timezone.prototype.getinstantfor step 5:
+ 5. Let _disambiguation_ be ? ToTemporalDisambiguation(_options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const timeZone = TemporalHelpers.springForwardFallBackTimeZone();
+const springForwardDateTime = new Temporal.PlainDateTime(2000, 4, 2, 2, 30);
+const fallBackDateTime = new Temporal.PlainDateTime(2000, 10, 29, 1, 30);
+
+[
+ [springForwardDateTime, 954671400_000_000_000n],
+ [fallBackDateTime, 972808200_000_000_000n],
+].forEach(([datetime, expected]) => {
+ const explicit = timeZone.getInstantFor(datetime, { disambiguation: undefined });
+ assert.sameValue(explicit.epochNanoseconds, expected, "default disambiguation is compatible");
+ const implicit = timeZone.getInstantFor(datetime, {});
+ assert.sameValue(implicit.epochNanoseconds, expected, "default disambiguation is compatible");
+ const lambda = timeZone.getInstantFor(datetime, () => {});
+ assert.sameValue(lambda.epochNanoseconds, expected, "default disambiguation is compatible");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/disambiguation-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/disambiguation-wrong-type.js
new file mode 100644
index 0000000000..8184f7cce9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/disambiguation-wrong-type.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.timezone.prototype.getinstantfor
+description: Type conversions for disambiguation option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaldisambiguation step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"disambiguation"*, « String », « *"compatible"*, *"earlier"*, *"later"*, *"reject"* », *"compatible"*).
+ sec-temporal.timezone.prototype.getinstantfor step 5:
+ 5. Let _disambiguation_ be ? ToTemporalDisambiguation(_options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2001, 9, 9, 1, 46, 40, 987, 654, 321);
+const timeZone = new Temporal.TimeZone("UTC");
+TemporalHelpers.checkStringOptionWrongType("disambiguation", "compatible",
+ (disambiguation) => timeZone.getInstantFor(datetime, { disambiguation }),
+ (result, descr) => assert.sameValue(result.epochNanoseconds, 1_000_000_000_987_654_321n, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/duplicate-calendar-fields.js
new file mode 100644
index 0000000000..b6a8d452a8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/duplicate-calendar-fields.js
@@ -0,0 +1,19 @@
+// |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.timezone.prototype.getinstantfor
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['day'], ['hour'], ['microsecond'], ['millisecond'], ['minute'], ['month'], ['monthCode'], ['nanosecond'], ['second'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar };
+ const instance = new Temporal.TimeZone("UTC");
+
+ assert.throws(RangeError, () => instance.getInstantFor(arg));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/getpossibleinstantsfor-called-with-iso8601-calendar.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/getpossibleinstantsfor-called-with-iso8601-calendar.js
new file mode 100644
index 0000000000..b78f801c25
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/getpossibleinstantsfor-called-with-iso8601-calendar.js
@@ -0,0 +1,59 @@
+// |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.timezone.prototype.getinstantfor
+description: >
+ Time zone's getPossibleInstantsFor is called with a PlainDateTime with the
+ built-in ISO 8601 calendar
+features: [Temporal]
+info: |
+ DisambiguatePossibleInstants:
+ 2. Let _n_ be _possibleInstants_'s length.
+ ...
+ 5. Assert: _n_ = 0.
+ ...
+ 19. If _disambiguation_ is *"earlier"*, then
+ ...
+ c. Let _earlierDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*).
+ d. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _earlierDateTime_).
+ ...
+ 20. Assert: _disambiguation_ is *"compatible"* or *"later"*.
+ ...
+ 23. Let _laterDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*).
+ 24. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _laterDateTime_).
+---*/
+
+class SkippedDateTime extends Temporal.TimeZone {
+ constructor() {
+ super("UTC");
+ this.calls = 0;
+ }
+
+ getPossibleInstantsFor(dateTime) {
+ // Calls occur in pairs. For the first one return no possible instants so
+ // that DisambiguatePossibleInstants will call it again
+ if (this.calls++ % 2 == 0) {
+ return [];
+ }
+
+ assert.sameValue(
+ dateTime.getISOFields().calendar,
+ "iso8601",
+ "getPossibleInstantsFor called with dateTime with built-in ISO 8601 calendar"
+ );
+ return super.getPossibleInstantsFor(dateTime);
+ }
+}
+
+const nonBuiltinISOCalendar = new Temporal.Calendar("iso8601");
+
+for (const disambiguation of ["earlier", "later", "compatible"]) {
+ const timeZone = new SkippedDateTime();
+ timeZone.getInstantFor(new Temporal.PlainDateTime(2000, 3, 4, 12, 34, 56, 0, 0, 0, nonBuiltinISOCalendar), { disambiguation });
+
+ assert.sameValue(timeZone.calls, 2, "getPossibleInstantsFor should have been called 2 times");
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..6916cead64
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/infinity-throws-rangeerror.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.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.timezone.prototype.getinstantfor
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.TimeZone("UTC");
+const base = { year: 2000, month: 5, day: 2, hour: 15, minute: 30, second: 45, millisecond: 987, microsecond: 654, nanosecond: 321 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.getInstantFor({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.getInstantFor({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/leap-second.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/leap-second.js
new file mode 100644
index 0000000000..e70881059c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/leap-second.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.timezone.prototype.getinstantfor
+description: Leap second is a valid ISO string for PlainDateTime
+features: [Temporal]
+---*/
+
+const instance = new Temporal.TimeZone("UTC");
+
+let arg = "2016-12-31T23:59:60";
+const result1 = instance.getInstantFor(arg);
+assert.sameValue(
+ result1.epochNanoseconds,
+ 1_483_228_799_000_000_000n,
+ "leap second is a valid ISO string for PlainDateTime"
+);
+
+arg = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+const result2 = instance.getInstantFor(arg);
+assert.sameValue(
+ result2.epochNanoseconds,
+ 1_483_228_799_000_000_000n,
+ "second: 60 is ignored in property bag for PlainDateTime"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/length.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/length.js
new file mode 100644
index 0000000000..d273fcf2a6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/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.timezone.prototype.getinstantfor
+description: Temporal.TimeZone.prototype.getInstantFor.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.TimeZone.prototype.getInstantFor, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/name.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/name.js
new file mode 100644
index 0000000000..6d07a0e673
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/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.timezone.prototype.getinstantfor
+description: Temporal.TimeZone.prototype.getInstantFor.name is "getInstantFor".
+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.TimeZone.prototype.getInstantFor, "name", {
+ value: "getInstantFor",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/not-a-constructor.js
new file mode 100644
index 0000000000..f639a9dc89
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/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.timezone.prototype.getinstantfor
+description: >
+ Temporal.TimeZone.prototype.getInstantFor 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.TimeZone.prototype.getInstantFor();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.TimeZone.prototype.getInstantFor), false,
+ "isConstructor(Temporal.TimeZone.prototype.getInstantFor)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/options-object.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/options-object.js
new file mode 100644
index 0000000000..1aa686d763
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/options-object.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.timezone.prototype.getinstantfor
+description: Empty or a function object may be used as options
+features: [Temporal]
+---*/
+
+const instance = new Temporal.TimeZone("UTC");
+
+const result1 = instance.getInstantFor(new Temporal.PlainDateTime(2019, 10, 29, 10, 46, 38), {});
+assert.sameValue(
+ result1.epochNanoseconds, 1572345998000000000n,
+ "options may be an empty plain object"
+);
+
+const result2 = instance.getInstantFor(new Temporal.PlainDateTime(2019, 10, 29, 10, 46, 38), () => {});
+assert.sameValue(
+ result2.epochNanoseconds, 1572345998000000000n,
+ "options may be a function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/options-undefined.js
new file mode 100644
index 0000000000..6ddd03d9ef
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/options-undefined.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.timezone.prototype.getinstantfor
+includes: [temporalHelpers.js]
+description: Verify that undefined options are handled correctly.
+features: [BigInt, Temporal]
+---*/
+
+const datetimeEarlier = new Temporal.PlainDateTime(2000, 10, 29, 1, 34, 56, 987, 654, 321);
+const datetimeLater = new Temporal.PlainDateTime(2000, 4, 2, 2, 34, 56, 987, 654, 321);
+const timeZone = TemporalHelpers.springForwardFallBackTimeZone();
+
+[
+ [datetimeEarlier, 972808496987654321n],
+ [datetimeLater, 954671696987654321n],
+].forEach(([datetime, expected]) => {
+ const explicit = timeZone.getInstantFor(datetime, undefined);
+ assert.sameValue(explicit.epochNanoseconds, expected, "default disambiguation is compatible");
+
+ const implicit = timeZone.getInstantFor(datetime);
+ assert.sameValue(implicit.epochNanoseconds, expected, "default disambiguation is compatible");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/options-wrong-type.js
new file mode 100644
index 0000000000..4806f4de41
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/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.timezone.prototype.getinstantfor
+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.TimeZone("UTC");
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.getInstantFor(new Temporal.PlainDateTime(2019, 10, 29, 10, 46, 38), value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/order-of-operations.js
new file mode 100644
index 0000000000..4705daa151
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/order-of-operations.js
@@ -0,0 +1,109 @@
+// |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.timezone.prototype.getinstantfor
+description: Properties on an object passed to getInstantFor() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ // GetTemporalCalendarSlotValueWithISODefault
+ "get fields.calendar",
+ "has fields.calendar.dateAdd",
+ "has fields.calendar.dateFromFields",
+ "has fields.calendar.dateUntil",
+ "has fields.calendar.day",
+ "has fields.calendar.dayOfWeek",
+ "has fields.calendar.dayOfYear",
+ "has fields.calendar.daysInMonth",
+ "has fields.calendar.daysInWeek",
+ "has fields.calendar.daysInYear",
+ "has fields.calendar.fields",
+ "has fields.calendar.id",
+ "has fields.calendar.inLeapYear",
+ "has fields.calendar.mergeFields",
+ "has fields.calendar.month",
+ "has fields.calendar.monthCode",
+ "has fields.calendar.monthDayFromFields",
+ "has fields.calendar.monthsInYear",
+ "has fields.calendar.weekOfYear",
+ "has fields.calendar.year",
+ "has fields.calendar.yearMonthFromFields",
+ "has fields.calendar.yearOfWeek",
+ // lookup
+ "get fields.calendar.dateFromFields",
+ "get fields.calendar.fields",
+ // CalendarFields
+ "call fields.calendar.fields",
+ // PrepareTemporalFields
+ "get fields.day",
+ "get fields.day.valueOf",
+ "call fields.day.valueOf",
+ "get fields.hour",
+ "get fields.hour.valueOf",
+ "call fields.hour.valueOf",
+ "get fields.microsecond",
+ "get fields.microsecond.valueOf",
+ "call fields.microsecond.valueOf",
+ "get fields.millisecond",
+ "get fields.millisecond.valueOf",
+ "call fields.millisecond.valueOf",
+ "get fields.minute",
+ "get fields.minute.valueOf",
+ "call fields.minute.valueOf",
+ "get fields.month",
+ "get fields.month.valueOf",
+ "call fields.month.valueOf",
+ "get fields.monthCode",
+ "get fields.monthCode.toString",
+ "call fields.monthCode.toString",
+ "get fields.nanosecond",
+ "get fields.nanosecond.valueOf",
+ "call fields.nanosecond.valueOf",
+ "get fields.second",
+ "get fields.second.valueOf",
+ "call fields.second.valueOf",
+ "get fields.year",
+ "get fields.year.valueOf",
+ "call fields.year.valueOf",
+ // InterpretTemporalDateTimeFields
+ "call fields.calendar.dateFromFields",
+ // ToTemporalDisambiguation
+ "get options.disambiguation",
+ "get options.disambiguation.toString",
+ "call options.disambiguation.toString",
+ // BuiltinTimeZoneGetInstantFor
+ "get this.getPossibleInstantsFor",
+ "call this.getPossibleInstantsFor",
+];
+const actual = [];
+
+const instance = new Temporal.TimeZone("UTC");
+TemporalHelpers.observeProperty(actual, instance, "getPossibleInstantsFor", function getPossibleInstantsFor(...args) {
+ actual.push("call this.getPossibleInstantsFor");
+ return Temporal.TimeZone.prototype.getPossibleInstantsFor.apply(instance, args);
+}, "this");
+
+const fields = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2000,
+ month: 5,
+ monthCode: "M05",
+ day: 2,
+ hour: 12,
+ minute: 34,
+ second: 56,
+ millisecond: 987,
+ microsecond: 654,
+ nanosecond: 321,
+ calendar: TemporalHelpers.calendarObserver(actual, "fields.calendar"),
+}, "fields");
+
+const options = TemporalHelpers.propertyBagObserver(actual, { disambiguation: "compatible" }, "options");
+
+instance.getInstantFor(fields, options);
+assert.compareArray(actual, expected, "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/prop-desc.js
new file mode 100644
index 0000000000..444a2d5e2a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/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.timezone.prototype.getinstantfor
+description: The "getInstantFor" property of Temporal.TimeZone.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.TimeZone.prototype.getInstantFor,
+ "function",
+ "`typeof TimeZone.prototype.getInstantFor` is `function`"
+);
+
+verifyProperty(Temporal.TimeZone.prototype, "getInstantFor", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/proto-in-calendar-fields.js
new file mode 100644
index 0000000000..e9dfd7a26f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/proto-in-calendar-fields.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.timezone.prototype.getinstantfor
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.TimeZone("UTC");
+
+assert.throws(RangeError, () => instance.getInstantFor(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/read-time-fields-before-datefromfields.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/read-time-fields-before-datefromfields.js
new file mode 100644
index 0000000000..87f11aac7f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/read-time-fields-before-datefromfields.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.timezone.prototype.getinstantfor
+description: The time fields are read from the object before being passed to dateFromFields().
+info: |
+ sec-temporal.timezone.prototype.getinstantfor step 3:
+ 3. Set _dateTime_ to ? ToTemporalDateTime(_dateTime_).
+ sec-temporal-totemporaldatetime step 2.e:
+ e. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+ sec-temporal-interprettemporaldatetimefields steps 1–2:
+ 1. Let _timeResult_ be ? ToTemporalTimeRecord(_fields_).
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const timezone = new Temporal.TimeZone("UTC");
+const calendar = TemporalHelpers.calendarMakeInfinityTime();
+const result = timezone.getInstantFor({ year: 1970, month: 1, day: 1, calendar });
+
+assert.sameValue(result.epochNanoseconds, 0n, "epochNanoseconds result");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/shell.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/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/TimeZone/prototype/getInstantFor/year-zero.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/year-zero.js
new file mode 100644
index 0000000000..3cdf65fe5b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getInstantFor/year-zero.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.timezone.prototype.getinstantfor
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-12-07",
+ "-000000-12-07T03:24:30",
+ "-000000-12-07T03:24:30+01:00",
+ "-000000-12-07T03:24:30+00:00[UTC]",
+];
+const instance = new Temporal.TimeZone("UTC");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.getInstantFor(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..7fc69d9415
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/argument-string-calendar-annotation.js
@@ -0,0 +1,33 @@
+// |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.timezone.prototype.getnexttransition
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["1970-01-01T00:00Z[u-ca=iso8601]", "without time zone"],
+ ["1970-01-01T00:00Z[UTC][u-ca=gregory]", "with time zone"],
+ ["1970-01-01T00:00Z[!u-ca=hebrew]", "with ! and no time zone"],
+ ["1970-01-01T00:00Z[UTC][!u-ca=chinese]", "with ! and time zone"],
+ ["1970-01-01T00:00Z[u-ca=discord]", "annotation is ignored"],
+ ["1970-01-01T00:00Z[!u-ca=discord]", "annotation with ! is ignored"],
+ ["1970-01-01T00:00Z[u-ca=iso8601][u-ca=discord]", "two annotations are ignored"],
+];
+
+const instance = new Temporal.TimeZone("UTC");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.getNextTransition(arg);
+
+ assert.sameValue(
+ result,
+ null,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..ea1c71ca90
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/argument-string-critical-unknown-annotation.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.timezone.prototype.getnexttransition
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01T00:00Z[!foo=bar]",
+ "1970-01-01T00:00Z[UTC][!foo=bar]",
+ "1970-01-01T00:00Z[u-ca=iso8601][!foo=bar]",
+ "1970-01-01T00:00Z[UTC][!foo=bar][u-ca=iso8601]",
+ "1970-01-01T00:00Z[foo=bar][!_foo-bar0=Dont-Ignore-This-99999999999]",
+];
+const instance = new Temporal.TimeZone("UTC");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.getNextTransition(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..1521a8cd4d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/argument-string-date-with-utc-offset.js
@@ -0,0 +1,52 @@
+// |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.timezone.prototype.getnexttransition
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+---*/
+
+const instance = new Temporal.TimeZone("UTC");
+
+const validStrings = [
+ "1970-01-01T00Z",
+ "1970-01-01T00Z[UTC]",
+ "1970-01-01T00Z[!UTC]",
+ "1970-01-01T00Z[Europe/Vienna]",
+ "1970-01-01T00+00:00",
+ "1970-01-01T00+00:00[UTC]",
+ "1970-01-01T00+00:00[!UTC]",
+ "1969-12-31T16-08:00[America/Vancouver]",
+];
+
+for (const arg of validStrings) {
+ const result = instance.getNextTransition(arg);
+
+ assert.sameValue(
+ result,
+ null,
+ `"${arg}" is a valid UTC offset with time for Instant`
+ );
+}
+
+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.getNextTransition(arg),
+ `"${arg}" UTC offset without time is not valid for Instant`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/argument-string-invalid.js
new file mode 100644
index 0000000000..fdb1c6a0f8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/argument-string-invalid.js
@@ -0,0 +1,69 @@
+// |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.timezone.prototype.getnexttransition
+description: >
+ RangeError thrown if an invalid ISO string (or syntactically valid ISO string
+ that is not supported) is used as an Instant
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ // invalid ISO strings:
+ "",
+ "invalid iso8601",
+ "2020-01-00T00:00Z",
+ "2020-01-32T00:00Z",
+ "2020-02-30T00:00Z",
+ "2021-02-29T00:00Z",
+ "2020-00-01T00:00Z",
+ "2020-13-01T00:00Z",
+ "2020-01-01TZ",
+ "2020-01-01T25:00:00Z",
+ "2020-01-01T01:60:00Z",
+ "2020-01-01T01:60:61Z",
+ "2020-01-01T00:00Zjunk",
+ "2020-01-01T00:00:00Zjunk",
+ "2020-01-01T00:00:00.000000000Zjunk",
+ "2020-01-01T00:00:00+00:00junk",
+ "2020-01-01T00:00:00+00:00[UTC]junk",
+ "2020-01-01T00:00:00+00:00[UTC][u-ca=iso8601]junk",
+ "02020-01-01T00:00Z",
+ "2020-001-01T00:00Z",
+ "2020-01-001T00:00Z",
+ "2020-01-01T001Z",
+ "2020-01-01T01:001Z",
+ "2020-01-01T01:01:001Z",
+ // valid, but forms not supported in Temporal:
+ "2020-W01-1T00:00Z",
+ "2020-001T00:00Z",
+ "+0002020-01-01T00:00Z",
+ // may be valid in other contexts, but insufficient information for Instant:
+ "2020-01",
+ "+002020-01",
+ "01-01",
+ "2020-W01",
+ "P1Y",
+ "-P12Y",
+ "2020-01-01",
+ "2020-01-01T00",
+ "2020-01-01T00:00",
+ "2020-01-01T00:00:00",
+ "2020-01-01T00:00:00.000000000",
+ // valid, but outside the supported range:
+ "-999999-01-01T00:00Z",
+ "+999999-01-01T00:00Z",
+];
+
+const instance = new Temporal.TimeZone("UTC");
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => instance.getNextTransition(arg),
+ `"${arg}" should not be a valid ISO string for an Instant`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..f4aa8ca87f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/argument-string-multiple-calendar.js
@@ -0,0 +1,28 @@
+// |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.timezone.prototype.getnexttransition
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01T00:00Z[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00Z[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01T00:00Z[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00Z[u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+];
+const instance = new Temporal.TimeZone("UTC");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.getNextTransition(arg),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..bfed54adfe
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/argument-string-multiple-time-zone.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.timezone.prototype.getnexttransition
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01T00:00Z[UTC][UTC]",
+ "1970-01-01T00:00Z[!UTC][UTC]",
+ "1970-01-01T00:00Z[UTC][!UTC]",
+ "1970-01-01T00:00Z[UTC][u-ca=iso8601][UTC]",
+ "1970-01-01T00:00Z[UTC][foo=bar][UTC]",
+];
+const instance = new Temporal.TimeZone("UTC");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.getNextTransition(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/argument-string-time-separators.js
new file mode 100644
index 0000000000..153d328c87
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/argument-string-time-separators.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.timezone.prototype.getnexttransition
+description: Time separator in string argument can vary
+features: [Temporal]
+---*/
+
+const tests = [
+ ["1970-01-01T00:00Z", "uppercase T"],
+ ["1970-01-01t00:00Z", "lowercase T"],
+ ["1970-01-01 00:00Z", "space between date and time"],
+];
+
+const instance = new Temporal.TimeZone("UTC");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.getNextTransition(arg);
+
+ assert.sameValue(
+ result,
+ null,
+ `variant time separators (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..efaf2f44b3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/argument-string-time-zone-annotation.js
@@ -0,0 +1,34 @@
+// |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.timezone.prototype.getnexttransition
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["1970-01-01T00:00Z[Asia/Kolkata]", "named, with Z"],
+ ["1970-01-01T00:00Z[!Europe/Vienna]", "named, with Z and !"],
+ ["1970-01-01T00:00Z[+00:00]", "numeric, with Z"],
+ ["1970-01-01T00:00Z[!-02:30]", "numeric, with Z and !"],
+ ["1970-01-01T00:00+00:00[UTC]", "named, with offset"],
+ ["1970-01-01T00:00+00:00[!Africa/Abidjan]", "named, with offset and !"],
+ ["1970-01-01T00:00+00:00[-08:00]", "numeric, with offset"],
+ ["1970-01-01T00:00+00:00[!+01:00]", "numeric, with offset and !"],
+];
+
+const instance = new Temporal.TimeZone("UTC");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.getNextTransition(arg);
+
+ assert.sameValue(
+ result,
+ null,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..e465d767d8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/argument-string-unknown-annotation.js
@@ -0,0 +1,31 @@
+// |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.timezone.prototype.getnexttransition
+description: Various forms of unknown annotation
+features: [Temporal]
+---*/
+
+const tests = [
+ ["1970-01-01T00:00Z[foo=bar]", "alone"],
+ ["1970-01-01T00:00Z[UTC][foo=bar]", "with time zone"],
+ ["1970-01-01T00:00Z[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["1970-01-01T00:00Z[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["1970-01-01T00:00Z[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+const instance = new Temporal.TimeZone("UTC");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.getNextTransition(arg);
+
+ assert.sameValue(
+ result,
+ null,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/argument-wrong-type.js
new file mode 100644
index 0000000000..b89dfe2520
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/argument-wrong-type.js
@@ -0,0 +1,46 @@
+// |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.timezone.prototype.getnexttransition
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ for Instant
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.TimeZone("UTC");
+
+const primitiveTests = [
+ [undefined, "undefined"],
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [19761118, "number that would convert to a valid ISO string in other contexts"],
+ [1n, "bigint"],
+ [{}, "plain object"],
+ [Temporal.Instant, "Temporal.Instant, object"],
+];
+
+for (const [arg, description] of primitiveTests) {
+ assert.throws(
+ typeof arg === "string" || (typeof arg === "object" && arg !== null) || typeof arg === "function"
+ ? RangeError
+ : TypeError,
+ () => instance.getNextTransition(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [Temporal.Instant.prototype, "Temporal.Instant.prototype, object"], // fails brand check in toString()
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.getNextTransition(arg), `${description} does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/argument-zoneddatetime.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/argument-zoneddatetime.js
new file mode 100644
index 0000000000..74217f32ae
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/argument-zoneddatetime.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.timezone.prototype.getnexttransition
+description: Fast path for converting Temporal.ZonedDateTime to Temporal.Instant
+info: |
+ sec-temporal.timezone.prototype.getnexttransition step 3:
+ 3. Set _startingPoint_ to ? ToTemporalInstant(_startingPoint_).
+ sec-temporal-totemporalinstant step 1.b:
+ b. If _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return ! CreateTemporalInstant(_item_.[[Nanoseconds]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalInstantFastPath((datetime) => {
+ const timeZone = Temporal.TimeZone.from("UTC");
+ const result = timeZone.getNextTransition(datetime);
+ assert.sameValue(result, null, "transition result");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/branding.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/branding.js
new file mode 100644
index 0000000000..c78e545d10
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/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.timezone.prototype.getnexttransition
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const getNextTransition = Temporal.TimeZone.prototype.getNextTransition;
+
+assert.sameValue(typeof getNextTransition, "function");
+
+const args = [new Temporal.Instant(0n)];
+
+assert.throws(TypeError, () => getNextTransition.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => getNextTransition.apply(null, args), "null");
+assert.throws(TypeError, () => getNextTransition.apply(true, args), "true");
+assert.throws(TypeError, () => getNextTransition.apply("", args), "empty string");
+assert.throws(TypeError, () => getNextTransition.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => getNextTransition.apply(1, args), "1");
+assert.throws(TypeError, () => getNextTransition.apply({}, args), "plain object");
+assert.throws(TypeError, () => getNextTransition.apply(Temporal.TimeZone, args), "Temporal.TimeZone");
+assert.throws(TypeError, () => getNextTransition.apply(Temporal.TimeZone.prototype, args), "Temporal.TimeZone.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/browser.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/builtin.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/builtin.js
new file mode 100644
index 0000000000..c63cf94509
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/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.timezone.prototype.getnexttransition
+description: >
+ Tests that Temporal.TimeZone.prototype.getNextTransition
+ 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.TimeZone.prototype.getNextTransition),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.TimeZone.prototype.getNextTransition),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.TimeZone.prototype.getNextTransition),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.TimeZone.prototype.getNextTransition.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/instant-string-limits.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/instant-string-limits.js
new file mode 100644
index 0000000000..769699dcfd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/instant-string-limits.js
@@ -0,0 +1,47 @@
+// |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.timezone.prototype.getnexttransition
+description: String arguments at the limit of the representable range
+features: [Temporal]
+---*/
+
+const instance = new Temporal.TimeZone("UTC");
+
+const minInstantStrings = [
+ "-271821-04-20T00:00Z",
+ "-271821-04-19T23:00-01:00",
+ "-271821-04-19T00:00:00.000000001-23:59:59.999999999",
+];
+for (const str of minInstantStrings) {
+ assert.sameValue(instance.getNextTransition(str), null, `instant string ${str} should be valid`);
+}
+
+const maxInstantStrings = [
+ "+275760-09-13T00:00Z",
+ "+275760-09-13T01:00+01:00",
+ "+275760-09-13T23:59:59.999999999+23:59:59.999999999",
+];
+
+for (const str of maxInstantStrings) {
+ assert.sameValue(instance.getNextTransition(str), null, `instant string ${str} should be valid`);
+}
+
+const outOfRangeInstantStrings = [
+ "-271821-04-19T23:59:59.999999999Z",
+ "-271821-04-19T23:00-00:59:59.999999999",
+ "-271821-04-19T00:00:00-23:59:59.999999999",
+ "-271821-04-19T00:00:00-24:00",
+ "+275760-09-13T00:00:00.000000001Z",
+ "+275760-09-13T01:00+00:59:59.999999999",
+ "+275760-09-14T00:00+23:59:59.999999999",
+ "+275760-09-14T00:00+24:00",
+];
+
+for (const str of outOfRangeInstantStrings) {
+ assert.throws(RangeError, () => instance.getNextTransition(str), `instant string ${str} should not be valid`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/instant-string.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/instant-string.js
new file mode 100644
index 0000000000..eb54738370
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/instant-string.js
@@ -0,0 +1,34 @@
+// |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.timezone.prototype.getnexttransition
+description: Conversion of ISO date-time strings to Temporal.Instant instances
+features: [Temporal]
+---*/
+
+const instance = new Temporal.TimeZone("UTC");
+
+let str = "1970-01-01T00:00";
+assert.throws(RangeError, () => instance.getNextTransition(str), "bare date-time string is not an instant");
+str = "1970-01-01T00:00[UTC]";
+assert.throws(RangeError, () => instance.getNextTransition(str), "date-time + IANA annotation is not an instant");
+
+// The following are all valid strings so should not throw:
+
+const valids = [
+ "1970-01-01T00:00Z",
+ "1970-01-01T00:00+01:00",
+ "1970-01-01T00:00Z[UTC]",
+ "1970-01-01T00:00+01:00[UTC]",
+ "1970-01-01T00:00Z[u-ca=hebrew]",
+ "1970-01-01T00:00+01:00[u-ca=hebrew]",
+ "1970-01-01T00:00+01:00[Etc/Ignored][u-ca=hebrew]",
+];
+for (const str of valids) {
+ const result = instance.getNextTransition(str);
+ assert.sameValue(result, null);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/leap-second.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/leap-second.js
new file mode 100644
index 0000000000..e7270bcb82
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/leap-second.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.timezone.prototype.getnexttransition
+description: Leap second is a valid ISO string for Instant
+features: [Temporal]
+---*/
+
+const instance = new Temporal.TimeZone("UTC");
+
+const arg = "2016-12-31T23:59:60Z";
+const result = instance.getNextTransition(arg);
+assert.sameValue(
+ result,
+ null,
+ "leap second is a valid ISO string for Instant"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/length.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/length.js
new file mode 100644
index 0000000000..15bda53094
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/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.timezone.prototype.getnexttransition
+description: Temporal.TimeZone.prototype.getNextTransition.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.TimeZone.prototype.getNextTransition, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/name.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/name.js
new file mode 100644
index 0000000000..2da5b50bdd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/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.timezone.prototype.getnexttransition
+description: Temporal.TimeZone.prototype.getNextTransition.name is "getNextTransition".
+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.TimeZone.prototype.getNextTransition, "name", {
+ value: "getNextTransition",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/not-a-constructor.js
new file mode 100644
index 0000000000..7685a1cc62
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/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.timezone.prototype.getnexttransition
+description: >
+ Temporal.TimeZone.prototype.getNextTransition 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.TimeZone.prototype.getNextTransition();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.TimeZone.prototype.getNextTransition), false,
+ "isConstructor(Temporal.TimeZone.prototype.getNextTransition)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/prop-desc.js
new file mode 100644
index 0000000000..eeedb1647c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/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.timezone.prototype.getnexttransition
+description: The "getNextTransition" property of Temporal.TimeZone.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.TimeZone.prototype.getNextTransition,
+ "function",
+ "`typeof TimeZone.prototype.getNextTransition` is `function`"
+);
+
+verifyProperty(Temporal.TimeZone.prototype, "getNextTransition", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/shell.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/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/TimeZone/prototype/getNextTransition/year-zero.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/year-zero.js
new file mode 100644
index 0000000000..0c65c5f647
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getNextTransition/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.timezone.prototype.getnexttransition
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-03-30T00:45Z",
+ "-000000-03-30T01:45+01:00",
+ "-000000-03-30T01:45:00+00:00[UTC]",
+];
+const instance = new Temporal.TimeZone("UTC");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.getNextTransition(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..082b498e9c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/argument-string-calendar-annotation.js
@@ -0,0 +1,33 @@
+// |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.timezone.prototype.getoffsetnanosecondsfor
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["1970-01-01T00:00Z[u-ca=iso8601]", "without time zone"],
+ ["1970-01-01T00:00Z[UTC][u-ca=gregory]", "with time zone"],
+ ["1970-01-01T00:00Z[!u-ca=hebrew]", "with ! and no time zone"],
+ ["1970-01-01T00:00Z[UTC][!u-ca=chinese]", "with ! and time zone"],
+ ["1970-01-01T00:00Z[u-ca=discord]", "annotation is ignored"],
+ ["1970-01-01T00:00Z[!u-ca=discord]", "annotation with ! is ignored"],
+ ["1970-01-01T00:00Z[u-ca=iso8601][u-ca=discord]", "two annotations are ignored"],
+];
+
+const instance = new Temporal.TimeZone("UTC");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.getOffsetNanosecondsFor(arg);
+
+ assert.sameValue(
+ result,
+ 0,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..e90272191d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/argument-string-critical-unknown-annotation.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.timezone.prototype.getoffsetnanosecondsfor
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01T00:00Z[!foo=bar]",
+ "1970-01-01T00:00Z[UTC][!foo=bar]",
+ "1970-01-01T00:00Z[u-ca=iso8601][!foo=bar]",
+ "1970-01-01T00:00Z[UTC][!foo=bar][u-ca=iso8601]",
+ "1970-01-01T00:00Z[foo=bar][!_foo-bar0=Dont-Ignore-This-99999999999]",
+];
+const instance = new Temporal.TimeZone("UTC");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.getOffsetNanosecondsFor(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..af93408ea2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/argument-string-date-with-utc-offset.js
@@ -0,0 +1,52 @@
+// |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.timezone.prototype.getoffsetnanosecondsfor
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+---*/
+
+const instance = new Temporal.TimeZone("UTC");
+
+const validStrings = [
+ "1970-01-01T00Z",
+ "1970-01-01T00Z[UTC]",
+ "1970-01-01T00Z[!UTC]",
+ "1970-01-01T00Z[Europe/Vienna]",
+ "1970-01-01T00+00:00",
+ "1970-01-01T00+00:00[UTC]",
+ "1970-01-01T00+00:00[!UTC]",
+ "1969-12-31T16-08:00[America/Vancouver]",
+];
+
+for (const arg of validStrings) {
+ const result = instance.getOffsetNanosecondsFor(arg);
+
+ assert.sameValue(
+ result,
+ 0,
+ `"${arg}" is a valid UTC offset with time for Instant`
+ );
+}
+
+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.getOffsetNanosecondsFor(arg),
+ `"${arg}" UTC offset without time is not valid for Instant`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/argument-string-invalid.js
new file mode 100644
index 0000000000..ef406af8ff
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/argument-string-invalid.js
@@ -0,0 +1,69 @@
+// |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.timezone.prototype.getoffsetnanosecondsfor
+description: >
+ RangeError thrown if an invalid ISO string (or syntactically valid ISO string
+ that is not supported) is used as an Instant
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ // invalid ISO strings:
+ "",
+ "invalid iso8601",
+ "2020-01-00T00:00Z",
+ "2020-01-32T00:00Z",
+ "2020-02-30T00:00Z",
+ "2021-02-29T00:00Z",
+ "2020-00-01T00:00Z",
+ "2020-13-01T00:00Z",
+ "2020-01-01TZ",
+ "2020-01-01T25:00:00Z",
+ "2020-01-01T01:60:00Z",
+ "2020-01-01T01:60:61Z",
+ "2020-01-01T00:00Zjunk",
+ "2020-01-01T00:00:00Zjunk",
+ "2020-01-01T00:00:00.000000000Zjunk",
+ "2020-01-01T00:00:00+00:00junk",
+ "2020-01-01T00:00:00+00:00[UTC]junk",
+ "2020-01-01T00:00:00+00:00[UTC][u-ca=iso8601]junk",
+ "02020-01-01T00:00Z",
+ "2020-001-01T00:00Z",
+ "2020-01-001T00:00Z",
+ "2020-01-01T001Z",
+ "2020-01-01T01:001Z",
+ "2020-01-01T01:01:001Z",
+ // valid, but forms not supported in Temporal:
+ "2020-W01-1T00:00Z",
+ "2020-001T00:00Z",
+ "+0002020-01-01T00:00Z",
+ // may be valid in other contexts, but insufficient information for Instant:
+ "2020-01",
+ "+002020-01",
+ "01-01",
+ "2020-W01",
+ "P1Y",
+ "-P12Y",
+ "2020-01-01",
+ "2020-01-01T00",
+ "2020-01-01T00:00",
+ "2020-01-01T00:00:00",
+ "2020-01-01T00:00:00.000000000",
+ // valid, but outside the supported range:
+ "-999999-01-01T00:00Z",
+ "+999999-01-01T00:00Z",
+];
+
+const instance = new Temporal.TimeZone("UTC");
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => instance.getOffsetNanosecondsFor(arg),
+ `"${arg}" should not be a valid ISO string for an Instant`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..147399f6e3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/argument-string-multiple-calendar.js
@@ -0,0 +1,28 @@
+// |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.timezone.prototype.getoffsetnanosecondsfor
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01T00:00Z[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00Z[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01T00:00Z[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00Z[u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+];
+const instance = new Temporal.TimeZone("UTC");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.getOffsetNanosecondsFor(arg),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..67630022fc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/argument-string-multiple-time-zone.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.timezone.prototype.getoffsetnanosecondsfor
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01T00:00Z[UTC][UTC]",
+ "1970-01-01T00:00Z[!UTC][UTC]",
+ "1970-01-01T00:00Z[UTC][!UTC]",
+ "1970-01-01T00:00Z[UTC][u-ca=iso8601][UTC]",
+ "1970-01-01T00:00Z[UTC][foo=bar][UTC]",
+];
+const instance = new Temporal.TimeZone("UTC");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.getOffsetNanosecondsFor(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/argument-string-time-separators.js
new file mode 100644
index 0000000000..7ab302d033
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/argument-string-time-separators.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.timezone.prototype.getoffsetnanosecondsfor
+description: Time separator in string argument can vary
+features: [Temporal]
+---*/
+
+const tests = [
+ ["1970-01-01T00:00Z", "uppercase T"],
+ ["1970-01-01t00:00Z", "lowercase T"],
+ ["1970-01-01 00:00Z", "space between date and time"],
+];
+
+const instance = new Temporal.TimeZone("UTC");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.getOffsetNanosecondsFor(arg);
+
+ assert.sameValue(
+ result,
+ 0,
+ `variant time separators (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..9748b0ceb9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/argument-string-time-zone-annotation.js
@@ -0,0 +1,34 @@
+// |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.timezone.prototype.getoffsetnanosecondsfor
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["1970-01-01T00:00Z[Asia/Kolkata]", "named, with Z"],
+ ["1970-01-01T00:00Z[!Europe/Vienna]", "named, with Z and !"],
+ ["1970-01-01T00:00Z[+00:00]", "numeric, with Z"],
+ ["1970-01-01T00:00Z[!-02:30]", "numeric, with Z and !"],
+ ["1970-01-01T00:00+00:00[UTC]", "named, with offset"],
+ ["1970-01-01T00:00+00:00[!Africa/Abidjan]", "named, with offset and !"],
+ ["1970-01-01T00:00+00:00[-08:00]", "numeric, with offset"],
+ ["1970-01-01T00:00+00:00[!+01:00]", "numeric, with offset and !"],
+];
+
+const instance = new Temporal.TimeZone("UTC");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.getOffsetNanosecondsFor(arg);
+
+ assert.sameValue(
+ result,
+ 0,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..f42373402a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/argument-string-unknown-annotation.js
@@ -0,0 +1,31 @@
+// |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.timezone.prototype.getoffsetnanosecondsfor
+description: Various forms of unknown annotation
+features: [Temporal]
+---*/
+
+const tests = [
+ ["1970-01-01T00:00Z[foo=bar]", "alone"],
+ ["1970-01-01T00:00Z[UTC][foo=bar]", "with time zone"],
+ ["1970-01-01T00:00Z[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["1970-01-01T00:00Z[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["1970-01-01T00:00Z[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+const instance = new Temporal.TimeZone("UTC");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.getOffsetNanosecondsFor(arg);
+
+ assert.sameValue(
+ result,
+ 0,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/argument-wrong-type.js
new file mode 100644
index 0000000000..23653301fa
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/argument-wrong-type.js
@@ -0,0 +1,46 @@
+// |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.timezone.prototype.getoffsetnanosecondsfor
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ for Instant
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.TimeZone("UTC");
+
+const primitiveTests = [
+ [undefined, "undefined"],
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [19761118, "number that would convert to a valid ISO string in other contexts"],
+ [1n, "bigint"],
+ [{}, "plain object"],
+ [Temporal.Instant, "Temporal.Instant, object"],
+];
+
+for (const [arg, description] of primitiveTests) {
+ assert.throws(
+ typeof arg === "string" || (typeof arg === "object" && arg !== null) || typeof arg === "function"
+ ? RangeError
+ : TypeError,
+ () => instance.getOffsetNanosecondsFor(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [Temporal.Instant.prototype, "Temporal.Instant.prototype, object"], // fails brand check in toString()
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.getOffsetNanosecondsFor(arg), `${description} does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/argument-zoneddatetime.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/argument-zoneddatetime.js
new file mode 100644
index 0000000000..f48f6cc84c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/argument-zoneddatetime.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.timezone.prototype.getoffsetnanosecondsfor
+description: Fast path for converting Temporal.ZonedDateTime to Temporal.Instant
+info: |
+ sec-temporal.timezone.prototype.getoffsetnanosecondsfor step 3:
+ 3. Set _instant_ to ? ToTemporalInstant(_instant_).
+ sec-temporal-totemporalinstant step 1.b:
+ b. If _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return ! CreateTemporalInstant(_item_.[[Nanoseconds]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalInstantFastPath((datetime) => {
+ const timeZone = Temporal.TimeZone.from("UTC");
+ const result = timeZone.getOffsetNanosecondsFor(datetime);
+ assert.sameValue(result, 0, "offset result");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/branding.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/branding.js
new file mode 100644
index 0000000000..f7450dd176
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/branding.js
@@ -0,0 +1,27 @@
+// |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.timezone.prototype.getoffsetnanosecondsfor
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const getOffsetNanosecondsFor = Temporal.TimeZone.prototype.getOffsetNanosecondsFor;
+
+assert.sameValue(typeof getOffsetNanosecondsFor, "function");
+
+const args = [new Temporal.Instant(0n)];
+
+assert.throws(TypeError, () => getOffsetNanosecondsFor.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => getOffsetNanosecondsFor.apply(null, args), "null");
+assert.throws(TypeError, () => getOffsetNanosecondsFor.apply(true, args), "true");
+assert.throws(TypeError, () => getOffsetNanosecondsFor.apply("", args), "empty string");
+assert.throws(TypeError, () => getOffsetNanosecondsFor.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => getOffsetNanosecondsFor.apply(1, args), "1");
+assert.throws(TypeError, () => getOffsetNanosecondsFor.apply({}, args), "plain object");
+assert.throws(TypeError, () => getOffsetNanosecondsFor.apply(Temporal.TimeZone, args), "Temporal.TimeZone");
+assert.throws(TypeError, () => getOffsetNanosecondsFor.apply(Temporal.TimeZone.prototype, args), "Temporal.TimeZone.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/browser.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/builtin.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/builtin.js
new file mode 100644
index 0000000000..abc493fc9c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/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.timezone.prototype.getoffsetnanosecondsfor
+description: >
+ Tests that Temporal.TimeZone.prototype.getOffsetNanosecondsFor
+ 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.TimeZone.prototype.getOffsetNanosecondsFor),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.TimeZone.prototype.getOffsetNanosecondsFor),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.TimeZone.prototype.getOffsetNanosecondsFor),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.TimeZone.prototype.getOffsetNanosecondsFor.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/instant-string-limits.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/instant-string-limits.js
new file mode 100644
index 0000000000..e2714c7819
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/instant-string-limits.js
@@ -0,0 +1,47 @@
+// |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.timezone.prototype.getoffsetnanosecondsfor
+description: String arguments at the limit of the representable range
+features: [Temporal]
+---*/
+
+const instance = new Temporal.TimeZone("UTC");
+
+const minInstantStrings = [
+ "-271821-04-20T00:00Z",
+ "-271821-04-19T23:00-01:00",
+ "-271821-04-19T00:00:00.000000001-23:59:59.999999999",
+];
+for (const str of minInstantStrings) {
+ assert.sameValue(instance.getOffsetNanosecondsFor(str), 0, `instant string ${str} should be valid`);
+}
+
+const maxInstantStrings = [
+ "+275760-09-13T00:00Z",
+ "+275760-09-13T01:00+01:00",
+ "+275760-09-13T23:59:59.999999999+23:59:59.999999999",
+];
+
+for (const str of maxInstantStrings) {
+ assert.sameValue(instance.getOffsetNanosecondsFor(str), 0, `instant string ${str} should be valid`);
+}
+
+const outOfRangeInstantStrings = [
+ "-271821-04-19T23:59:59.999999999Z",
+ "-271821-04-19T23:00-00:59:59.999999999",
+ "-271821-04-19T00:00:00-23:59:59.999999999",
+ "-271821-04-19T00:00:00-24:00",
+ "+275760-09-13T00:00:00.000000001Z",
+ "+275760-09-13T01:00+00:59:59.999999999",
+ "+275760-09-14T00:00+23:59:59.999999999",
+ "+275760-09-14T00:00+24:00",
+];
+
+for (const str of outOfRangeInstantStrings) {
+ assert.throws(RangeError, () => instance.getOffsetNanosecondsFor(str), `instant string ${str} should not be valid`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/instant-string.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/instant-string.js
new file mode 100644
index 0000000000..455ea14865
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/instant-string.js
@@ -0,0 +1,34 @@
+// |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.timezone.prototype.getoffsetnanosecondsfor
+description: Conversion of ISO date-time strings to Temporal.Instant instances
+features: [Temporal]
+---*/
+
+const instance = new Temporal.TimeZone("UTC");
+
+let str = "1970-01-01T00:00";
+assert.throws(RangeError, () => instance.getOffsetNanosecondsFor(str), "bare date-time string is not an instant");
+str = "1970-01-01T00:00[UTC]";
+assert.throws(RangeError, () => instance.getOffsetNanosecondsFor(str), "date-time + IANA annotation is not an instant");
+
+// The following are all valid strings so should not throw:
+
+const valids = [
+ "1970-01-01T00:00Z",
+ "1970-01-01T00:00+01:00",
+ "1970-01-01T00:00Z[UTC]",
+ "1970-01-01T00:00+01:00[UTC]",
+ "1970-01-01T00:00Z[u-ca=hebrew]",
+ "1970-01-01T00:00+01:00[u-ca=hebrew]",
+ "1970-01-01T00:00+01:00[Etc/Ignored][u-ca=hebrew]",
+];
+for (const str of valids) {
+ const result = instance.getOffsetNanosecondsFor(str);
+ assert.sameValue(result, 0);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/leap-second.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/leap-second.js
new file mode 100644
index 0000000000..1fb52dbb1d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/leap-second.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.timezone.prototype.getoffsetnanosecondsfor
+description: Leap second is a valid ISO string for Instant
+features: [Temporal]
+---*/
+
+const instance = new Temporal.TimeZone("UTC");
+
+const arg = "2016-12-31T23:59:60Z";
+const result = instance.getOffsetNanosecondsFor(arg);
+assert.sameValue(
+ result,
+ 0,
+ "leap second is a valid ISO string for Instant"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/length.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/length.js
new file mode 100644
index 0000000000..9a9a560997
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/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.timezone.prototype.getoffsetnanosecondsfor
+description: Temporal.TimeZone.prototype.getOffsetNanosecondsFor.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.TimeZone.prototype.getOffsetNanosecondsFor, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/name.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/name.js
new file mode 100644
index 0000000000..83bf240147
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/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.timezone.prototype.getoffsetnanosecondsfor
+description: Temporal.TimeZone.prototype.getOffsetNanosecondsFor.name is "getOffsetNanosecondsFor".
+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.TimeZone.prototype.getOffsetNanosecondsFor, "name", {
+ value: "getOffsetNanosecondsFor",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/not-a-constructor.js
new file mode 100644
index 0000000000..c8dc628507
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/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.timezone.prototype.getoffsetnanosecondsfor
+description: >
+ Temporal.TimeZone.prototype.getOffsetNanosecondsFor 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.TimeZone.prototype.getOffsetNanosecondsFor();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.TimeZone.prototype.getOffsetNanosecondsFor), false,
+ "isConstructor(Temporal.TimeZone.prototype.getOffsetNanosecondsFor)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/prop-desc.js
new file mode 100644
index 0000000000..5a077f9087
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/prop-desc.js
@@ -0,0 +1,24 @@
+// |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.timezone.prototype.getoffsetnanosecondsfor
+description: The "getOffsetNanosecondsFor" property of Temporal.TimeZone.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.TimeZone.prototype.getOffsetNanosecondsFor,
+ "function",
+ "`typeof TimeZone.prototype.getOffsetNanosecondsFor` is `function`"
+);
+
+verifyProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/shell.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/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/TimeZone/prototype/getOffsetNanosecondsFor/year-zero.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/year-zero.js
new file mode 100644
index 0000000000..c71e09cdf3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/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.timezone.prototype.getoffsetnanosecondsfor
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-03-30T00:45Z",
+ "-000000-03-30T01:45+01:00",
+ "-000000-03-30T01:45:00+00:00[UTC]",
+];
+const instance = new Temporal.TimeZone("UTC");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.getOffsetNanosecondsFor(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/argument-not-absolute-getOffsetNanosecondsFor-override.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/argument-not-absolute-getOffsetNanosecondsFor-override.js
new file mode 100644
index 0000000000..6db17f2203
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/argument-not-absolute-getOffsetNanosecondsFor-override.js
@@ -0,0 +1,24 @@
+// |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.timezone.prototype.getoffsetstringfor
+description: timeZone.getOffsetNanosecondsFor not called when argument cannot be converted to Temporal.Instant
+features: [Temporal]
+---*/
+
+const timeZone = Temporal.TimeZone.from("UTC");
+let called = false;
+timeZone.getOffsetNanosecondsFor = () => called = true;
+assert.throws(TypeError, () => timeZone.getOffsetStringFor(undefined), "undefined");
+assert.throws(TypeError, () => timeZone.getOffsetStringFor(null), "null");
+assert.throws(TypeError, () => timeZone.getOffsetStringFor(true), "boolean");
+assert.throws(RangeError, () => timeZone.getOffsetStringFor(""), "empty string");
+assert.throws(TypeError, () => timeZone.getOffsetStringFor(Symbol()), "Symbol");
+assert.throws(TypeError, () => timeZone.getOffsetStringFor(5), "number");
+assert.throws(TypeError, () => timeZone.getOffsetStringFor(5n), "bigint");
+assert.throws(RangeError, () => timeZone.getOffsetStringFor({}), "plain object");
+assert.sameValue(called, false);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..b943ca04b0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/argument-string-calendar-annotation.js
@@ -0,0 +1,33 @@
+// |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.timezone.prototype.getoffsetstringfor
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["1970-01-01T00:00Z[u-ca=iso8601]", "without time zone"],
+ ["1970-01-01T00:00Z[UTC][u-ca=gregory]", "with time zone"],
+ ["1970-01-01T00:00Z[!u-ca=hebrew]", "with ! and no time zone"],
+ ["1970-01-01T00:00Z[UTC][!u-ca=chinese]", "with ! and time zone"],
+ ["1970-01-01T00:00Z[u-ca=discord]", "annotation is ignored"],
+ ["1970-01-01T00:00Z[!u-ca=discord]", "annotation with ! is ignored"],
+ ["1970-01-01T00:00Z[u-ca=iso8601][u-ca=discord]", "two annotations are ignored"],
+];
+
+const instance = new Temporal.TimeZone("UTC");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.getOffsetStringFor(arg);
+
+ assert.sameValue(
+ result,
+ "+00:00",
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..86c1b0beef
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/argument-string-critical-unknown-annotation.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.timezone.prototype.getoffsetstringfor
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01T00:00Z[!foo=bar]",
+ "1970-01-01T00:00Z[UTC][!foo=bar]",
+ "1970-01-01T00:00Z[u-ca=iso8601][!foo=bar]",
+ "1970-01-01T00:00Z[UTC][!foo=bar][u-ca=iso8601]",
+ "1970-01-01T00:00Z[foo=bar][!_foo-bar0=Dont-Ignore-This-99999999999]",
+];
+const instance = new Temporal.TimeZone("UTC");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.getOffsetStringFor(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..9b40603406
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/argument-string-date-with-utc-offset.js
@@ -0,0 +1,52 @@
+// |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.timezone.prototype.getoffsetstringfor
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+---*/
+
+const instance = new Temporal.TimeZone("UTC");
+
+const validStrings = [
+ "1970-01-01T00Z",
+ "1970-01-01T00Z[UTC]",
+ "1970-01-01T00Z[!UTC]",
+ "1970-01-01T00Z[Europe/Vienna]",
+ "1970-01-01T00+00:00",
+ "1970-01-01T00+00:00[UTC]",
+ "1970-01-01T00+00:00[!UTC]",
+ "1969-12-31T16-08:00[America/Vancouver]",
+];
+
+for (const arg of validStrings) {
+ const result = instance.getOffsetStringFor(arg);
+
+ assert.sameValue(
+ result,
+ "+00:00",
+ `"${arg}" is a valid UTC offset with time for Instant`
+ );
+}
+
+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.getOffsetStringFor(arg),
+ `"${arg}" UTC offset without time is not valid for Instant`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/argument-string-invalid.js
new file mode 100644
index 0000000000..f79f971678
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/argument-string-invalid.js
@@ -0,0 +1,69 @@
+// |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.timezone.prototype.getoffsetstringfor
+description: >
+ RangeError thrown if an invalid ISO string (or syntactically valid ISO string
+ that is not supported) is used as an Instant
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ // invalid ISO strings:
+ "",
+ "invalid iso8601",
+ "2020-01-00T00:00Z",
+ "2020-01-32T00:00Z",
+ "2020-02-30T00:00Z",
+ "2021-02-29T00:00Z",
+ "2020-00-01T00:00Z",
+ "2020-13-01T00:00Z",
+ "2020-01-01TZ",
+ "2020-01-01T25:00:00Z",
+ "2020-01-01T01:60:00Z",
+ "2020-01-01T01:60:61Z",
+ "2020-01-01T00:00Zjunk",
+ "2020-01-01T00:00:00Zjunk",
+ "2020-01-01T00:00:00.000000000Zjunk",
+ "2020-01-01T00:00:00+00:00junk",
+ "2020-01-01T00:00:00+00:00[UTC]junk",
+ "2020-01-01T00:00:00+00:00[UTC][u-ca=iso8601]junk",
+ "02020-01-01T00:00Z",
+ "2020-001-01T00:00Z",
+ "2020-01-001T00:00Z",
+ "2020-01-01T001Z",
+ "2020-01-01T01:001Z",
+ "2020-01-01T01:01:001Z",
+ // valid, but forms not supported in Temporal:
+ "2020-W01-1T00:00Z",
+ "2020-001T00:00Z",
+ "+0002020-01-01T00:00Z",
+ // may be valid in other contexts, but insufficient information for Instant:
+ "2020-01",
+ "+002020-01",
+ "01-01",
+ "2020-W01",
+ "P1Y",
+ "-P12Y",
+ "2020-01-01",
+ "2020-01-01T00",
+ "2020-01-01T00:00",
+ "2020-01-01T00:00:00",
+ "2020-01-01T00:00:00.000000000",
+ // valid, but outside the supported range:
+ "-999999-01-01T00:00Z",
+ "+999999-01-01T00:00Z",
+];
+
+const instance = new Temporal.TimeZone("UTC");
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => instance.getOffsetStringFor(arg),
+ `"${arg}" should not be a valid ISO string for an Instant`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..bfd20cc7bb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/argument-string-multiple-calendar.js
@@ -0,0 +1,28 @@
+// |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.timezone.prototype.getoffsetstringfor
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01T00:00Z[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00Z[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01T00:00Z[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00Z[u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+];
+const instance = new Temporal.TimeZone("UTC");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.getOffsetStringFor(arg),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..a932833b66
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/argument-string-multiple-time-zone.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.timezone.prototype.getoffsetstringfor
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01T00:00Z[UTC][UTC]",
+ "1970-01-01T00:00Z[!UTC][UTC]",
+ "1970-01-01T00:00Z[UTC][!UTC]",
+ "1970-01-01T00:00Z[UTC][u-ca=iso8601][UTC]",
+ "1970-01-01T00:00Z[UTC][foo=bar][UTC]",
+];
+const instance = new Temporal.TimeZone("UTC");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.getOffsetStringFor(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/argument-string-time-separators.js
new file mode 100644
index 0000000000..6568bbc3e4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/argument-string-time-separators.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.timezone.prototype.getoffsetstringfor
+description: Time separator in string argument can vary
+features: [Temporal]
+---*/
+
+const tests = [
+ ["1970-01-01T00:00Z", "uppercase T"],
+ ["1970-01-01t00:00Z", "lowercase T"],
+ ["1970-01-01 00:00Z", "space between date and time"],
+];
+
+const instance = new Temporal.TimeZone("UTC");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.getOffsetStringFor(arg);
+
+ assert.sameValue(
+ result,
+ "+00:00",
+ `variant time separators (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..b158b605e6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/argument-string-time-zone-annotation.js
@@ -0,0 +1,34 @@
+// |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.timezone.prototype.getoffsetstringfor
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["1970-01-01T00:00Z[Asia/Kolkata]", "named, with Z"],
+ ["1970-01-01T00:00Z[!Europe/Vienna]", "named, with Z and !"],
+ ["1970-01-01T00:00Z[+00:00]", "numeric, with Z"],
+ ["1970-01-01T00:00Z[!-02:30]", "numeric, with Z and !"],
+ ["1970-01-01T00:00+00:00[UTC]", "named, with offset"],
+ ["1970-01-01T00:00+00:00[!Africa/Abidjan]", "named, with offset and !"],
+ ["1970-01-01T00:00+00:00[-08:00]", "numeric, with offset"],
+ ["1970-01-01T00:00+00:00[!+01:00]", "numeric, with offset and !"],
+];
+
+const instance = new Temporal.TimeZone("UTC");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.getOffsetStringFor(arg);
+
+ assert.sameValue(
+ result,
+ "+00:00",
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..6903345bba
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/argument-string-unknown-annotation.js
@@ -0,0 +1,31 @@
+// |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.timezone.prototype.getoffsetstringfor
+description: Various forms of unknown annotation
+features: [Temporal]
+---*/
+
+const tests = [
+ ["1970-01-01T00:00Z[foo=bar]", "alone"],
+ ["1970-01-01T00:00Z[UTC][foo=bar]", "with time zone"],
+ ["1970-01-01T00:00Z[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["1970-01-01T00:00Z[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["1970-01-01T00:00Z[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+const instance = new Temporal.TimeZone("UTC");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.getOffsetStringFor(arg);
+
+ assert.sameValue(
+ result,
+ "+00:00",
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/argument-wrong-type.js
new file mode 100644
index 0000000000..d64c058d3c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/argument-wrong-type.js
@@ -0,0 +1,46 @@
+// |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.timezone.prototype.getoffsetstringfor
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ for Instant
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.TimeZone("UTC");
+
+const primitiveTests = [
+ [undefined, "undefined"],
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [19761118, "number that would convert to a valid ISO string in other contexts"],
+ [1n, "bigint"],
+ [{}, "plain object"],
+ [Temporal.Instant, "Temporal.Instant, object"],
+];
+
+for (const [arg, description] of primitiveTests) {
+ assert.throws(
+ typeof arg === "string" || (typeof arg === "object" && arg !== null) || typeof arg === "function"
+ ? RangeError
+ : TypeError,
+ () => instance.getOffsetStringFor(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [Temporal.Instant.prototype, "Temporal.Instant.prototype, object"], // fails brand check in toString()
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.getOffsetStringFor(arg), `${description} does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/argument-zoneddatetime.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/argument-zoneddatetime.js
new file mode 100644
index 0000000000..38ce243810
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/argument-zoneddatetime.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.timezone.prototype.getoffsetstringfor
+description: Fast path for converting Temporal.ZonedDateTime to Temporal.Instant
+info: |
+ sec-temporal.timezone.prototype.getoffsetstringfor step 3:
+ 3. Set _instant_ to ? ToTemporalInstant(_instant_).
+ sec-temporal-totemporalinstant step 1.b:
+ b. If _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return ! CreateTemporalInstant(_item_.[[Nanoseconds]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalInstantFastPath((datetime) => {
+ const timeZone = Temporal.TimeZone.from("UTC");
+ const result = timeZone.getOffsetStringFor(datetime);
+ assert.sameValue(result, "+00:00", "offset result");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/basic.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/basic.js
new file mode 100644
index 0000000000..cf45188e28
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/basic.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.timezone.prototype.getoffsetstringfor
+description: Basic tests for Temporal.TimeZone.prototype.getOffsetStringFor
+features: [BigInt, Temporal]
+---*/
+
+const instant = new Temporal.Instant(0n);
+
+function test(timeZoneIdentifier, expectedOffsetString, description) {
+ const timeZone = new Temporal.TimeZone(timeZoneIdentifier);
+ assert.sameValue(timeZone.getOffsetStringFor(instant), expectedOffsetString, description);
+}
+
+test("UTC", "+00:00", "offset of UTC is +00:00");
+test("+01:00", "+01:00", "positive offset");
+test("-05:00", "-05:00", "negative offset");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/branding.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/branding.js
new file mode 100644
index 0000000000..404397204f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/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.timezone.prototype.getoffsetstringfor
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const getOffsetStringFor = Temporal.TimeZone.prototype.getOffsetStringFor;
+
+assert.sameValue(typeof getOffsetStringFor, "function");
+
+const args = [new Temporal.Instant(0n)];
+
+assert.throws(TypeError, () => getOffsetStringFor.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => getOffsetStringFor.apply(null, args), "null");
+assert.throws(TypeError, () => getOffsetStringFor.apply(true, args), "true");
+assert.throws(TypeError, () => getOffsetStringFor.apply("", args), "empty string");
+assert.throws(TypeError, () => getOffsetStringFor.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => getOffsetStringFor.apply(1, args), "1");
+assert.throws(TypeError, () => getOffsetStringFor.apply({}, args), "plain object");
+assert.throws(TypeError, () => getOffsetStringFor.apply(Temporal.TimeZone, args), "Temporal.TimeZone");
+assert.throws(TypeError, () => getOffsetStringFor.apply(Temporal.TimeZone.prototype, args), "Temporal.TimeZone.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/browser.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/builtin.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/builtin.js
new file mode 100644
index 0000000000..8f21c1bdac
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/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.timezone.prototype.getoffsetstringfor
+description: >
+ Tests that Temporal.TimeZone.prototype.getOffsetStringFor
+ 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.TimeZone.prototype.getOffsetStringFor),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.TimeZone.prototype.getOffsetStringFor),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.TimeZone.prototype.getOffsetStringFor),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.TimeZone.prototype.getOffsetStringFor.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/instant-string-limits.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/instant-string-limits.js
new file mode 100644
index 0000000000..aa1a319ad6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/instant-string-limits.js
@@ -0,0 +1,47 @@
+// |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.timezone.prototype.getoffsetstringfor
+description: String arguments at the limit of the representable range
+features: [Temporal]
+---*/
+
+const instance = new Temporal.TimeZone("UTC");
+
+const minInstantStrings = [
+ "-271821-04-20T00:00Z",
+ "-271821-04-19T23:00-01:00",
+ "-271821-04-19T00:00:00.000000001-23:59:59.999999999",
+];
+for (const str of minInstantStrings) {
+ assert.sameValue(instance.getOffsetStringFor(str), "+00:00", `instant string ${str} should be valid`);
+}
+
+const maxInstantStrings = [
+ "+275760-09-13T00:00Z",
+ "+275760-09-13T01:00+01:00",
+ "+275760-09-13T23:59:59.999999999+23:59:59.999999999",
+];
+
+for (const str of maxInstantStrings) {
+ assert.sameValue(instance.getOffsetStringFor(str), "+00:00", `instant string ${str} should be valid`);
+}
+
+const outOfRangeInstantStrings = [
+ "-271821-04-19T23:59:59.999999999Z",
+ "-271821-04-19T23:00-00:59:59.999999999",
+ "-271821-04-19T00:00:00-23:59:59.999999999",
+ "-271821-04-19T00:00:00-24:00",
+ "+275760-09-13T00:00:00.000000001Z",
+ "+275760-09-13T01:00+00:59:59.999999999",
+ "+275760-09-14T00:00+23:59:59.999999999",
+ "+275760-09-14T00:00+24:00",
+];
+
+for (const str of outOfRangeInstantStrings) {
+ assert.throws(RangeError, () => instance.getOffsetStringFor(str), `instant string ${str} should not be valid`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/instant-string.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/instant-string.js
new file mode 100644
index 0000000000..7f24d20313
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/instant-string.js
@@ -0,0 +1,34 @@
+// |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.timezone.prototype.getoffsetstringfor
+description: Conversion of ISO date-time strings to Temporal.Instant instances
+features: [Temporal]
+---*/
+
+const instance = new Temporal.TimeZone("UTC");
+
+let str = "1970-01-01T00:00";
+assert.throws(RangeError, () => instance.getOffsetStringFor(str), "bare date-time string is not an instant");
+str = "1970-01-01T00:00[UTC]";
+assert.throws(RangeError, () => instance.getOffsetStringFor(str), "date-time + IANA annotation is not an instant");
+
+// The following are all valid strings so should not throw:
+
+const valids = [
+ "1970-01-01T00:00Z",
+ "1970-01-01T00:00+01:00",
+ "1970-01-01T00:00Z[UTC]",
+ "1970-01-01T00:00+01:00[UTC]",
+ "1970-01-01T00:00Z[u-ca=hebrew]",
+ "1970-01-01T00:00+01:00[u-ca=hebrew]",
+ "1970-01-01T00:00+01:00[Etc/Ignored][u-ca=hebrew]",
+];
+for (const str of valids) {
+ const result = instance.getOffsetStringFor(str);
+ assert.sameValue(result, "+00:00");
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/leap-second.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/leap-second.js
new file mode 100644
index 0000000000..a7eccfdb9b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/leap-second.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.timezone.prototype.getoffsetstringfor
+description: Leap second is a valid ISO string for Instant
+features: [Temporal]
+---*/
+
+const instance = new Temporal.TimeZone("UTC");
+
+const arg = "2016-12-31T23:59:60Z";
+const result = instance.getOffsetStringFor(arg);
+assert.sameValue(
+ result,
+ "+00:00",
+ "leap second is a valid ISO string for Instant"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/length.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/length.js
new file mode 100644
index 0000000000..459d358ba2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/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.timezone.prototype.getoffsetstringfor
+description: Temporal.TimeZone.prototype.getOffsetStringFor.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.TimeZone.prototype.getOffsetStringFor, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/name.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/name.js
new file mode 100644
index 0000000000..2d89db3d8b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/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.timezone.prototype.getoffsetstringfor
+description: Temporal.TimeZone.prototype.getOffsetStringFor.name is "getOffsetStringFor".
+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.TimeZone.prototype.getOffsetStringFor, "name", {
+ value: "getOffsetStringFor",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/not-a-constructor.js
new file mode 100644
index 0000000000..44e9fbc212
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/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.timezone.prototype.getoffsetstringfor
+description: >
+ Temporal.TimeZone.prototype.getOffsetStringFor 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.TimeZone.prototype.getOffsetStringFor();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.TimeZone.prototype.getOffsetStringFor), false,
+ "isConstructor(Temporal.TimeZone.prototype.getOffsetStringFor)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/prop-desc.js
new file mode 100644
index 0000000000..0e08435c6f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/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.timezone.prototype.getoffsetstringfor
+description: The "getOffsetStringFor" property of Temporal.TimeZone.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.TimeZone.prototype.getOffsetStringFor,
+ "function",
+ "`typeof TimeZone.prototype.getOffsetStringFor` is `function`"
+);
+
+verifyProperty(Temporal.TimeZone.prototype, "getOffsetStringFor", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/shell.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/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/TimeZone/prototype/getOffsetStringFor/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..2adc452075
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,18 @@
+// |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.timezone.prototype.getoffsetstringfor
+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 instant = new Temporal.Instant(1_000_000_000_987_654_321n);
+ assert.throws(RangeError, () => timeZone.getOffsetStringFor(instant));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..ac0ea2597c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/timezone-getoffsetnanosecondsfor-not-callable.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.timezone.prototype.getoffsetstringfor
+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 instant = new Temporal.Instant(1_000_000_000_987_654_321n);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => timeZone.getOffsetStringFor(instant),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..3f0d30bc8d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,18 @@
+// |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.timezone.prototype.getoffsetstringfor
+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 instant = new Temporal.Instant(1_000_000_000_987_654_321n);
+ assert.throws(RangeError, () => timeZone.getOffsetStringFor(instant));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..0085d63704
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/timezone-getoffsetnanosecondsfor-wrong-type.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.timezone.prototype.getoffsetstringfor
+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 instant = new Temporal.Instant(1_000_000_000_987_654_321n);
+ assert.throws(TypeError, () => timeZone.getOffsetStringFor(instant));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/year-zero.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/year-zero.js
new file mode 100644
index 0000000000..36f2e103e7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/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.timezone.prototype.getoffsetstringfor
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-03-30T00:45Z",
+ "-000000-03-30T01:45+01:00",
+ "-000000-03-30T01:45:00+00:00[UTC]",
+];
+const instance = new Temporal.TimeZone("UTC");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.getOffsetStringFor(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-negative-epochnanoseconds.js
new file mode 100644
index 0000000000..debcd5957e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-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.timezone.prototype.getplaindatetimefor
+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 instant = new Temporal.Instant(-13849764_999_999_999n);
+
+// 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.TimeZone("UTC");
+const result = instance.getPlainDateTimeFor(instant);
+TemporalHelpers.assertPlainDateTime(result, 1969, 7, "M07", 24, 16, 50, 35, 0, 0, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-not-absolute-getOffsetNanosecondsFor-override.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-not-absolute-getOffsetNanosecondsFor-override.js
new file mode 100644
index 0000000000..6c88ccfadf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-not-absolute-getOffsetNanosecondsFor-override.js
@@ -0,0 +1,24 @@
+// |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.timezone.prototype.getplaindatetimefor
+description: timeZone.getOffsetNanosecondsFor not called when argument cannot be converted to Temporal.Instant
+features: [Temporal]
+---*/
+
+const timeZone = Temporal.TimeZone.from("UTC");
+let called = false;
+timeZone.getOffsetNanosecondsFor = () => called = true;
+assert.throws(TypeError, () => timeZone.getPlainDateTimeFor(undefined), "undefined");
+assert.throws(TypeError, () => timeZone.getPlainDateTimeFor(null), "null");
+assert.throws(TypeError, () => timeZone.getPlainDateTimeFor(true), "boolean");
+assert.throws(RangeError, () => timeZone.getPlainDateTimeFor(""), "empty string");
+assert.throws(TypeError, () => timeZone.getPlainDateTimeFor(Symbol()), "Symbol");
+assert.throws(TypeError, () => timeZone.getPlainDateTimeFor(5), "number");
+assert.throws(TypeError, () => timeZone.getPlainDateTimeFor(5n), "bigint");
+assert.throws(RangeError, () => timeZone.getPlainDateTimeFor({}), "plain object");
+assert.sameValue(called, false);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-object-tostring.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-object-tostring.js
new file mode 100644
index 0000000000..371979f3b2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-object-tostring.js
@@ -0,0 +1,23 @@
+// |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.timezone.prototype.getplaindatetimefor
+description: Object is converted to a string, then to Temporal.Instant
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.TimeZone("UTC");
+
+const arg = {};
+assert.throws(RangeError, () => instance.getPlainDateTimeFor(arg), "[object Object] is not a valid ISO string");
+
+arg.toString = function() {
+ return "1970-01-01T00:00Z";
+};
+const result = instance.getPlainDateTimeFor(arg);
+TemporalHelpers.assertPlainDateTime(result, 1970, 1, "M01", 1, 0, 0, 0, 0, 0, 0, "result of toString is interpreted as ISO string");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..2e4e0795d1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-string-calendar-annotation.js
@@ -0,0 +1,34 @@
+// |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.timezone.prototype.getplaindatetimefor
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["1970-01-01T00:00Z[u-ca=iso8601]", "without time zone"],
+ ["1970-01-01T00:00Z[UTC][u-ca=gregory]", "with time zone"],
+ ["1970-01-01T00:00Z[!u-ca=hebrew]", "with ! and no time zone"],
+ ["1970-01-01T00:00Z[UTC][!u-ca=chinese]", "with ! and time zone"],
+ ["1970-01-01T00:00Z[u-ca=discord]", "annotation is ignored"],
+ ["1970-01-01T00:00Z[!u-ca=discord]", "annotation with ! is ignored"],
+ ["1970-01-01T00:00Z[u-ca=iso8601][u-ca=discord]", "two annotations are ignored"],
+];
+
+const instance = new Temporal.TimeZone("UTC");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.getPlainDateTimeFor(arg);
+
+ TemporalHelpers.assertPlainDateTime(
+ result,
+ 1970, 1, "M01", 1, 0, 0, 0, 0, 0, 0,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..b2af63f79f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-string-critical-unknown-annotation.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.timezone.prototype.getplaindatetimefor
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01T00:00Z[!foo=bar]",
+ "1970-01-01T00:00Z[UTC][!foo=bar]",
+ "1970-01-01T00:00Z[u-ca=iso8601][!foo=bar]",
+ "1970-01-01T00:00Z[UTC][!foo=bar][u-ca=iso8601]",
+ "1970-01-01T00:00Z[foo=bar][!_foo-bar0=Dont-Ignore-This-99999999999]",
+];
+const instance = new Temporal.TimeZone("UTC");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.getPlainDateTimeFor(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..2f5ed3fd9b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/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.timezone.prototype.getplaindatetimefor
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const instance = new Temporal.TimeZone("UTC");
+
+const validStrings = [
+ "1970-01-01T00Z",
+ "1970-01-01T00Z[UTC]",
+ "1970-01-01T00Z[!UTC]",
+ "1970-01-01T00Z[Europe/Vienna]",
+ "1970-01-01T00+00:00",
+ "1970-01-01T00+00:00[UTC]",
+ "1970-01-01T00+00:00[!UTC]",
+ "1969-12-31T16-08:00[America/Vancouver]",
+];
+
+for (const arg of validStrings) {
+ const result = instance.getPlainDateTimeFor(arg);
+
+ TemporalHelpers.assertPlainDateTime(
+ result,
+ 1970, 1, "M01", 1, 0, 0, 0, 0, 0, 0,
+ `"${arg}" is a valid UTC offset with time for Instant`
+ );
+}
+
+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.getPlainDateTimeFor(arg),
+ `"${arg}" UTC offset without time is not valid for Instant`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-string-invalid.js
new file mode 100644
index 0000000000..940de774cf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-string-invalid.js
@@ -0,0 +1,69 @@
+// |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.timezone.prototype.getplaindatetimefor
+description: >
+ RangeError thrown if an invalid ISO string (or syntactically valid ISO string
+ that is not supported) is used as an Instant
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ // invalid ISO strings:
+ "",
+ "invalid iso8601",
+ "2020-01-00T00:00Z",
+ "2020-01-32T00:00Z",
+ "2020-02-30T00:00Z",
+ "2021-02-29T00:00Z",
+ "2020-00-01T00:00Z",
+ "2020-13-01T00:00Z",
+ "2020-01-01TZ",
+ "2020-01-01T25:00:00Z",
+ "2020-01-01T01:60:00Z",
+ "2020-01-01T01:60:61Z",
+ "2020-01-01T00:00Zjunk",
+ "2020-01-01T00:00:00Zjunk",
+ "2020-01-01T00:00:00.000000000Zjunk",
+ "2020-01-01T00:00:00+00:00junk",
+ "2020-01-01T00:00:00+00:00[UTC]junk",
+ "2020-01-01T00:00:00+00:00[UTC][u-ca=iso8601]junk",
+ "02020-01-01T00:00Z",
+ "2020-001-01T00:00Z",
+ "2020-01-001T00:00Z",
+ "2020-01-01T001Z",
+ "2020-01-01T01:001Z",
+ "2020-01-01T01:01:001Z",
+ // valid, but forms not supported in Temporal:
+ "2020-W01-1T00:00Z",
+ "2020-001T00:00Z",
+ "+0002020-01-01T00:00Z",
+ // may be valid in other contexts, but insufficient information for Instant:
+ "2020-01",
+ "+002020-01",
+ "01-01",
+ "2020-W01",
+ "P1Y",
+ "-P12Y",
+ "2020-01-01",
+ "2020-01-01T00",
+ "2020-01-01T00:00",
+ "2020-01-01T00:00:00",
+ "2020-01-01T00:00:00.000000000",
+ // valid, but outside the supported range:
+ "-999999-01-01T00:00Z",
+ "+999999-01-01T00:00Z",
+];
+
+const instance = new Temporal.TimeZone("UTC");
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => instance.getPlainDateTimeFor(arg),
+ `"${arg}" should not be a valid ISO string for an Instant`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..795d9d0314
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-string-multiple-calendar.js
@@ -0,0 +1,28 @@
+// |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.timezone.prototype.getplaindatetimefor
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01T00:00Z[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00Z[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01T00:00Z[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00Z[u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+];
+const instance = new Temporal.TimeZone("UTC");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.getPlainDateTimeFor(arg),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..6738c47956
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-string-multiple-time-zone.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.timezone.prototype.getplaindatetimefor
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01T00:00Z[UTC][UTC]",
+ "1970-01-01T00:00Z[!UTC][UTC]",
+ "1970-01-01T00:00Z[UTC][!UTC]",
+ "1970-01-01T00:00Z[UTC][u-ca=iso8601][UTC]",
+ "1970-01-01T00:00Z[UTC][foo=bar][UTC]",
+];
+const instance = new Temporal.TimeZone("UTC");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.getPlainDateTimeFor(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-string-time-separators.js
new file mode 100644
index 0000000000..ea443c1a3c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-string-time-separators.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.timezone.prototype.getplaindatetimefor
+description: Time separator in string argument can vary
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["1970-01-01T00:00Z", "uppercase T"],
+ ["1970-01-01t00:00Z", "lowercase T"],
+ ["1970-01-01 00:00Z", "space between date and time"],
+];
+
+const instance = new Temporal.TimeZone("UTC");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.getPlainDateTimeFor(arg);
+
+ TemporalHelpers.assertPlainDateTime(
+ result,
+ 1970, 1, "M01", 1, 0, 0, 0, 0, 0, 0,
+ `variant time separators (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..15a4408df4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-string-time-zone-annotation.js
@@ -0,0 +1,35 @@
+// |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.timezone.prototype.getplaindatetimefor
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["1970-01-01T00:00Z[Asia/Kolkata]", "named, with Z"],
+ ["1970-01-01T00:00Z[!Europe/Vienna]", "named, with Z and !"],
+ ["1970-01-01T00:00Z[+00:00]", "numeric, with Z"],
+ ["1970-01-01T00:00Z[!-02:30]", "numeric, with Z and !"],
+ ["1970-01-01T00:00+00:00[UTC]", "named, with offset"],
+ ["1970-01-01T00:00+00:00[!Africa/Abidjan]", "named, with offset and !"],
+ ["1970-01-01T00:00+00:00[-08:00]", "numeric, with offset"],
+ ["1970-01-01T00:00+00:00[!+01:00]", "numeric, with offset and !"],
+];
+
+const instance = new Temporal.TimeZone("UTC");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.getPlainDateTimeFor(arg);
+
+ TemporalHelpers.assertPlainDateTime(
+ result,
+ 1970, 1, "M01", 1, 0, 0, 0, 0, 0, 0,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..bf73224e68
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-string-unknown-annotation.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.timezone.prototype.getplaindatetimefor
+description: Various forms of unknown annotation
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["1970-01-01T00:00Z[foo=bar]", "alone"],
+ ["1970-01-01T00:00Z[UTC][foo=bar]", "with time zone"],
+ ["1970-01-01T00:00Z[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["1970-01-01T00:00Z[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["1970-01-01T00:00Z[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+const instance = new Temporal.TimeZone("UTC");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.getPlainDateTimeFor(arg);
+
+ TemporalHelpers.assertPlainDateTime(
+ result,
+ 1970, 1, "M01", 1, 0, 0, 0, 0, 0, 0,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-wrong-type.js
new file mode 100644
index 0000000000..b6c9d6ccb0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-wrong-type.js
@@ -0,0 +1,46 @@
+// |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.timezone.prototype.getplaindatetimefor
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ for Instant
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.TimeZone("UTC");
+
+const primitiveTests = [
+ [undefined, "undefined"],
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [19761118, "number that would convert to a valid ISO string in other contexts"],
+ [1n, "bigint"],
+ [{}, "plain object"],
+ [Temporal.Instant, "Temporal.Instant, object"],
+];
+
+for (const [arg, description] of primitiveTests) {
+ assert.throws(
+ typeof arg === "string" || (typeof arg === "object" && arg !== null) || typeof arg === "function"
+ ? RangeError
+ : TypeError,
+ () => instance.getPlainDateTimeFor(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [Temporal.Instant.prototype, "Temporal.Instant.prototype, object"], // fails brand check in toString()
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.getPlainDateTimeFor(arg), `${description} does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-zoneddatetime.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-zoneddatetime.js
new file mode 100644
index 0000000000..b25c280c9d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-zoneddatetime.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.timezone.prototype.getplaindatetimefor
+description: Fast path for converting Temporal.ZonedDateTime to Temporal.Instant
+info: |
+ sec-temporal.timezone.prototype.getplaindatetimefor step 2:
+ 2. Set _instant_ to ? ToTemporalInstant(_instant_).
+ sec-temporal-totemporalinstant step 1.b:
+ b. If _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return ! CreateTemporalInstant(_item_.[[Nanoseconds]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalInstantFastPath((datetime) => {
+ const timeZone = Temporal.TimeZone.from("UTC");
+ const result = timeZone.getPlainDateTimeFor(datetime);
+ TemporalHelpers.assertPlainDateTime(result, 2001, 9, "M09", 9, 1, 46, 40, 987, 654, 321);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/balance-negative-time-units.js
new file mode 100644
index 0000000000..12587c4260
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/balance-negative-time-units.js
@@ -0,0 +1,42 @@
+// |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.timezone.prototype.getplaindatetimefor
+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-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-get-temporal.timezone.prototype.getplaindatetimefor step 4:
+ 4. Return ? BuiltinTimeZoneGetPlainDateTimeFor(_timeZone_, _instant_, _calendar_).
+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 instant = new Temporal.Instant(1001n);
+
+const pdt = tz.getPlainDateTimeFor(instant);
+
+TemporalHelpers.assertPlainDateTime(pdt, 1970, 1, "M01", 1, 0, 0, 0, 0, 0, 999);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/branding.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/branding.js
new file mode 100644
index 0000000000..f3f7eccc9c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/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.timezone.prototype.getplaindatetimefor
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const getPlainDateTimeFor = Temporal.TimeZone.prototype.getPlainDateTimeFor;
+
+assert.sameValue(typeof getPlainDateTimeFor, "function");
+
+const args = [new Temporal.Instant(0n)];
+
+assert.throws(TypeError, () => getPlainDateTimeFor.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => getPlainDateTimeFor.apply(null, args), "null");
+assert.throws(TypeError, () => getPlainDateTimeFor.apply(true, args), "true");
+assert.throws(TypeError, () => getPlainDateTimeFor.apply("", args), "empty string");
+assert.throws(TypeError, () => getPlainDateTimeFor.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => getPlainDateTimeFor.apply(1, args), "1");
+assert.throws(TypeError, () => getPlainDateTimeFor.apply({}, args), "plain object");
+assert.throws(TypeError, () => getPlainDateTimeFor.apply(Temporal.TimeZone, args), "Temporal.TimeZone");
+assert.throws(TypeError, () => getPlainDateTimeFor.apply(Temporal.TimeZone.prototype, args), "Temporal.TimeZone.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/browser.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/builtin.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/builtin.js
new file mode 100644
index 0000000000..efcbeaf959
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/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.timezone.prototype.getplaindatetimefor
+description: >
+ Tests that Temporal.TimeZone.prototype.getPlainDateTimeFor
+ 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.TimeZone.prototype.getPlainDateTimeFor),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.TimeZone.prototype.getPlainDateTimeFor),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.TimeZone.prototype.getPlainDateTimeFor),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.TimeZone.prototype.getPlainDateTimeFor.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/calendar-case-insensitive.js
new file mode 100644
index 0000000000..fdfa4a39ba
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/calendar-case-insensitive.js
@@ -0,0 +1,17 @@
+// |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.timezone.prototype.getplaindatetimefor
+description: Calendar names are case-insensitive
+features: [Temporal]
+---*/
+
+const instance = new Temporal.TimeZone("UTC");
+
+const arg = "iSo8601";
+const result = instance.getPlainDateTimeFor(new Temporal.Instant(0n), arg);
+assert.sameValue(result.calendarId, "iso8601", "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/calendar-number.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/calendar-number.js
new file mode 100644
index 0000000000..0cf2bc2a3b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/calendar-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.timezone.prototype.getplaindatetimefor
+description: A number is not allowed to be a calendar
+features: [Temporal]
+---*/
+
+const instance = new Temporal.TimeZone("UTC");
+
+const numbers = [
+ 1,
+ -19761118,
+ 19761118,
+ 1234567890,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => instance.getPlainDateTimeFor(new Temporal.Instant(0n), arg),
+ "A number is not a valid ISO string for Calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/calendar-string-leap-second.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/calendar-string-leap-second.js
new file mode 100644
index 0000000000..9b3aeeb9d7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/calendar-string-leap-second.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.timezone.prototype.getplaindatetimefor
+description: Leap second is a valid ISO string for Calendar
+features: [Temporal]
+---*/
+
+const instance = new Temporal.TimeZone("UTC");
+
+const arg = "2016-12-31T23:59:60";
+const result = instance.getPlainDateTimeFor(new Temporal.Instant(0n), arg);
+assert.sameValue(
+ result.calendarId,
+ "iso8601",
+ "leap second is a valid ISO string for Calendar"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/calendar-string.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/calendar-string.js
new file mode 100644
index 0000000000..eb154757a6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/calendar-string.js
@@ -0,0 +1,18 @@
+// |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.timezone.prototype.getplaindatetimefor
+description: A calendar ID is valid input for Calendar
+features: [Temporal]
+---*/
+
+const instance = new Temporal.TimeZone("UTC");
+
+const arg = "iso8601";
+
+const result = instance.getPlainDateTimeFor(new Temporal.Instant(0n), arg);
+assert.sameValue(result.getISOFields().calendar, "iso8601", `Calendar created from string "${arg}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/calendar-temporal-object.js
new file mode 100644
index 0000000000..295ea57cfb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/calendar-temporal-object.js
@@ -0,0 +1,42 @@
+// |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.timezone.prototype.getplaindatetimefor
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal-totemporalcalendar step 1.b:
+ b. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const plainDate = new Temporal.PlainDate(2000, 5, 2);
+const plainDateTime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const plainMonthDay = new Temporal.PlainMonthDay(5, 2);
+const plainYearMonth = new Temporal.PlainYearMonth(2000, 5);
+const zonedDateTime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+
+[plainDate, plainDateTime, plainMonthDay, plainYearMonth, zonedDateTime].forEach((arg) => {
+ const actual = [];
+ const expected = [];
+
+ const calendar = arg.getISOFields().calendar;
+
+ Object.defineProperty(arg, "calendar", {
+ get() {
+ actual.push("get calendar");
+ return calendar;
+ },
+ });
+
+ const instance = new Temporal.TimeZone("UTC");
+ const result = instance.getPlainDateTimeFor(new Temporal.Instant(0n), arg);
+ assert.sameValue(result.getISOFields().calendar, calendar, "Temporal object coerced to calendar");
+
+ assert.compareArray(actual, expected, "calendar getter not called");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/calendar-undefined.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/calendar-undefined.js
new file mode 100644
index 0000000000..6041c710d6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/calendar-undefined.js
@@ -0,0 +1,26 @@
+// |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.timezone.prototype.getplaindatetimefor
+description: Calendar argument defaults to the built-in ISO 8601 calendar
+features: [Temporal]
+---*/
+
+const instant = Temporal.Instant.from("1975-02-02T14:25:36.123456789Z");
+const timeZone = Temporal.TimeZone.from("UTC");
+
+Object.defineProperty(Temporal.Calendar, "from", {
+ get() {
+ throw new Test262Error("Should not call Calendar.from");
+ },
+});
+
+const result1 = timeZone.getPlainDateTimeFor(instant);
+assert.sameValue(result1.calendarId, "iso8601");
+
+const result2 = timeZone.getPlainDateTimeFor(instant, undefined);
+assert.sameValue(result2.calendarId, "iso8601");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/calendar-wrong-type.js
new file mode 100644
index 0000000000..9998c731b9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/calendar-wrong-type.js
@@ -0,0 +1,42 @@
+// |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.timezone.prototype.getplaindatetimefor
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or object for Calendar
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.TimeZone("UTC");
+
+const primitiveTests = [
+ [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.getPlainDateTimeFor(new Temporal.Instant(0n), arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.getPlainDateTimeFor(new Temporal.Instant(0n), arg), `${description} is not a valid object and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/custom-timezone.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/custom-timezone.js
new file mode 100644
index 0000000000..b7844462a5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/custom-timezone.js
@@ -0,0 +1,30 @@
+// |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.timezone.prototype.getplaindatetimefor
+description: getOffsetNanosecondsFor is called by getPlainDateTimeFor
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const expected = [
+ "get getOffsetNanosecondsFor",
+ "call timeZone.getOffsetNanosecondsFor",
+];
+
+const instant = Temporal.Instant.from("1975-02-02T14:25:36.123456789Z");
+const timeZone = new Temporal.TimeZone("UTC");
+TemporalHelpers.observeProperty(actual, timeZone, "getOffsetNanosecondsFor", function (instantArg) {
+ actual.push("call timeZone.getOffsetNanosecondsFor");
+ assert.sameValue(instantArg, instant);
+ return 9876543210123;
+});
+
+const result = timeZone.getPlainDateTimeFor(instant);
+TemporalHelpers.assertPlainDateTime(result, 1975, 2, "M02", 2, 17, 10, 12, 666, 666, 912);
+assert.compareArray(actual, expected);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/instant-string-limits.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/instant-string-limits.js
new file mode 100644
index 0000000000..e57fef37f1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/instant-string-limits.js
@@ -0,0 +1,48 @@
+// |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.timezone.prototype.getplaindatetimefor
+description: String arguments at the limit of the representable range
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.TimeZone("UTC");
+
+const minInstantStrings = [
+ "-271821-04-20T00:00Z",
+ "-271821-04-19T23:00-01:00",
+ "-271821-04-19T00:00:00.000000001-23:59:59.999999999",
+];
+for (const str of minInstantStrings) {
+ TemporalHelpers.assertPlainDateTime(instance.getPlainDateTimeFor(str), -271821, 4, "M04", 20, 0, 0, 0, 0, 0, 0, `instant string ${str} should be valid`);
+}
+
+const maxInstantStrings = [
+ "+275760-09-13T00:00Z",
+ "+275760-09-13T01:00+01:00",
+ "+275760-09-13T23:59:59.999999999+23:59:59.999999999",
+];
+
+for (const str of maxInstantStrings) {
+ TemporalHelpers.assertPlainDateTime(instance.getPlainDateTimeFor(str), 275760, 9, "M09", 13, 0, 0, 0, 0, 0, 0, `instant string ${str} should be valid`);
+}
+
+const outOfRangeInstantStrings = [
+ "-271821-04-19T23:59:59.999999999Z",
+ "-271821-04-19T23:00-00:59:59.999999999",
+ "-271821-04-19T00:00:00-23:59:59.999999999",
+ "-271821-04-19T00:00:00-24:00",
+ "+275760-09-13T00:00:00.000000001Z",
+ "+275760-09-13T01:00+00:59:59.999999999",
+ "+275760-09-14T00:00+23:59:59.999999999",
+ "+275760-09-14T00:00+24:00",
+];
+
+for (const str of outOfRangeInstantStrings) {
+ assert.throws(RangeError, () => instance.getPlainDateTimeFor(str), `instant string ${str} should not be valid`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/instant-string-multiple-offsets.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/instant-string-multiple-offsets.js
new file mode 100644
index 0000000000..b4a9db98e0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/instant-string-multiple-offsets.js
@@ -0,0 +1,18 @@
+// |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.timezone.prototype.getplaindatetimefor
+description: Instant strings with UTC offset fractional part are not confused with time fractional part
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.TimeZone("UTC");
+const str = "1970-01-01T00:02:00.000000000+00:02[+01:30]";
+
+const result = instance.getPlainDateTimeFor(str);
+TemporalHelpers.assertPlainDateTime(result, 1970, 1, "M01", 1, 0, 0, 0, 0, 0, 0, "UTC offset determined from offset part of string");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/instant-string-sub-minute-offset.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/instant-string-sub-minute-offset.js
new file mode 100644
index 0000000000..3e1cf18982
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/instant-string-sub-minute-offset.js
@@ -0,0 +1,68 @@
+// |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.timezone.prototype.getplaindatetimefor
+description: Temporal.Instant string with sub-minute offset
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.TimeZone("UTC");
+
+const str = "1970-01-01T00:19:32.37+00:19:32.37";
+const result = instance.getPlainDateTimeFor(str);
+TemporalHelpers.assertPlainDateTime(result, 1970, 1, "M01", 1, 0, 0, 0, 0, 0, 0, "if present, sub-minute offset is accepted exactly");
+
+[
+ "2021-08-19T17:30-07:00:01[-07:00:01]",
+ "2021-08-19T17:30-07:00:00[-07:00:00]",
+ "2021-08-19T17:30-07:00:00.1[-07:00:00.1]",
+ "2021-08-19T17:30-07:00:00.0[-07:00:00.0]",
+ "2021-08-19T17:30-07:00:00.01[-07:00:00.01]",
+ "2021-08-19T17:30-07:00:00.00[-07:00:00.00]",
+ "2021-08-19T17:30-07:00:00.001[-07:00:00.001]",
+ "2021-08-19T17:30-07:00:00.000[-07:00:00.000]",
+ "2021-08-19T17:30-07:00:00.0001[-07:00:00.0001]",
+ "2021-08-19T17:30-07:00:00.0000[-07:00:00.0000]",
+ "2021-08-19T17:30-07:00:00.00001[-07:00:00.00001]",
+ "2021-08-19T17:30-07:00:00.00000[-07:00:00.00000]",
+ "2021-08-19T17:30-07:00:00.000001[-07:00:00.000001]",
+ "2021-08-19T17:30-07:00:00.000000[-07:00:00.000000]",
+ "2021-08-19T17:30-07:00:00.0000001[-07:00:00.0000001]",
+ "2021-08-19T17:30-07:00:00.0000000[-07:00:00.0000000]",
+ "2021-08-19T17:30-07:00:00.00000001[-07:00:00.00000001]",
+ "2021-08-19T17:30-07:00:00.00000000[-07:00:00.00000000]",
+ "2021-08-19T17:30-07:00:00.000000001[-07:00:00.000000001]",
+ "2021-08-19T17:30-07:00:00.000000000[-07:00:00.000000000]",
+
+ "2021-08-19T17:30-07:00:01[-070001]",
+ "2021-08-19T17:30-07:00:00[-070000]",
+ "2021-08-19T17:30-07:00:00.1[-070000.1]",
+ "2021-08-19T17:30-07:00:00.0[-070000.0]",
+ "2021-08-19T17:30-07:00:00.01[-070000.01]",
+ "2021-08-19T17:30-07:00:00.00[-070000.00]",
+ "2021-08-19T17:30-07:00:00.001[-070000.001]",
+ "2021-08-19T17:30-07:00:00.000[-070000.000]",
+ "2021-08-19T17:30-07:00:00.0001[-070000.0001]",
+ "2021-08-19T17:30-07:00:00.0000[-070000.0000]",
+ "2021-08-19T17:30-07:00:00.00001[-070000.00001]",
+ "2021-08-19T17:30-07:00:00.00000[-070000.00000]",
+ "2021-08-19T17:30-07:00:00.000001[-070000.000001]",
+ "2021-08-19T17:30-07:00:00.000000[-070000.000000]",
+ "2021-08-19T17:30-07:00:00.0000001[-070000.0000001]",
+ "2021-08-19T17:30-07:00:00.0000000[-070000.0000000]",
+ "2021-08-19T17:30-07:00:00.00000001[-070000.00000001]",
+ "2021-08-19T17:30-07:00:00.00000000[-070000.00000000]",
+ "2021-08-19T17:30-07:00:00.000000001[-070000.000000001]",
+ "2021-08-19T17:30-07:00:00.000000000[-070000.000000000]"
+].forEach((str) => {
+ assert.throws(
+ RangeError,
+ () => instance.getPlainDateTimeFor(str),
+ `ISO strings cannot have sub-minute offsets in time zone annotations: ${str}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/instant-string.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/instant-string.js
new file mode 100644
index 0000000000..3f1abf2108
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/instant-string.js
@@ -0,0 +1,47 @@
+// |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.timezone.prototype.getplaindatetimefor
+description: Conversion of ISO date-time strings to Temporal.Instant instances
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.TimeZone("UTC");
+
+let str = "1970-01-01T00:00";
+assert.throws(RangeError, () => instance.getPlainDateTimeFor(str), "bare date-time string is not an instant");
+str = "1970-01-01T00:00[UTC]";
+assert.throws(RangeError, () => instance.getPlainDateTimeFor(str), "date-time + IANA annotation is not an instant");
+
+str = "1970-01-01T00:00Z";
+const result1 = instance.getPlainDateTimeFor(str);
+TemporalHelpers.assertPlainDateTime(result1, 1970, 1, "M01", 1, 0, 0, 0, 0, 0, 0, "date-time + Z preserves exact time");
+
+str = "1970-01-01T00:00+01:00";
+const result2 = instance.getPlainDateTimeFor(str);
+TemporalHelpers.assertPlainDateTime(result2, 1969, 12, "M12", 31, 23, 0, 0, 0, 0, 0, "date-time + offset preserves exact time with offset");
+
+str = "1970-01-01T00:00Z[Etc/Ignored]";
+const result3 = instance.getPlainDateTimeFor(str);
+TemporalHelpers.assertPlainDateTime(result3, 1970, 1, "M01", 1, 0, 0, 0, 0, 0, 0, "date-time + Z + IANA annotation ignores the IANA annotation");
+
+str = "1970-01-01T00:00+01:00[Etc/Ignored]";
+const result4 = instance.getPlainDateTimeFor(str);
+TemporalHelpers.assertPlainDateTime(result4, 1969, 12, "M12", 31, 23, 0, 0, 0, 0, 0, "date-time + offset + IANA annotation ignores the IANA annotation");
+
+str = "1970-01-01T00:00Z[u-ca=hebrew]";
+const result6 = instance.getPlainDateTimeFor(str);
+TemporalHelpers.assertPlainDateTime(result6, 1970, 1, "M01", 1, 0, 0, 0, 0, 0, 0, "date-time + Z + Calendar ignores the Calendar");
+
+str = "1970-01-01T00:00+01:00[u-ca=hebrew]";
+const result5 = instance.getPlainDateTimeFor(str);
+TemporalHelpers.assertPlainDateTime(result5, 1969, 12, "M12", 31, 23, 0, 0, 0, 0, 0, "date-time + offset + Calendar ignores the Calendar");
+
+str = "1970-01-01T00:00+01:00[Etc/Ignored][u-ca=hebrew]";
+const result7 = instance.getPlainDateTimeFor(str);
+TemporalHelpers.assertPlainDateTime(result7, 1969, 12, "M12", 31, 23, 0, 0, 0, 0, 0, "date-time + offset + IANA annotation + Calendar ignores the Calendar and IANA annotation");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/leap-second.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/leap-second.js
new file mode 100644
index 0000000000..446e96c161
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/leap-second.js
@@ -0,0 +1,22 @@
+// |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.timezone.prototype.getplaindatetimefor
+description: Leap second is a valid ISO string for Instant
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.TimeZone("UTC");
+
+const arg = "2016-12-31T23:59:60Z";
+const result = instance.getPlainDateTimeFor(arg);
+TemporalHelpers.assertPlainDateTime(
+ result,
+ 2016, 12, "M12", 31, 23, 59, 59, 0, 0, 0,
+ "leap second is a valid ISO string for Instant"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/length.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/length.js
new file mode 100644
index 0000000000..b3021c4bc7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/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.timezone.prototype.getplaindatetimefor
+description: Temporal.TimeZone.prototype.getPlainDateTimeFor.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.TimeZone.prototype.getPlainDateTimeFor, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/limits.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/limits.js
new file mode 100644
index 0000000000..f4cf0af7af
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/limits.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.plaindatetime.prototype.getplaindatetimefor
+description: Checking limits of representable PlainDateTime
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const min = new Temporal.Instant(-8_640_000_000_000_000_000_000n);
+const offsetMin = new Temporal.TimeZone("-23:59");
+const max = new Temporal.Instant(8_640_000_000_000_000_000_000n);
+const offsetMax = new Temporal.TimeZone("+23:59");
+
+TemporalHelpers.assertPlainDateTime(
+ offsetMin.getPlainDateTimeFor(min, "iso8601"),
+ -271821, 4, "M04", 19, 0, 1, 0, 0, 0, 0,
+ "converting from Instant (negative case)"
+);
+
+TemporalHelpers.assertPlainDateTime(
+ offsetMax.getPlainDateTimeFor(max, "iso8601"),
+ 275760, 9, "M09", 13, 23, 59, 0, 0, 0, 0,
+ "converting from Instant (positive case)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/name.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/name.js
new file mode 100644
index 0000000000..abf3dbe263
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/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.timezone.prototype.getplaindatetimefor
+description: Temporal.TimeZone.prototype.getPlainDateTimeFor.name is "getPlainDateTimeFor".
+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.TimeZone.prototype.getPlainDateTimeFor, "name", {
+ value: "getPlainDateTimeFor",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/not-a-constructor.js
new file mode 100644
index 0000000000..2d75ed6e0f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/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.timezone.prototype.getplaindatetimefor
+description: >
+ Temporal.TimeZone.prototype.getPlainDateTimeFor 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.TimeZone.prototype.getPlainDateTimeFor();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.TimeZone.prototype.getPlainDateTimeFor), false,
+ "isConstructor(Temporal.TimeZone.prototype.getPlainDateTimeFor)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/pre-epoch.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/pre-epoch.js
new file mode 100644
index 0000000000..97cd406175
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/pre-epoch.js
@@ -0,0 +1,18 @@
+// |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.timezone.prototype.getplaindatetimefor
+description: Test of basic functionality for an exact time earlier than the Unix epoch
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instant = Temporal.Instant.from("1969-07-16T13:32:01.234567891Z");
+assert.sameValue(instant.toString(), "1969-07-16T13:32:01.234567891Z");
+const timeZone = Temporal.TimeZone.from("-04:00");
+const dateTime = timeZone.getPlainDateTimeFor(instant);
+TemporalHelpers.assertPlainDateTime(dateTime, 1969, 7, "M07", 16, 9, 32, 1, 234, 567, 891);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/prop-desc.js
new file mode 100644
index 0000000000..b7ec44a682
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/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.timezone.prototype.getplaindatetimefor
+description: The "getPlainDateTimeFor" property of Temporal.TimeZone.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.TimeZone.prototype.getPlainDateTimeFor,
+ "function",
+ "`typeof TimeZone.prototype.getPlainDateTimeFor` is `function`"
+);
+
+verifyProperty(Temporal.TimeZone.prototype, "getPlainDateTimeFor", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/shell.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/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/TimeZone/prototype/getPlainDateTimeFor/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..29518fb241
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,18 @@
+// |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.timezone.prototype.getplaindatetimefor
+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 instant = new Temporal.Instant(1_000_000_000_987_654_321n);
+ assert.throws(RangeError, () => timeZone.getPlainDateTimeFor(instant));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..f3608d2f51
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/timezone-getoffsetnanosecondsfor-not-callable.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.timezone.prototype.getplaindatetimefor
+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 instant = new Temporal.Instant(1_000_000_000_987_654_321n);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => timeZone.getPlainDateTimeFor(instant),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..56ce2f31e6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,18 @@
+// |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.timezone.prototype.getplaindatetimefor
+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 instant = new Temporal.Instant(1_000_000_000_987_654_321n);
+ assert.throws(RangeError, () => timeZone.getPlainDateTimeFor(instant));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..1976e19cd0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/timezone-getoffsetnanosecondsfor-wrong-type.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.timezone.prototype.getplaindatetimefor
+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 instant = new Temporal.Instant(1_000_000_000_987_654_321n);
+ assert.throws(TypeError, () => timeZone.getPlainDateTimeFor(instant));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/year-zero.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/year-zero.js
new file mode 100644
index 0000000000..56c08eb631
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/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.timezone.prototype.getplaindatetimefor
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-03-30T00:45Z",
+ "-000000-03-30T01:45+01:00",
+ "-000000-03-30T01:45:00+00:00[UTC]",
+];
+const instance = new Temporal.TimeZone("UTC");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.getPlainDateTimeFor(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..13b01796cb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-builtin-calendar-no-array-iteration.js
@@ -0,0 +1,24 @@
+// |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.timezone.prototype.getpossibleinstantsfor
+description: >
+ Calling the method with a property bag argument with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.TimeZone("UTC");
+const arg = { year: 2000, month: 5, day: 2, hour: 21, minute: 43, second: 5, calendar: "iso8601" };
+instance.getPossibleInstantsFor(arg);
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-not-datetime.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-not-datetime.js
new file mode 100644
index 0000000000..6886ee6617
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-not-datetime.js
@@ -0,0 +1,21 @@
+// |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.timezone.prototype.getpossibleinstantsfor
+description: Appropriate error thrown when argument cannot be converted to Temporal.PlainDateTime
+features: [Temporal]
+---*/
+
+const timeZone = Temporal.TimeZone.from("UTC");
+assert.throws(TypeError, () => timeZone.getPossibleInstantsFor(undefined), "undefined");
+assert.throws(TypeError, () => timeZone.getPossibleInstantsFor(null), "null");
+assert.throws(TypeError, () => timeZone.getPossibleInstantsFor(true), "boolean");
+assert.throws(RangeError, () => timeZone.getPossibleInstantsFor(""), "empty string");
+assert.throws(TypeError, () => timeZone.getPossibleInstantsFor(Symbol()), "Symbol");
+assert.throws(TypeError, () => timeZone.getPossibleInstantsFor(5), "number");
+assert.throws(TypeError, () => timeZone.getPossibleInstantsFor(5n), "bigint");
+assert.throws(TypeError, () => timeZone.getPossibleInstantsFor({}), "plain object");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-number.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-number.js
new file mode 100644
index 0000000000..a0002e932b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/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.timezone.prototype.getpossibleinstantsfor
+description: A number cannot be used in place of a Temporal.PlainDateTime
+features: [Temporal]
+---*/
+
+const instance = new Temporal.TimeZone("UTC");
+
+const numbers = [
+ 1,
+ 19761118,
+ -19761118,
+ 1234567890,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => instance.getPossibleInstantsFor(arg),
+ `A number (${arg}) is not a valid ISO string for PlainDateTime`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-plaindate.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-plaindate.js
new file mode 100644
index 0000000000..967fdf7454
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-plaindate.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.plaindatetime.prototype.until
+description: Fast path for converting Temporal.PlainDate to Temporal.PlainDateTime by reading internal slots
+info: |
+ sec-temporal.timezone.prototype.getpossibleinstantsfor step 3:
+ 3. Set _dateTime_ to ? ToTemporalDateTime(_dateTime_).
+ sec-temporal-totemporaldatetime step 2.b:
+ b. If _item_ has an [[InitializedTemporalDate]] internal slot, then
+ i. Return ? CreateTemporalDateTime(_item_.[[ISOYear]], _item_.[[ISOMonth]], _item_.[[ISODay]], 0, 0, 0, 0, 0, 0, _item_.[[Calendar]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalPlainDateTimeFastPath((date) => {
+ const timezone = new Temporal.TimeZone("UTC");
+ const result = timezone.getPossibleInstantsFor(date);
+ assert.sameValue(result.length, 1, "one possible instant");
+ assert.sameValue(result[0].epochNanoseconds, 957_225_600_000_000_000n, "epochNanoseconds result");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-propertybag-calendar-case-insensitive.js
new file mode 100644
index 0000000000..b83394a8e0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-propertybag-calendar-case-insensitive.js
@@ -0,0 +1,20 @@
+// |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.timezone.prototype.getpossibleinstantsfor
+description: The calendar name is case-insensitive
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.TimeZone("UTC");
+
+const calendar = "IsO8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.getPossibleInstantsFor(arg);
+assert.compareArray(result.map(i => i.epochNanoseconds), [217_123_200_000_000_000n], "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-propertybag-calendar-leap-second.js
new file mode 100644
index 0000000000..9c4175f5da
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-propertybag-calendar-leap-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.timezone.prototype.getpossibleinstantsfor
+description: Leap second is a valid ISO string for a calendar in a property bag
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.TimeZone("UTC");
+
+const calendar = "2016-12-31T23:59:60";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.getPossibleInstantsFor(arg);
+assert.compareArray(
+ result.map(i => i.epochNanoseconds),
+ [217_123_200_000_000_000n],
+ "leap second is a valid ISO string for calendar"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-propertybag-calendar-number.js
new file mode 100644
index 0000000000..c677cdf355
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-propertybag-calendar-number.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.timezone.prototype.getpossibleinstantsfor
+description: A number as calendar in a property bag is not accepted
+features: [Temporal]
+---*/
+
+const instance = new Temporal.TimeZone("UTC");
+
+const numbers = [
+ 1,
+ 19970327,
+ -19970327,
+ 1234567890,
+];
+
+for (const calendar of numbers) {
+ const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+ assert.throws(
+ TypeError,
+ () => instance.getPossibleInstantsFor(arg),
+ "Numbers cannot be used as a calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-propertybag-calendar-string.js
new file mode 100644
index 0000000000..bcccdb578c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-propertybag-calendar-string.js
@@ -0,0 +1,20 @@
+// |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.timezone.prototype.getpossibleinstantsfor
+description: A calendar ID is valid input for Calendar
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.TimeZone("UTC");
+
+const calendar = "iso8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.getPossibleInstantsFor(arg);
+assert.compareArray(result.map(i => i.epochNanoseconds), [217_123_200_000_000_000n], `Calendar created from string "${calendar}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-propertybag-calendar-wrong-type.js
new file mode 100644
index 0000000000..9bb67c587e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-propertybag-calendar-wrong-type.js
@@ -0,0 +1,46 @@
+// |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.timezone.prototype.getpossibleinstantsfor
+description: >
+ Appropriate error thrown when a calendar property from a property bag cannot
+ be converted to a calendar object or string
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.TimeZone("UTC");
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [calendar, description] of primitiveTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ typeof calendar === 'string' ? RangeError : TypeError,
+ () => instance.getPossibleInstantsFor(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+ [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields()
+];
+
+for (const [calendar, description] of typeErrorTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(TypeError, () => instance.getPossibleInstantsFor(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/TimeZone/prototype/getPossibleInstantsFor/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-propertybag-calendar-year-zero.js
new file mode 100644
index 0000000000..0afcf6f978
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-propertybag-calendar-year-zero.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.timezone.prototype.getpossibleinstantsfor
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T17:45",
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+01:00",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+const instance = new Temporal.TimeZone("UTC");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.getPossibleInstantsFor(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..ccc8eb6447
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-string-calendar-annotation.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.timezone.prototype.getpossibleinstantsfor
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+includes: [compareArray.js]
+---*/
+
+const tests = [
+ ["1976-11-18T15:23[u-ca=iso8601]", "without time zone"],
+ ["1976-11-18T15:23[UTC][u-ca=iso8601]", "with time zone"],
+ ["1976-11-18T15:23[!u-ca=iso8601]", "with ! and no time zone"],
+ ["1976-11-18T15:23[UTC][!u-ca=iso8601]", "with ! and time zone"],
+ ["1976-11-18T15:23[u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+const instance = new Temporal.TimeZone("UTC");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.getPossibleInstantsFor(arg);
+
+ assert.compareArray(
+ result.map(i => i.epochNanoseconds),
+ [217_178_580_000_000_000n],
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..d0d4f3bb3d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-string-critical-unknown-annotation.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.timezone.prototype.getpossibleinstantsfor
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "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.TimeZone("UTC");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.getPossibleInstantsFor(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..a6a045e438
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-string-date-with-utc-offset.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.timezone.prototype.getpossibleinstantsfor
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+includes: [compareArray.js]
+---*/
+
+const instance = new Temporal.TimeZone("UTC");
+
+const validStrings = [
+ "1976-11-18T15:23+00:00",
+ "1976-11-18T15:23+00:00[UTC]",
+ "1976-11-18T15:23+00:00[!UTC]",
+ "1976-11-18T15:23-02:30[America/St_Johns]",
+];
+
+for (const arg of validStrings) {
+ const result = instance.getPossibleInstantsFor(arg);
+
+ assert.compareArray(
+ result.map(i => i.epochNanoseconds),
+ [217_178_580_000_000_000n],
+ `"${arg}" is a valid UTC offset with time for PlainDateTime`
+ );
+}
+
+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.getPossibleInstantsFor(arg),
+ `"${arg}" UTC offset without time is not valid for PlainDateTime`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..b29552e4c7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/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.timezone.prototype.getpossibleinstantsfor
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[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.TimeZone("UTC");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.getPossibleInstantsFor(arg),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..8d8944fe22
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-string-multiple-time-zone.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.timezone.prototype.getpossibleinstantsfor
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "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.TimeZone("UTC");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.getPossibleInstantsFor(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-string-time-separators.js
new file mode 100644
index 0000000000..d25d8923d1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-string-time-separators.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.timezone.prototype.getpossibleinstantsfor
+description: Time separator in string argument can vary
+features: [Temporal]
+includes: [compareArray.js]
+---*/
+
+const tests = [
+ ["1976-11-18T15:23", "uppercase T"],
+ ["1976-11-18t15:23", "lowercase T"],
+ ["1976-11-18 15:23", "space between date and time"],
+];
+
+const instance = new Temporal.TimeZone("UTC");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.getPossibleInstantsFor(arg);
+
+ assert.compareArray(
+ result.map(i => i.epochNanoseconds),
+ [217_178_580_000_000_000n],
+ `variant time separators (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..eba484f410
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-string-time-zone-annotation.js
@@ -0,0 +1,35 @@
+// |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.timezone.prototype.getpossibleinstantsfor
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+includes: [compareArray.js]
+---*/
+
+const tests = [
+ ["1976-11-18T15:23[Asia/Kolkata]", "named, with no offset"],
+ ["1976-11-18T15:23[!Europe/Vienna]", "named, with ! and no offset"],
+ ["1976-11-18T15:23[+00:00]", "numeric, with no offset"],
+ ["1976-11-18T15:23[!-02:30]", "numeric, with ! and no offset"],
+ ["1976-11-18T15:23+00:00[UTC]", "named, with offset"],
+ ["1976-11-18T15:23+00:00[!Africa/Abidjan]", "named, with offset and !"],
+ ["1976-11-18T15:23+00:00[+01:00]", "numeric, with offset"],
+ ["1976-11-18T15:23+00:00[!-08:00]", "numeric, with offset and !"],
+];
+
+const instance = new Temporal.TimeZone("UTC");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.getPossibleInstantsFor(arg);
+
+ assert.compareArray(
+ result.map(i => i.epochNanoseconds),
+ [217_178_580_000_000_000n],
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..df1811ea08
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-string-unknown-annotation.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.timezone.prototype.getpossibleinstantsfor
+description: Various forms of unknown annotation
+features: [Temporal]
+includes: [compareArray.js]
+---*/
+
+const tests = [
+ ["1976-11-18T15:23[foo=bar]", "alone"],
+ ["1976-11-18T15:23[UTC][foo=bar]", "with time zone"],
+ ["1976-11-18T15:23[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["1976-11-18T15:23[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["1976-11-18T15:23[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+const instance = new Temporal.TimeZone("UTC");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.getPossibleInstantsFor(arg);
+
+ assert.compareArray(
+ result.map(i => i.epochNanoseconds),
+ [217_178_580_000_000_000n],
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..11ee17ba4e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-string-with-utc-designator.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.timezone.prototype.getpossibleinstantsfor
+description: RangeError thrown if a string with UTC designator is used as a PlainDateTime
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "2019-10-01T09:00:00Z",
+ "2019-10-01T09:00:00Z[UTC]",
+];
+const instance = new Temporal.TimeZone("UTC");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.getPossibleInstantsFor(arg),
+ "String with UTC designator should not be valid as a PlainDateTime"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-wrong-type.js
new file mode 100644
index 0000000000..697d95c314
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/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.timezone.prototype.getpossibleinstantsfor
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainDateTime
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.TimeZone("UTC");
+
+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.getPossibleInstantsFor(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.PlainDateTime, "Temporal.PlainDateTime, object"],
+ [Temporal.PlainDateTime.prototype, "Temporal.PlainDateTime.prototype, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.getPossibleInstantsFor(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/TimeZone/prototype/getPossibleInstantsFor/argument-zoneddatetime-balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-zoneddatetime-balance-negative-time-units.js
new file mode 100644
index 0000000000..62e3cd227c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-zoneddatetime-balance-negative-time-units.js
@@ -0,0 +1,48 @@
+// |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.timezone.prototype.getpossibleinstantsfor
+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-totemporaldatetime step 3.b:
+ b. If _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ ...
+ ii. 1. Return ? BuiltinTimeZoneGetPlainDateTimeFor(_item_.[[TimeZone]], _instant_, _item_.[[Calendar]]).
+ sec-temporal.timezone.prototype.getpossibleinstantsfor step 3:
+ 3. Set _dateTime_ ? ToTemporalDateTime(_dateTime_).
+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 conversionTimeZone = new Temporal.TimeZone("UTC"); // should not be used to interpret the argument
+const instants = conversionTimeZone.getPossibleInstantsFor(datetime);
+
+assert.sameValue(instants.length, 1);
+assert.sameValue(instants[0].epochNanoseconds, 3661_001_000_999n);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-zoneddatetime-negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-zoneddatetime-negative-epochnanoseconds.js
new file mode 100644
index 0000000000..765b0c7115
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/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.timezone.prototype.getpossibleinstantsfor
+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: [compareArray.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.TimeZone("UTC");
+const result = instance.getPossibleInstantsFor(datetime);
+assert.compareArray(result.map((i) => i.epochNanoseconds), [-13849764_999_999_999n]);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..10dabb1e8e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/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.timezone.prototype.getpossibleinstantsfor
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const builtinTimeZone = new Temporal.TimeZone("UTC");
+ assert.throws(RangeError, () => builtinTimeZone.getPossibleInstantsFor(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..3326adae54
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/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.timezone.prototype.getpossibleinstantsfor
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const builtinTimeZone = new Temporal.TimeZone("UTC");
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => builtinTimeZone.getPossibleInstantsFor(datetime),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..e84e4bb835
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/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.timezone.prototype.getpossibleinstantsfor
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const builtinTimeZone = new Temporal.TimeZone("UTC");
+ assert.throws(RangeError, () => builtinTimeZone.getPossibleInstantsFor(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..b892868072
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/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.timezone.prototype.getpossibleinstantsfor
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const builtinTimeZone = new Temporal.TimeZone("UTC");
+ assert.throws(TypeError, () => builtinTimeZone.getPossibleInstantsFor(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/branding.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/branding.js
new file mode 100644
index 0000000000..28112ebb0b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/branding.js
@@ -0,0 +1,27 @@
+// |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.timezone.prototype.getpossibleinstantsfor
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const getPossibleInstantsFor = Temporal.TimeZone.prototype.getPossibleInstantsFor;
+
+assert.sameValue(typeof getPossibleInstantsFor, "function");
+
+const args = [new Temporal.PlainDateTime(2022, 6, 22)];
+
+assert.throws(TypeError, () => getPossibleInstantsFor.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => getPossibleInstantsFor.apply(null, args), "null");
+assert.throws(TypeError, () => getPossibleInstantsFor.apply(true, args), "true");
+assert.throws(TypeError, () => getPossibleInstantsFor.apply("", args), "empty string");
+assert.throws(TypeError, () => getPossibleInstantsFor.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => getPossibleInstantsFor.apply(1, args), "1");
+assert.throws(TypeError, () => getPossibleInstantsFor.apply({}, args), "plain object");
+assert.throws(TypeError, () => getPossibleInstantsFor.apply(Temporal.TimeZone, args), "Temporal.TimeZone");
+assert.throws(TypeError, () => getPossibleInstantsFor.apply(Temporal.TimeZone.prototype, args), "Temporal.TimeZone.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/browser.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/builtin.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/builtin.js
new file mode 100644
index 0000000000..2b19200aef
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/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.timezone.prototype.getpossibleinstantsfor
+description: >
+ Tests that Temporal.TimeZone.prototype.getPossibleInstantsFor
+ 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.TimeZone.prototype.getPossibleInstantsFor),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.TimeZone.prototype.getPossibleInstantsFor),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.TimeZone.prototype.getPossibleInstantsFor),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.TimeZone.prototype.getPossibleInstantsFor.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/calendar-datefromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..97dcbe9239
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/calendar-datefromfields-called-with-null-prototype-fields.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.timezone.prototype.getpossibleinstantsfor
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.TimeZone("UTC");
+const arg = { year: 2000, month: 5, day: 2, calendar };
+instance.getPossibleInstantsFor(arg);
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/calendar-fields-iterable.js
new file mode 100644
index 0000000000..ecbfbceb69
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/calendar-fields-iterable.js
@@ -0,0 +1,34 @@
+// |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.timezone.prototype.getpossibleinstantsfor
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.timezone.prototype.getpossibleinstantsfor step 3:
+ 3. Set _dateTime_ to ? ToTemporalDateTime(_dateTime_).
+ sec-temporal-totemporaldatetime step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"hour"*, *"microsecond"*, *"millisecond"*, *"minute"*, *"month"*, *"monthCode"*, *"nanosecond"*, *"second"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+const timeZone = new Temporal.TimeZone("UTC");
+timeZone.getPossibleInstantsFor({ year: 2000, month: 5, day: 2, calendar });
+
+assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/calendar-temporal-object.js
new file mode 100644
index 0000000000..32b8613628
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/calendar-temporal-object.js
@@ -0,0 +1,29 @@
+// |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.timezone.prototype.getpossibleinstantsfor
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.timezone.prototype.getpossibleinstantsfor step 3:
+ 3. Set _dateTime_ to ? ToTemporalDateTime(_dateTime_).
+ sec-temporal-totemporaldatetime step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const timeZone = new Temporal.TimeZone("UTC");
+ timeZone.getPossibleInstantsFor({ year: 2000, month: 5, day: 2, calendar: temporalObject });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..245f90b226
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/constructor-in-calendar-fields.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.timezone.prototype.getpossibleinstantsfor
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.TimeZone("UTC");
+
+assert.throws(RangeError, () => instance.getPossibleInstantsFor(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/duplicate-calendar-fields.js
new file mode 100644
index 0000000000..30d79e324c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/duplicate-calendar-fields.js
@@ -0,0 +1,19 @@
+// |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.timezone.prototype.getpossibleinstantsfor
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['day'], ['hour'], ['microsecond'], ['millisecond'], ['minute'], ['month'], ['monthCode'], ['nanosecond'], ['second'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar };
+ const instance = new Temporal.TimeZone("UTC");
+
+ assert.throws(RangeError, () => instance.getPossibleInstantsFor(arg));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/fixed-offset-near-date-time-limits.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/fixed-offset-near-date-time-limits.js
new file mode 100644
index 0000000000..bf70291dc9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/fixed-offset-near-date-time-limits.js
@@ -0,0 +1,57 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getpossibleinstantsfor
+description: >
+ Call getPossibleInstantsFor with values near the date/time limit and a fixed offset.
+features: [Temporal, exponentiation]
+---*/
+
+const oneHour = 1n * 60n * 60n * 1000n**3n;
+
+const minDt = new Temporal.PlainDateTime(-271821, 4, 19, 1, 0, 0, 0, 0, 0);
+const minValidDt = new Temporal.PlainDateTime(-271821, 4, 20, 0, 0, 0, 0, 0, 0);
+const maxDt = new Temporal.PlainDateTime(275760, 9, 13, 0, 0, 0, 0, 0, 0);
+
+let zero = new Temporal.TimeZone("+00");
+let plusOne = new Temporal.TimeZone("+01");
+let minusOne = new Temporal.TimeZone("-01");
+
+// Try the minimum date-time.
+assert.throws(RangeError, () => zero.getPossibleInstantsFor(minDt));
+assert.throws(RangeError, () => plusOne.getPossibleInstantsFor(minDt));
+assert.throws(RangeError, () => minusOne.getPossibleInstantsFor(minDt));
+
+// Try the minimum valid date-time.
+{
+ let r = zero.getPossibleInstantsFor(minValidDt);
+ assert.sameValue(r.length, 1);
+ assert.sameValue(r[0].epochNanoseconds, -86_40000_00000_00000_00000n);
+}
+
+{
+ let r = minusOne.getPossibleInstantsFor(minValidDt);
+ assert.sameValue(r.length, 1);
+ assert.sameValue(r[0].epochNanoseconds, -86_40000_00000_00000_00000n + oneHour);
+}
+
+assert.throws(RangeError, () => plusOne.getPossibleInstantsFor(minValidDt));
+
+// Try the maximum valid date-time.
+{
+ let r = zero.getPossibleInstantsFor(maxDt);
+ assert.sameValue(r.length, 1);
+ assert.sameValue(r[0].epochNanoseconds, 86_40000_00000_00000_00000n);
+}
+
+{
+ let r = plusOne.getPossibleInstantsFor(maxDt);
+ assert.sameValue(r.length, 1);
+ assert.sameValue(r[0].epochNanoseconds, 86_40000_00000_00000_00000n - oneHour);
+}
+
+assert.throws(RangeError, () => minusOne.getPossibleInstantsFor(maxDt));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..d40ebce61e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/infinity-throws-rangeerror.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.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.timezone.prototype.getpossibleinstantsfor
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.TimeZone("UTC");
+const base = { year: 2000, month: 5, day: 2, hour: 15, minute: 30, second: 45, millisecond: 987, microsecond: 654, nanosecond: 321 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.getPossibleInstantsFor({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.getPossibleInstantsFor({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/leap-second.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/leap-second.js
new file mode 100644
index 0000000000..6b96daa289
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/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.timezone.prototype.getpossibleinstantsfor
+description: Leap second is a valid ISO string for PlainDateTime
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.TimeZone("UTC");
+
+let arg = "2016-12-31T23:59:60";
+const result1 = instance.getPossibleInstantsFor(arg);
+assert.compareArray(
+ result1.map(i => i.epochNanoseconds),
+ [1_483_228_799_000_000_000n],
+ "leap second is a valid ISO string for PlainDateTime"
+);
+
+arg = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+const result2 = instance.getPossibleInstantsFor(arg);
+assert.compareArray(
+ result2.map(i => i.epochNanoseconds),
+ [1_483_228_799_000_000_000n],
+ "second: 60 is ignored in property bag for PlainDateTime"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/length.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/length.js
new file mode 100644
index 0000000000..cc04a14788
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/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.timezone.prototype.getpossibleinstantsfor
+description: Temporal.TimeZone.prototype.getPossibleInstantsFor.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.TimeZone.prototype.getPossibleInstantsFor, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/name.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/name.js
new file mode 100644
index 0000000000..f5713a2e80
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/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.timezone.prototype.getpossibleinstantsfor
+description: Temporal.TimeZone.prototype.getPossibleInstantsFor.name is "getPossibleInstantsFor".
+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.TimeZone.prototype.getPossibleInstantsFor, "name", {
+ value: "getPossibleInstantsFor",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/not-a-constructor.js
new file mode 100644
index 0000000000..14789fdf50
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/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.timezone.prototype.getpossibleinstantsfor
+description: >
+ Temporal.TimeZone.prototype.getPossibleInstantsFor 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.TimeZone.prototype.getPossibleInstantsFor();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.TimeZone.prototype.getPossibleInstantsFor), false,
+ "isConstructor(Temporal.TimeZone.prototype.getPossibleInstantsFor)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/prop-desc.js
new file mode 100644
index 0000000000..aeb6aa976e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/prop-desc.js
@@ -0,0 +1,24 @@
+// |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.timezone.prototype.getpossibleinstantsfor
+description: The "getPossibleInstantsFor" property of Temporal.TimeZone.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.TimeZone.prototype.getPossibleInstantsFor,
+ "function",
+ "`typeof TimeZone.prototype.getPossibleInstantsFor` is `function`"
+);
+
+verifyProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/proto-in-calendar-fields.js
new file mode 100644
index 0000000000..09aeb655a2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/proto-in-calendar-fields.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.timezone.prototype.getpossibleinstantsfor
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.TimeZone("UTC");
+
+assert.throws(RangeError, () => instance.getPossibleInstantsFor(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/read-time-fields-before-datefromfields.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/read-time-fields-before-datefromfields.js
new file mode 100644
index 0000000000..e75ccfd8cf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/read-time-fields-before-datefromfields.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.timezone.prototype.getpossibleinstantsfor
+description: The time fields are read from the object before being passed to dateFromFields().
+info: |
+ sec-temporal.timezone.prototype.getpossibleinstantsfor step 3:
+ 3. Set _dateTime_ to ? ToTemporalDateTime(_dateTime_).
+ sec-temporal-totemporaldatetime step 2.e:
+ e. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+ sec-temporal-interprettemporaldatetimefields steps 1–2:
+ 1. Let _timeResult_ be ? ToTemporalTimeRecord(_fields_).
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const timezone = new Temporal.TimeZone("UTC");
+const calendar = TemporalHelpers.calendarMakeInfinityTime();
+const result = timezone.getPossibleInstantsFor({ year: 1970, month: 1, day: 1, calendar });
+
+assert.sameValue(result.length, 1, "result array length");
+assert.sameValue(result[0].epochNanoseconds, 0n, "epochNanoseconds result");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/shell.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/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/TimeZone/prototype/getPossibleInstantsFor/year-zero.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/year-zero.js
new file mode 100644
index 0000000000..fa4c15dce1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/year-zero.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.timezone.prototype.getpossibleinstantsfor
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-12-07",
+ "-000000-12-07T03:24:30",
+ "-000000-12-07T03:24:30+01:00",
+ "-000000-12-07T03:24:30+00:00[UTC]",
+];
+const instance = new Temporal.TimeZone("UTC");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.getPossibleInstantsFor(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..b8867e8b6a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/argument-string-calendar-annotation.js
@@ -0,0 +1,33 @@
+// |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.timezone.prototype.getprevioustransition
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["1970-01-01T00:00Z[u-ca=iso8601]", "without time zone"],
+ ["1970-01-01T00:00Z[UTC][u-ca=gregory]", "with time zone"],
+ ["1970-01-01T00:00Z[!u-ca=hebrew]", "with ! and no time zone"],
+ ["1970-01-01T00:00Z[UTC][!u-ca=chinese]", "with ! and time zone"],
+ ["1970-01-01T00:00Z[u-ca=discord]", "annotation is ignored"],
+ ["1970-01-01T00:00Z[!u-ca=discord]", "annotation with ! is ignored"],
+ ["1970-01-01T00:00Z[u-ca=iso8601][u-ca=discord]", "two annotations are ignored"],
+];
+
+const instance = new Temporal.TimeZone("UTC");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.getPreviousTransition(arg);
+
+ assert.sameValue(
+ result,
+ null,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..7476dd322d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/argument-string-critical-unknown-annotation.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.timezone.prototype.getprevioustransition
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01T00:00Z[!foo=bar]",
+ "1970-01-01T00:00Z[UTC][!foo=bar]",
+ "1970-01-01T00:00Z[u-ca=iso8601][!foo=bar]",
+ "1970-01-01T00:00Z[UTC][!foo=bar][u-ca=iso8601]",
+ "1970-01-01T00:00Z[foo=bar][!_foo-bar0=Dont-Ignore-This-99999999999]",
+];
+const instance = new Temporal.TimeZone("UTC");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.getPreviousTransition(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..f8c6ed644e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/argument-string-date-with-utc-offset.js
@@ -0,0 +1,52 @@
+// |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.timezone.prototype.getprevioustransition
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+---*/
+
+const instance = new Temporal.TimeZone("UTC");
+
+const validStrings = [
+ "1970-01-01T00Z",
+ "1970-01-01T00Z[UTC]",
+ "1970-01-01T00Z[!UTC]",
+ "1970-01-01T00Z[Europe/Vienna]",
+ "1970-01-01T00+00:00",
+ "1970-01-01T00+00:00[UTC]",
+ "1970-01-01T00+00:00[!UTC]",
+ "1969-12-31T16-08:00[America/Vancouver]",
+];
+
+for (const arg of validStrings) {
+ const result = instance.getPreviousTransition(arg);
+
+ assert.sameValue(
+ result,
+ null,
+ `"${arg}" is a valid UTC offset with time for Instant`
+ );
+}
+
+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.getPreviousTransition(arg),
+ `"${arg}" UTC offset without time is not valid for Instant`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/argument-string-invalid.js
new file mode 100644
index 0000000000..56b970b203
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/argument-string-invalid.js
@@ -0,0 +1,69 @@
+// |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.timezone.prototype.getprevioustransition
+description: >
+ RangeError thrown if an invalid ISO string (or syntactically valid ISO string
+ that is not supported) is used as an Instant
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ // invalid ISO strings:
+ "",
+ "invalid iso8601",
+ "2020-01-00T00:00Z",
+ "2020-01-32T00:00Z",
+ "2020-02-30T00:00Z",
+ "2021-02-29T00:00Z",
+ "2020-00-01T00:00Z",
+ "2020-13-01T00:00Z",
+ "2020-01-01TZ",
+ "2020-01-01T25:00:00Z",
+ "2020-01-01T01:60:00Z",
+ "2020-01-01T01:60:61Z",
+ "2020-01-01T00:00Zjunk",
+ "2020-01-01T00:00:00Zjunk",
+ "2020-01-01T00:00:00.000000000Zjunk",
+ "2020-01-01T00:00:00+00:00junk",
+ "2020-01-01T00:00:00+00:00[UTC]junk",
+ "2020-01-01T00:00:00+00:00[UTC][u-ca=iso8601]junk",
+ "02020-01-01T00:00Z",
+ "2020-001-01T00:00Z",
+ "2020-01-001T00:00Z",
+ "2020-01-01T001Z",
+ "2020-01-01T01:001Z",
+ "2020-01-01T01:01:001Z",
+ // valid, but forms not supported in Temporal:
+ "2020-W01-1T00:00Z",
+ "2020-001T00:00Z",
+ "+0002020-01-01T00:00Z",
+ // may be valid in other contexts, but insufficient information for Instant:
+ "2020-01",
+ "+002020-01",
+ "01-01",
+ "2020-W01",
+ "P1Y",
+ "-P12Y",
+ "2020-01-01",
+ "2020-01-01T00",
+ "2020-01-01T00:00",
+ "2020-01-01T00:00:00",
+ "2020-01-01T00:00:00.000000000",
+ // valid, but outside the supported range:
+ "-999999-01-01T00:00Z",
+ "+999999-01-01T00:00Z",
+];
+
+const instance = new Temporal.TimeZone("UTC");
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => instance.getPreviousTransition(arg),
+ `"${arg}" should not be a valid ISO string for an Instant`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..9aef6ab2a8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/argument-string-multiple-calendar.js
@@ -0,0 +1,28 @@
+// |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.timezone.prototype.getprevioustransition
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01T00:00Z[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00Z[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01T00:00Z[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00Z[u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+];
+const instance = new Temporal.TimeZone("UTC");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.getPreviousTransition(arg),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..2903f9d7e2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/argument-string-multiple-time-zone.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.timezone.prototype.getprevioustransition
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01T00:00Z[UTC][UTC]",
+ "1970-01-01T00:00Z[!UTC][UTC]",
+ "1970-01-01T00:00Z[UTC][!UTC]",
+ "1970-01-01T00:00Z[UTC][u-ca=iso8601][UTC]",
+ "1970-01-01T00:00Z[UTC][foo=bar][UTC]",
+];
+const instance = new Temporal.TimeZone("UTC");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.getPreviousTransition(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/argument-string-time-separators.js
new file mode 100644
index 0000000000..8e6d11d103
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/argument-string-time-separators.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.timezone.prototype.getprevioustransition
+description: Time separator in string argument can vary
+features: [Temporal]
+---*/
+
+const tests = [
+ ["1970-01-01T00:00Z", "uppercase T"],
+ ["1970-01-01t00:00Z", "lowercase T"],
+ ["1970-01-01 00:00Z", "space between date and time"],
+];
+
+const instance = new Temporal.TimeZone("UTC");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.getPreviousTransition(arg);
+
+ assert.sameValue(
+ result,
+ null,
+ `variant time separators (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..fe3c987a08
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/argument-string-time-zone-annotation.js
@@ -0,0 +1,34 @@
+// |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.timezone.prototype.getprevioustransition
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["1970-01-01T00:00Z[Asia/Kolkata]", "named, with Z"],
+ ["1970-01-01T00:00Z[!Europe/Vienna]", "named, with Z and !"],
+ ["1970-01-01T00:00Z[+00:00]", "numeric, with Z"],
+ ["1970-01-01T00:00Z[!-02:30]", "numeric, with Z and !"],
+ ["1970-01-01T00:00+00:00[UTC]", "named, with offset"],
+ ["1970-01-01T00:00+00:00[!Africa/Abidjan]", "named, with offset and !"],
+ ["1970-01-01T00:00+00:00[-08:00]", "numeric, with offset"],
+ ["1970-01-01T00:00+00:00[!+01:00]", "numeric, with offset and !"],
+];
+
+const instance = new Temporal.TimeZone("UTC");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.getPreviousTransition(arg);
+
+ assert.sameValue(
+ result,
+ null,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..d998b85a68
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/argument-string-unknown-annotation.js
@@ -0,0 +1,31 @@
+// |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.timezone.prototype.getprevioustransition
+description: Various forms of unknown annotation
+features: [Temporal]
+---*/
+
+const tests = [
+ ["1970-01-01T00:00Z[foo=bar]", "alone"],
+ ["1970-01-01T00:00Z[UTC][foo=bar]", "with time zone"],
+ ["1970-01-01T00:00Z[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["1970-01-01T00:00Z[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["1970-01-01T00:00Z[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+const instance = new Temporal.TimeZone("UTC");
+
+tests.forEach(([arg, description]) => {
+ const result = instance.getPreviousTransition(arg);
+
+ assert.sameValue(
+ result,
+ null,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/argument-wrong-type.js
new file mode 100644
index 0000000000..0519c26435
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/argument-wrong-type.js
@@ -0,0 +1,46 @@
+// |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.timezone.prototype.getprevioustransition
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ for Instant
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.TimeZone("UTC");
+
+const primitiveTests = [
+ [undefined, "undefined"],
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [19761118, "number that would convert to a valid ISO string in other contexts"],
+ [1n, "bigint"],
+ [{}, "plain object"],
+ [Temporal.Instant, "Temporal.Instant, object"],
+];
+
+for (const [arg, description] of primitiveTests) {
+ assert.throws(
+ typeof arg === "string" || (typeof arg === "object" && arg !== null) || typeof arg === "function"
+ ? RangeError
+ : TypeError,
+ () => instance.getPreviousTransition(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [Temporal.Instant.prototype, "Temporal.Instant.prototype, object"], // fails brand check in toString()
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.getPreviousTransition(arg), `${description} does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/argument-zoneddatetime.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/argument-zoneddatetime.js
new file mode 100644
index 0000000000..dcc93e83c5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/argument-zoneddatetime.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.timezone.prototype.getprevioustransition
+description: Fast path for converting Temporal.ZonedDateTime to Temporal.Instant
+info: |
+ sec-temporal.timezone.prototype.getprevioustransition step 3:
+ 3. Set _startingPoint_ to ? ToTemporalInstant(_startingPoint_).
+ sec-temporal-totemporalinstant step 1.b:
+ b. If _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return ! CreateTemporalInstant(_item_.[[Nanoseconds]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalInstantFastPath((datetime) => {
+ const timeZone = Temporal.TimeZone.from("UTC");
+ const result = timeZone.getPreviousTransition(datetime);
+ assert.sameValue(result, null, "transition result");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/branding.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/branding.js
new file mode 100644
index 0000000000..5d8f674b80
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/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.timezone.prototype.getprevioustransition
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const getPreviousTransition = Temporal.TimeZone.prototype.getPreviousTransition;
+
+assert.sameValue(typeof getPreviousTransition, "function");
+
+const args = [new Temporal.Instant(0n)];
+
+assert.throws(TypeError, () => getPreviousTransition.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => getPreviousTransition.apply(null, args), "null");
+assert.throws(TypeError, () => getPreviousTransition.apply(true, args), "true");
+assert.throws(TypeError, () => getPreviousTransition.apply("", args), "empty string");
+assert.throws(TypeError, () => getPreviousTransition.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => getPreviousTransition.apply(1, args), "1");
+assert.throws(TypeError, () => getPreviousTransition.apply({}, args), "plain object");
+assert.throws(TypeError, () => getPreviousTransition.apply(Temporal.TimeZone, args), "Temporal.TimeZone");
+assert.throws(TypeError, () => getPreviousTransition.apply(Temporal.TimeZone.prototype, args), "Temporal.TimeZone.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/browser.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/builtin.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/builtin.js
new file mode 100644
index 0000000000..0051ee5e68
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/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.timezone.prototype.getprevioustransition
+description: >
+ Tests that Temporal.TimeZone.prototype.getPreviousTransition
+ 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.TimeZone.prototype.getPreviousTransition),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.TimeZone.prototype.getPreviousTransition),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.TimeZone.prototype.getPreviousTransition),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.TimeZone.prototype.getPreviousTransition.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/instant-string-limits.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/instant-string-limits.js
new file mode 100644
index 0000000000..7bf8f08e9d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/instant-string-limits.js
@@ -0,0 +1,47 @@
+// |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.timezone.prototype.getprevioustransition
+description: String arguments at the limit of the representable range
+features: [Temporal]
+---*/
+
+const instance = new Temporal.TimeZone("UTC");
+
+const minInstantStrings = [
+ "-271821-04-20T00:00Z",
+ "-271821-04-19T23:00-01:00",
+ "-271821-04-19T00:00:00.000000001-23:59:59.999999999",
+];
+for (const str of minInstantStrings) {
+ assert.sameValue(instance.getPreviousTransition(str), null, `instant string ${str} should be valid`);
+}
+
+const maxInstantStrings = [
+ "+275760-09-13T00:00Z",
+ "+275760-09-13T01:00+01:00",
+ "+275760-09-13T23:59:59.999999999+23:59:59.999999999",
+];
+
+for (const str of maxInstantStrings) {
+ assert.sameValue(instance.getPreviousTransition(str), null, `instant string ${str} should be valid`);
+}
+
+const outOfRangeInstantStrings = [
+ "-271821-04-19T23:59:59.999999999Z",
+ "-271821-04-19T23:00-00:59:59.999999999",
+ "-271821-04-19T00:00:00-23:59:59.999999999",
+ "-271821-04-19T00:00:00-24:00",
+ "+275760-09-13T00:00:00.000000001Z",
+ "+275760-09-13T01:00+00:59:59.999999999",
+ "+275760-09-14T00:00+23:59:59.999999999",
+ "+275760-09-14T00:00+24:00",
+];
+
+for (const str of outOfRangeInstantStrings) {
+ assert.throws(RangeError, () => instance.getPreviousTransition(str), `instant string ${str} should not be valid`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/instant-string.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/instant-string.js
new file mode 100644
index 0000000000..1b29755e93
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/instant-string.js
@@ -0,0 +1,34 @@
+// |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.timezone.prototype.getprevioustransition
+description: Conversion of ISO date-time strings to Temporal.Instant instances
+features: [Temporal]
+---*/
+
+const instance = new Temporal.TimeZone("UTC");
+
+let str = "1970-01-01T00:00";
+assert.throws(RangeError, () => instance.getPreviousTransition(str), "bare date-time string is not an instant");
+str = "1970-01-01T00:00[UTC]";
+assert.throws(RangeError, () => instance.getPreviousTransition(str), "date-time + IANA annotation is not an instant");
+
+// The following are all valid strings so should not throw:
+
+const valids = [
+ "1970-01-01T00:00Z",
+ "1970-01-01T00:00+01:00",
+ "1970-01-01T00:00Z[UTC]",
+ "1970-01-01T00:00+01:00[UTC]",
+ "1970-01-01T00:00Z[u-ca=hebrew]",
+ "1970-01-01T00:00+01:00[u-ca=hebrew]",
+ "1970-01-01T00:00+01:00[Etc/Ignored][u-ca=hebrew]",
+];
+for (const str of valids) {
+ const result = instance.getPreviousTransition(str);
+ assert.sameValue(result, null);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/leap-second.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/leap-second.js
new file mode 100644
index 0000000000..b2db8c3b61
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/leap-second.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.timezone.prototype.getprevioustransition
+description: Leap second is a valid ISO string for Instant
+features: [Temporal]
+---*/
+
+const instance = new Temporal.TimeZone("UTC");
+
+const arg = "2016-12-31T23:59:60Z";
+const result = instance.getPreviousTransition(arg);
+assert.sameValue(
+ result,
+ null,
+ "leap second is a valid ISO string for Instant"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/length.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/length.js
new file mode 100644
index 0000000000..ce577d9e1f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/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.timezone.prototype.getprevioustransition
+description: Temporal.TimeZone.prototype.getPreviousTransition.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.TimeZone.prototype.getPreviousTransition, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/name.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/name.js
new file mode 100644
index 0000000000..863f68d954
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/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.timezone.prototype.getprevioustransition
+description: Temporal.TimeZone.prototype.getPreviousTransition.name is "getPreviousTransition".
+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.TimeZone.prototype.getPreviousTransition, "name", {
+ value: "getPreviousTransition",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/not-a-constructor.js
new file mode 100644
index 0000000000..ff62b8aeeb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/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.timezone.prototype.getprevioustransition
+description: >
+ Temporal.TimeZone.prototype.getPreviousTransition 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.TimeZone.prototype.getPreviousTransition();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.TimeZone.prototype.getPreviousTransition), false,
+ "isConstructor(Temporal.TimeZone.prototype.getPreviousTransition)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/prop-desc.js
new file mode 100644
index 0000000000..06f2b98876
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/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.timezone.prototype.getprevioustransition
+description: The "getPreviousTransition" property of Temporal.TimeZone.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.TimeZone.prototype.getPreviousTransition,
+ "function",
+ "`typeof TimeZone.prototype.getPreviousTransition` is `function`"
+);
+
+verifyProperty(Temporal.TimeZone.prototype, "getPreviousTransition", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/shell.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/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/TimeZone/prototype/getPreviousTransition/year-zero.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/year-zero.js
new file mode 100644
index 0000000000..1d0208c42f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/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.timezone.prototype.getprevioustransition
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-03-30T00:45Z",
+ "-000000-03-30T01:45+01:00",
+ "-000000-03-30T01:45:00+00:00[UTC]",
+];
+const instance = new Temporal.TimeZone("UTC");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.getPreviousTransition(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/id/branding.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/id/branding.js
new file mode 100644
index 0000000000..0673aa9475
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/id/branding.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-get-temporal.timezone.prototype.id
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "id");
+const id = descriptor.get;
+
+assert.sameValue(typeof id, "function");
+
+assert.throws(TypeError, () => id.call(undefined), "undefined");
+assert.throws(TypeError, () => id.call(null), "null");
+assert.throws(TypeError, () => id.call(true), "true");
+assert.throws(TypeError, () => id.call(""), "empty string");
+assert.throws(TypeError, () => id.call(Symbol()), "symbol");
+assert.throws(TypeError, () => id.call(1), "1");
+assert.throws(TypeError, () => id.call({}), "plain object");
+assert.throws(TypeError, () => id.call(Temporal.TimeZone), "Temporal.TimeZone");
+assert.throws(TypeError, () => id.call(Temporal.TimeZone.prototype), "Temporal.TimeZone.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/id/browser.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/id/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/id/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/id/custom-timezone.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/id/custom-timezone.js
new file mode 100644
index 0000000000..31918f4866
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/id/custom-timezone.js
@@ -0,0 +1,26 @@
+// |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-get-temporal.timezone.prototype.id
+description: Getter does not call toString(), returns the ID from internal slot
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const expected = [];
+
+const timeZone = new Temporal.TimeZone("UTC");
+TemporalHelpers.observeProperty(actual, timeZone, Symbol.toPrimitive, undefined);
+TemporalHelpers.observeProperty(actual, timeZone, "toString", function () {
+ actual.push("call timeZone.toString");
+ return "time zone";
+});
+
+const result = timeZone.id;
+assert.compareArray(actual, expected);
+assert.sameValue(result, "UTC");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/id/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/id/prop-desc.js
new file mode 100644
index 0000000000..f1e4358ba0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/id/prop-desc.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-get-temporal.timezone.prototype.id
+description: The "id" property of Temporal.TimeZone.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "id");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/id/shell.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/id/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/id/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/prop-desc.js
new file mode 100644
index 0000000000..040655cc05
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/prop-desc.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-timezone-prototype
+description: The "prototype" property of Temporal.TimeZone
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(typeof Temporal.TimeZone.prototype, "object");
+assert.notSameValue(Temporal.TimeZone.prototype, null);
+
+verifyProperty(Temporal.TimeZone, "prototype", {
+ writable: false,
+ enumerable: false,
+ configurable: false,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/shell.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toJSON/branding.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toJSON/branding.js
new file mode 100644
index 0000000000..c2c2d70b43
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toJSON/branding.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.timezone.prototype.tojson
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toJSON = Temporal.TimeZone.prototype.toJSON;
+
+assert.sameValue(typeof toJSON, "function");
+
+assert.throws(TypeError, () => toJSON.call(undefined), "undefined");
+assert.throws(TypeError, () => toJSON.call(null), "null");
+assert.throws(TypeError, () => toJSON.call(true), "true");
+assert.throws(TypeError, () => toJSON.call(""), "empty string");
+assert.throws(TypeError, () => toJSON.call(Symbol()), "symbol");
+assert.throws(TypeError, () => toJSON.call(1), "1");
+assert.throws(TypeError, () => toJSON.call({}), "plain object");
+assert.throws(TypeError, () => toJSON.call(Temporal.TimeZone), "Temporal.TimeZone");
+assert.throws(TypeError, () => toJSON.call(Temporal.TimeZone.prototype), "Temporal.TimeZone.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toJSON/browser.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toJSON/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toJSON/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toJSON/builtin.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toJSON/builtin.js
new file mode 100644
index 0000000000..6be9473aca
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toJSON/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.timezone.prototype.tojson
+description: >
+ Tests that Temporal.TimeZone.prototype.toJSON
+ 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.TimeZone.prototype.toJSON),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.TimeZone.prototype.toJSON),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.TimeZone.prototype.toJSON),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.TimeZone.prototype.toJSON.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toJSON/length.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toJSON/length.js
new file mode 100644
index 0000000000..c9643b2cb4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toJSON/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.timezone.prototype.tojson
+description: Temporal.TimeZone.prototype.toJSON.length is 0
+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.TimeZone.prototype.toJSON, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toJSON/name.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toJSON/name.js
new file mode 100644
index 0000000000..120b740fcb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toJSON/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.timezone.prototype.tojson
+description: Temporal.TimeZone.prototype.toJSON.name is "toJSON".
+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.TimeZone.prototype.toJSON, "name", {
+ value: "toJSON",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toJSON/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toJSON/not-a-constructor.js
new file mode 100644
index 0000000000..1e2dd72b4b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toJSON/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.timezone.prototype.tojson
+description: >
+ Temporal.TimeZone.prototype.toJSON 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.TimeZone.prototype.toJSON();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.TimeZone.prototype.toJSON), false,
+ "isConstructor(Temporal.TimeZone.prototype.toJSON)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toJSON/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toJSON/prop-desc.js
new file mode 100644
index 0000000000..4d6365274f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toJSON/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.timezone.prototype.tojson
+description: The "toJSON" property of Temporal.TimeZone.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.TimeZone.prototype.toJSON,
+ "function",
+ "`typeof TimeZone.prototype.toJSON` is `function`"
+);
+
+verifyProperty(Temporal.TimeZone.prototype, "toJSON", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toJSON/returns-identifier-slot.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toJSON/returns-identifier-slot.js
new file mode 100644
index 0000000000..9681aab599
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toJSON/returns-identifier-slot.js
@@ -0,0 +1,26 @@
+// |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.timezone.prototype.tojson
+description: toJSON() returns the internal slot value
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+
+const timeZone = new Temporal.TimeZone("UTC");
+TemporalHelpers.observeProperty(actual, timeZone, Symbol.toPrimitive, undefined);
+TemporalHelpers.observeProperty(actual, timeZone, "id", "Etc/Bogus");
+TemporalHelpers.observeProperty(actual, timeZone, "toString", function () {
+ actual.push("call timeZone.toString");
+ return "Etc/TAI";
+});
+
+const result = timeZone.toJSON();
+assert.sameValue(result, "UTC", "toJSON gets the internal slot value");
+assert.compareArray(actual, [], "should not invoke any observable operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toJSON/shell.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toJSON/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toJSON/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/TimeZone/prototype/toString/branding.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toString/branding.js
new file mode 100644
index 0000000000..ec12ff4fbd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toString/branding.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.timezone.prototype.tostring
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toString = Temporal.TimeZone.prototype.toString;
+
+assert.sameValue(typeof toString, "function");
+
+assert.throws(TypeError, () => toString.call(undefined), "undefined");
+assert.throws(TypeError, () => toString.call(null), "null");
+assert.throws(TypeError, () => toString.call(true), "true");
+assert.throws(TypeError, () => toString.call(""), "empty string");
+assert.throws(TypeError, () => toString.call(Symbol()), "symbol");
+assert.throws(TypeError, () => toString.call(1), "1");
+assert.throws(TypeError, () => toString.call({}), "plain object");
+assert.throws(TypeError, () => toString.call(Temporal.TimeZone), "Temporal.TimeZone");
+assert.throws(TypeError, () => toString.call(Temporal.TimeZone.prototype), "Temporal.TimeZone.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toString/browser.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toString/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toString/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toString/builtin.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toString/builtin.js
new file mode 100644
index 0000000000..d1626775aa
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toString/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.timezone.prototype.tostring
+description: >
+ Tests that Temporal.TimeZone.prototype.toString
+ 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.TimeZone.prototype.toString),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.TimeZone.prototype.toString),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.TimeZone.prototype.toString),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.TimeZone.prototype.toString.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toString/length.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toString/length.js
new file mode 100644
index 0000000000..793d2c25f1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toString/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.timezone.prototype.tostring
+description: Temporal.TimeZone.prototype.toString.length is 0
+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.TimeZone.prototype.toString, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toString/name.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toString/name.js
new file mode 100644
index 0000000000..1197a8faa9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toString/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.timezone.prototype.tostring
+description: Temporal.TimeZone.prototype.toString.name is "toString".
+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.TimeZone.prototype.toString, "name", {
+ value: "toString",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toString/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toString/not-a-constructor.js
new file mode 100644
index 0000000000..ce6e8d490a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toString/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.timezone.prototype.tostring
+description: >
+ Temporal.TimeZone.prototype.toString 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.TimeZone.prototype.toString();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.TimeZone.prototype.toString), false,
+ "isConstructor(Temporal.TimeZone.prototype.toString)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toString/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toString/prop-desc.js
new file mode 100644
index 0000000000..f4b0067d12
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toString/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.timezone.prototype.tostring
+description: The "toString" property of Temporal.TimeZone.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.TimeZone.prototype.toString,
+ "function",
+ "`typeof TimeZone.prototype.toString` is `function`"
+);
+
+verifyProperty(Temporal.TimeZone.prototype, "toString", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toString/shell.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toString/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toString/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/TimeZone/prototype/toStringTag/browser.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toStringTag/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toStringTag/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toStringTag/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toStringTag/prop-desc.js
new file mode 100644
index 0000000000..fae1256a56
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toStringTag/prop-desc.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.timezone.prototype-@@tostringtag
+description: The @@toStringTag property of Temporal.TimeZone
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.TimeZone.prototype, Symbol.toStringTag, {
+ value: "Temporal.TimeZone",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toStringTag/shell.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toStringTag/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/prototype/toStringTag/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/shell.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/TimeZone/subclass.js b/js/src/tests/test262/built-ins/Temporal/TimeZone/subclass.js
new file mode 100644
index 0000000000..a67e299e3a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/TimeZone/subclass.js
@@ -0,0 +1,20 @@
+// |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.timezone
+description: Test for Temporal.TimeZone subclassing.
+features: [Temporal]
+---*/
+
+class CustomTimeZone extends Temporal.TimeZone {
+}
+
+const instance = new CustomTimeZone("UTC");
+assert.sameValue(instance.toString(), "UTC");
+assert.sameValue(Object.getPrototypeOf(instance), CustomTimeZone.prototype, "Instance of CustomTimeZone");
+assert(instance instanceof CustomTimeZone, "Instance of CustomTimeZone");
+assert(instance instanceof Temporal.TimeZone, "Instance of Temporal.TimeZone");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/builtin.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/builtin.js
new file mode 100644
index 0000000000..6184820ef9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/builtin.js
@@ -0,0 +1,30 @@
+// |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.zoneddatetime
+description: Tests that Temporal.ZonedDateTime meets the requirements for built-in objects
+info: |
+ 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.ZonedDateTime),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime),
+ Function.prototype, "prototype");
+
+assert.sameValue(typeof Temporal.ZonedDateTime.prototype,
+ "object", "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/calendar-case-insensitive.js
new file mode 100644
index 0000000000..80ff0f9703
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/calendar-case-insensitive.js
@@ -0,0 +1,16 @@
+// |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.zoneddatetime
+description: Calendar names are case-insensitive
+features: [Temporal]
+---*/
+
+const arg = "iSo8601";
+
+const result = new Temporal.ZonedDateTime(0n, "UTC", arg);
+assert.sameValue(result.calendarId, "iso8601", "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/calendar-number.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/calendar-number.js
new file mode 100644
index 0000000000..14b85249a1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/calendar-number.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.zoneddatetime
+description: A number is not allowed to be a calendar
+features: [Temporal]
+---*/
+
+const numbers = [
+ 1,
+ -19761118,
+ 19761118,
+ 1234567890,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => new Temporal.ZonedDateTime(0n, "UTC", arg),
+ "A number is not a valid ISO string for Calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/calendar-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/calendar-string.js
new file mode 100644
index 0000000000..f411804a50
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/calendar-string.js
@@ -0,0 +1,16 @@
+// |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.zoneddatetime.constructor
+description: A calendar ID is valid input for Calendar
+features: [Temporal]
+---*/
+
+const arg = "iso8601";
+
+const result = new Temporal.ZonedDateTime(0n, "UTC", arg);
+assert.sameValue(result.getISOFields().calendar, "iso8601", `Calendar created from string "${arg}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/calendar-temporal-object.js
new file mode 100644
index 0000000000..21d3b71e75
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/calendar-temporal-object.js
@@ -0,0 +1,41 @@
+// |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.zoneddatetime
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal-totemporalcalendar step 1.b:
+ b. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const plainDate = new Temporal.PlainDate(2000, 5, 2);
+const plainDateTime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const plainMonthDay = new Temporal.PlainMonthDay(5, 2);
+const plainYearMonth = new Temporal.PlainYearMonth(2000, 5);
+const zonedDateTime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+
+[plainDate, plainDateTime, plainMonthDay, plainYearMonth, zonedDateTime].forEach((arg) => {
+ const actual = [];
+ const expected = [];
+
+ const calendar = arg.getISOFields().calendar;
+
+ Object.defineProperty(arg, "calendar", {
+ get() {
+ actual.push("get calendar");
+ return calendar;
+ },
+ });
+
+ const result = new Temporal.ZonedDateTime(0n, "UTC", arg);
+ assert.sameValue(result.getISOFields().calendar, calendar, "Temporal object coerced to calendar");
+
+ assert.compareArray(actual, expected, "calendar getter not called");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/calendar-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/calendar-undefined.js
new file mode 100644
index 0000000000..ecc264b47d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/calendar-undefined.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.zoneddatetime
+description: Calendar argument defaults to the built-in ISO 8601 calendar
+features: [BigInt, Temporal]
+---*/
+
+const args = [957270896987654321n, new Temporal.TimeZone("UTC")];
+
+Object.defineProperty(Temporal.Calendar, "from", {
+ get() {
+ throw new Test262Error("Should not get Calendar.from");
+ },
+});
+
+const explicit = new Temporal.ZonedDateTime(...args, undefined);
+assert.sameValue(explicit.getISOFields().calendar, "iso8601", "calendar slot should store a string");
+
+const implicit = new Temporal.ZonedDateTime(...args);
+assert.sameValue(implicit.getISOFields().calendar, "iso8601", "calendar slot should store a string");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/calendar-wrong-type.js
new file mode 100644
index 0000000000..82cf2a3c83
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/calendar-wrong-type.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.zoneddatetime
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or object for Calendar
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const primitiveTests = [
+ [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,
+ () => new Temporal.ZonedDateTime(0n, "UTC", arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => new Temporal.ZonedDateTime(0n, "UTC", arg), `${description} is not a valid object and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..7a7d87020f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-builtin-calendar-no-array-iteration.js
@@ -0,0 +1,26 @@
+// |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.zoneddatetime.compare
+description: >
+ Calling the method with a property bag argument with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const timeZone = "UTC";
+const datetime = new Temporal.ZonedDateTime(0n, timeZone);
+const arg = { year: 2000, month: 5, day: 2, hour: 21, minute: 43, second: 5, timeZone, calendar: "iso8601" };
+Temporal.ZonedDateTime.compare(arg, datetime);
+Temporal.ZonedDateTime.compare(datetime, arg);
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-ambiguous-wall-clock-time.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-ambiguous-wall-clock-time.js
new file mode 100644
index 0000000000..82854f00eb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-ambiguous-wall-clock-time.js
@@ -0,0 +1,138 @@
+// |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.zoneddatetime.compare
+description: >
+ Correct time zone calls are made when converting a ZonedDateTime-like property
+ bag denoting an ambiguous wall-clock time
+includes: [temporalHelpers.js, compareArray.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+
+const dstTimeZone = TemporalHelpers.springForwardFallBackTimeZone();
+const timeZone1 = TemporalHelpers.timeZoneObserver(actual, "one.timeZone", {
+ getOffsetNanosecondsFor: dstTimeZone.getOffsetNanosecondsFor.bind(dstTimeZone),
+ getPossibleInstantsFor: dstTimeZone.getPossibleInstantsFor.bind(dstTimeZone),
+});
+const timeZone2 = TemporalHelpers.timeZoneObserver(actual, "two.timeZone", {
+ getOffsetNanosecondsFor: dstTimeZone.getOffsetNanosecondsFor.bind(dstTimeZone),
+ getPossibleInstantsFor: dstTimeZone.getPossibleInstantsFor.bind(dstTimeZone),
+});
+const calendar1 = TemporalHelpers.calendarObserver(actual, "one.calendar");
+const calendar2 = TemporalHelpers.calendarObserver(actual, "two.calendar");
+
+const expectedOne = [
+ // GetTemporalCalendarSlotValueWithISODefault
+ "has one.calendar.dateAdd",
+ "has one.calendar.dateFromFields",
+ "has one.calendar.dateUntil",
+ "has one.calendar.day",
+ "has one.calendar.dayOfWeek",
+ "has one.calendar.dayOfYear",
+ "has one.calendar.daysInMonth",
+ "has one.calendar.daysInWeek",
+ "has one.calendar.daysInYear",
+ "has one.calendar.fields",
+ "has one.calendar.id",
+ "has one.calendar.inLeapYear",
+ "has one.calendar.mergeFields",
+ "has one.calendar.month",
+ "has one.calendar.monthCode",
+ "has one.calendar.monthDayFromFields",
+ "has one.calendar.monthsInYear",
+ "has one.calendar.weekOfYear",
+ "has one.calendar.year",
+ "has one.calendar.yearMonthFromFields",
+ "has one.calendar.yearOfWeek",
+ // lookup
+ "get one.calendar.dateFromFields",
+ "get one.calendar.fields",
+ // CalendarFields
+ "call one.calendar.fields",
+ // ToTemporalTimeZoneSlotValue
+ "has one.timeZone.getOffsetNanosecondsFor",
+ "has one.timeZone.getPossibleInstantsFor",
+ "has one.timeZone.id",
+ // InterpretTemporalDateTimeFields
+ "call one.calendar.dateFromFields",
+ // lookup
+ "get one.timeZone.getOffsetNanosecondsFor",
+ "get one.timeZone.getPossibleInstantsFor",
+ // InterpretISODateTimeOffset
+ "call one.timeZone.getPossibleInstantsFor",
+];
+
+const expectedTwo = [
+ // GetTemporalCalendarSlotValueWithISODefault
+ "has two.calendar.dateAdd",
+ "has two.calendar.dateFromFields",
+ "has two.calendar.dateUntil",
+ "has two.calendar.day",
+ "has two.calendar.dayOfWeek",
+ "has two.calendar.dayOfYear",
+ "has two.calendar.daysInMonth",
+ "has two.calendar.daysInWeek",
+ "has two.calendar.daysInYear",
+ "has two.calendar.fields",
+ "has two.calendar.id",
+ "has two.calendar.inLeapYear",
+ "has two.calendar.mergeFields",
+ "has two.calendar.month",
+ "has two.calendar.monthCode",
+ "has two.calendar.monthDayFromFields",
+ "has two.calendar.monthsInYear",
+ "has two.calendar.weekOfYear",
+ "has two.calendar.year",
+ "has two.calendar.yearMonthFromFields",
+ "has two.calendar.yearOfWeek",
+ // lookup
+ "get two.calendar.dateFromFields",
+ "get two.calendar.fields",
+ // CalendarFields
+ "call two.calendar.fields",
+ // ToTemporalTimeZoneSlotValue
+ "has two.timeZone.getOffsetNanosecondsFor",
+ "has two.timeZone.getPossibleInstantsFor",
+ "has two.timeZone.id",
+ // InterpretTemporalDateTimeFields
+ "call two.calendar.dateFromFields",
+ // lookup
+ "get two.timeZone.getOffsetNanosecondsFor",
+ "get two.timeZone.getPossibleInstantsFor",
+ // InterpretISODateTimeOffset
+ "call two.timeZone.getPossibleInstantsFor",
+];
+
+Temporal.ZonedDateTime.compare(
+ { year: 2000, month: 4, day: 2, hour: 2, minute: 30, timeZone: timeZone1, calendar: calendar1 },
+ { year: 2000, month: 4, day: 2, hour: 2, minute: 30, timeZone: timeZone2, calendar: calendar2 },
+);
+
+const expectedSpringForward = expectedOne.concat([
+ // DisambiguatePossibleInstants
+ "call one.timeZone.getOffsetNanosecondsFor",
+ "call one.timeZone.getOffsetNanosecondsFor",
+ "call one.timeZone.getPossibleInstantsFor",
+], expectedTwo, [
+ // DisambiguatePossibleInstants
+ "call two.timeZone.getOffsetNanosecondsFor",
+ "call two.timeZone.getOffsetNanosecondsFor",
+ "call two.timeZone.getPossibleInstantsFor",
+]);
+assert.compareArray(actual, expectedSpringForward, "order of operations converting property bags at skipped wall-clock time");
+actual.splice(0); // clear
+
+Temporal.ZonedDateTime.compare(
+ { year: 2000, month: 10, day: 29, hour: 1, minute: 30, timeZone: timeZone1, calendar: calendar1 },
+ { year: 2000, month: 10, day: 29, hour: 1, minute: 30, timeZone: timeZone2, calendar: calendar2 },
+);
+
+const expectedFallBack = expectedOne.concat(expectedTwo);
+assert.compareArray(actual, expectedFallBack, "order of operations converting property bags at repeated wall-clock time");
+actual.splice(0); // clear
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-calendar-case-insensitive.js
new file mode 100644
index 0000000000..ea05affac6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-calendar-case-insensitive.js
@@ -0,0 +1,22 @@
+// |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.zoneddatetime.compare
+description: The calendar name is case-insensitive
+features: [Temporal]
+---*/
+
+const calendar = "IsO8601";
+
+const timeZone = new Temporal.TimeZone("UTC");
+const datetime = new Temporal.ZonedDateTime(0n, timeZone);
+
+const arg = { year: 1970, monthCode: "M01", day: 1, timeZone, calendar };
+const result1 = Temporal.ZonedDateTime.compare(arg, datetime);
+assert.sameValue(result1, 0, "Calendar is case-insensitive (first argument)");
+const result2 = Temporal.ZonedDateTime.compare(datetime, arg);
+assert.sameValue(result2, 0, "Calendar is case-insensitive (second argument)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-calendar-leap-second.js
new file mode 100644
index 0000000000..9a09ea68b9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-calendar-leap-second.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.zoneddatetime.compare
+description: Leap second is a valid ISO string for a calendar in a property bag
+features: [Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const datetime = new Temporal.ZonedDateTime(217_123_200_000_000_000n, timeZone);
+const calendar = "2016-12-31T23:59:60+00:00[UTC]";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, timeZone, calendar };
+const result1 = Temporal.ZonedDateTime.compare(arg, datetime);
+assert.sameValue(result1, 0, "leap second is a valid ISO string for calendar (first argument)");
+const result2 = Temporal.ZonedDateTime.compare(datetime, arg);
+assert.sameValue(result2, 0, "leap second is a valid ISO string for calendar (second argument)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-calendar-number.js
new file mode 100644
index 0000000000..3171b189a9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-calendar-number.js
@@ -0,0 +1,35 @@
+// |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.zoneddatetime.compare
+description: A number as calendar in a property bag is not accepted
+features: [Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const datetime = new Temporal.ZonedDateTime(0n, timeZone);
+
+const numbers = [
+ 1,
+ 19970327,
+ -19970327,
+ 1234567890,
+];
+
+for (const calendar of numbers) {
+ const arg = { year: 1970, monthCode: "M01", day: 1, calendar, timeZone };
+ assert.throws(
+ TypeError,
+ () => Temporal.ZonedDateTime.compare(arg, datetime),
+ "A number is not a valid ISO string for calendar (first argument)"
+ );
+ assert.throws(
+ TypeError,
+ () => Temporal.ZonedDateTime.compare(datetime, arg),
+ "A number is not a valid ISO string for calendar (second argument)"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-calendar-string.js
new file mode 100644
index 0000000000..873234eab5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-calendar-string.js
@@ -0,0 +1,35 @@
+// |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.zoneddatetime.compare
+description: A calendar ID is valid input for Calendar
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dateFromFieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dateFromFields");
+Object.defineProperty(Temporal.Calendar.prototype, "dateFromFields", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("dateFromFields should not be looked up");
+ },
+});
+
+const calendar = "iso8601";
+
+const timeZone = new Temporal.TimeZone("UTC");
+const datetime = new Temporal.ZonedDateTime(0n, timeZone);
+const arg = { year: 1970, monthCode: "M01", day: 1, timeZone, calendar };
+
+const result1 = Temporal.ZonedDateTime.compare(arg, datetime);
+assert.sameValue(result1, 0, `Calendar created from string "${arg}" (first argument)`);
+
+const result2 = Temporal.ZonedDateTime.compare(datetime, arg);
+assert.sameValue(result2, 0, `Calendar created from string "${arg}" (second argument)`);
+
+Object.defineProperty(Temporal.Calendar.prototype, "dateFromFields", dateFromFieldsOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-calendar-wrong-type.js
new file mode 100644
index 0000000000..9d70107461
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-calendar-wrong-type.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.zoneddatetime.compare
+description: >
+ Appropriate error thrown when a calendar property from a property bag cannot
+ be converted to a calendar object or string
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(0n, new Temporal.TimeZone("UTC"));
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [calendar, description] of primitiveTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ typeof calendar === "string" ? RangeError : TypeError,
+ () => Temporal.ZonedDateTime.compare(arg, datetime),
+ `${description} does not convert to a valid ISO string (first argument)`
+ );
+ assert.throws(
+ typeof calendar === "string" ? RangeError : TypeError,
+ () => Temporal.ZonedDateTime.compare(datetime, arg),
+ `${description} does not convert to a valid ISO string (second argument)`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+ [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields()
+];
+
+for (const [calendar, description] of typeErrorTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(TypeError, () => Temporal.ZonedDateTime.compare(arg, datetime), `${description} is not a valid property bag and does not convert to a string (first argument)`);
+ assert.throws(TypeError, () => Temporal.ZonedDateTime.compare(datetime, arg), `${description} is not a valid property bag and does not convert to a string (second argument)`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-calendar-year-zero.js
new file mode 100644
index 0000000000..734ad65e6a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-calendar-year-zero.js
@@ -0,0 +1,33 @@
+// |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.zoneddatetime.compare
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(0n, new Temporal.TimeZone("UTC"));
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T17:45",
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+01:00",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.ZonedDateTime.compare(arg, datetime),
+ "reject minus zero as extended year (first argument)"
+ );
+ assert.throws(
+ RangeError,
+ () => Temporal.ZonedDateTime.compare(datetime, arg),
+ "reject minus zero as extended year (second argument)"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-getpossibleinstantsfor-called-with-iso8601-calendar.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-getpossibleinstantsfor-called-with-iso8601-calendar.js
new file mode 100644
index 0000000000..1e963166ba
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-getpossibleinstantsfor-called-with-iso8601-calendar.js
@@ -0,0 +1,61 @@
+// |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.zoneddatetime.prototype.compare
+description: >
+ Time zone's getPossibleInstantsFor is called with a PlainDateTime with the
+ built-in ISO 8601 calendar
+features: [Temporal]
+info: |
+ DisambiguatePossibleInstants:
+ 2. Let _n_ be _possibleInstants_'s length.
+ ...
+ 5. Assert: _n_ = 0.
+ ...
+ 19. If _disambiguation_ is *"earlier"*, then
+ ...
+ c. Let _earlierDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*).
+ d. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _earlierDateTime_).
+ ...
+ 20. Assert: _disambiguation_ is *"compatible"* or *"later"*.
+ ...
+ 23. Let _laterDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*).
+ 24. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _laterDateTime_).
+---*/
+
+class SkippedDateTime extends Temporal.TimeZone {
+ constructor() {
+ super("UTC");
+ this.calls = 0;
+ }
+
+ getPossibleInstantsFor(dateTime) {
+ // Calls occur in pairs. For the first one return no possible instants so
+ // that DisambiguatePossibleInstants will call it again
+ if (this.calls++ % 2 == 0) {
+ return [];
+ }
+
+ assert.sameValue(
+ dateTime.getISOFields().calendar,
+ "iso8601",
+ "getPossibleInstantsFor called with dateTime with built-in ISO 8601 calendar"
+ );
+ return super.getPossibleInstantsFor(dateTime);
+ }
+}
+
+const nonBuiltinISOCalendar = new Temporal.Calendar("iso8601");
+const timeZone1 = new SkippedDateTime();
+const arg1 = { year: 2000, month: 5, day: 2, timeZone: timeZone1, calendar: nonBuiltinISOCalendar };
+const timeZone2 = new SkippedDateTime();
+const arg2 = { year: 2000, month: 5, day: 2, timeZone: timeZone2, calendar: nonBuiltinISOCalendar };
+
+Temporal.ZonedDateTime.compare(arg1, arg2);
+
+assert.sameValue(timeZone1.calls, 2, "getPossibleInstantsFor should have been called 2 times on first time zone");
+assert.sameValue(timeZone2.calls, 2, "getPossibleInstantsFor should have been called 2 times on second time zone");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-invalid-offset-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-invalid-offset-string.js
new file mode 100644
index 0000000000..704149ba69
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-invalid-offset-string.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.zoneddatetime.compare
+description: Property bag with offset property is rejected if offset is in the wrong format
+features: [Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+
+const badOffsets = [
+ "00:00", // missing sign
+ "+0", // too short
+ "-000:00", // too long
+ 0, // must be a string
+ null, // must be a string
+ true, // must be a string
+ 1000n, // must be a string
+];
+badOffsets.forEach((offset) => {
+ const arg = { year: 2021, month: 10, day: 28, offset, timeZone };
+ assert.throws(
+ typeof(offset) === 'string' ? RangeError : TypeError,
+ () => Temporal.ZonedDateTime.compare(arg, datetime),
+ `"${offset} is not a valid offset string (second argument)`
+ );
+ assert.throws(
+ typeof(offset) === 'string' ? RangeError : TypeError,
+ () => Temporal.ZonedDateTime.compare(datetime, arg),
+ `"${offset} is not a valid offset string (second argument)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-offset-not-agreeing-with-timezone.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-offset-not-agreeing-with-timezone.js
new file mode 100644
index 0000000000..e1261ae9df
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-offset-not-agreeing-with-timezone.js
@@ -0,0 +1,18 @@
+// |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.zoneddatetime.compare
+description: Property bag with offset property is rejected if offset does not agree with time zone
+features: [Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("+01:00");
+const datetime = new Temporal.ZonedDateTime(0n, timeZone);
+
+const properties = { year: 2021, month: 10, day: 28, offset: "-07:00", timeZone };
+assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(properties, datetime), "offset property not matching time zone is rejected (first argument)");
+assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(datetime, properties), "offset property not matching time zone is rejected (second argument)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..aa8fba749d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-getoffsetnanosecondsfor-non-integer.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.zoneddatetime.compare
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+
+ assert.throws(RangeError, () => Temporal.ZonedDateTime.compare({ year: 2000, month: 5, day: 2, hour: 12, timeZone }, datetime));
+ assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(datetime, { year: 2000, month: 5, day: 2, hour: 12, timeZone }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..202927c9b8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-getoffsetnanosecondsfor-not-callable.js
@@ -0,0 +1,31 @@
+// |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.zoneddatetime.compare
+description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable
+features: [BigInt, Symbol, Temporal, arrow-function]
+---*/
+
+Temporal.TimeZone.prototype.getPossibleInstantsFor = function () {
+ return [];
+};
+
+[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => {
+ const timeZone = new Temporal.TimeZone("UTC");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => Temporal.ZonedDateTime.compare({ year: 2000, month: 5, day: 2, hour: 12, timeZone }, datetime),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+ assert.throws(
+ TypeError,
+ () => Temporal.ZonedDateTime.compare(datetime, { year: 2000, month: 5, day: 2, hour: 12, timeZone }),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..6671777aa7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.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.zoneddatetime.compare
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+
+ assert.throws(RangeError, () => Temporal.ZonedDateTime.compare({ year: 2000, month: 5, day: 2, hour: 12, timeZone }, datetime));
+ assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(datetime, { year: 2000, month: 5, day: 2, hour: 12, timeZone }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..64a00cb41b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,41 @@
+// |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.zoneddatetime.compare
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+
+ assert.throws(
+ TypeError,
+ () => Temporal.ZonedDateTime.compare({ year: 2000, month: 5, day: 2, hour: 12, timeZone }, datetime),
+ `invalid offset: ${String(wrongOffset)} (${typeof wrongOffset})`
+ );
+ assert.throws(
+ TypeError,
+ () => Temporal.ZonedDateTime.compare(datetime, { year: 2000, month: 5, day: 2, hour: 12, timeZone }),
+ `invalid offset: ${String(wrongOffset)} (${typeof wrongOffset})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-string-datetime.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-string-datetime.js
new file mode 100644
index 0000000000..858ba851c4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-string-datetime.js
@@ -0,0 +1,64 @@
+// |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.zoneddatetime.compare
+description: Conversion of ISO date-time strings to Temporal.TimeZone instances
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(0n, "UTC");
+
+let timeZone = "2021-08-19T17:30";
+assert.throws(RangeError, () => Temporal.ZonedDateTime.compare({ year: 2000, month: 5, day: 2, timeZone }, instance), "bare date-time string is not a time zone (arg 1)");
+assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(instance, { year: 2000, month: 5, day: 2, timeZone }), "bare date-time string is not a time zone (arg 2)");
+
+[
+ "2021-08-19T17:30-07:00:01",
+ "2021-08-19T17:30-07:00:00",
+ "2021-08-19T17:30-07:00:00.1",
+ "2021-08-19T17:30-07:00:00.0",
+ "2021-08-19T17:30-07:00:00.01",
+ "2021-08-19T17:30-07:00:00.00",
+ "2021-08-19T17:30-07:00:00.001",
+ "2021-08-19T17:30-07:00:00.000",
+ "2021-08-19T17:30-07:00:00.0001",
+ "2021-08-19T17:30-07:00:00.0000",
+ "2021-08-19T17:30-07:00:00.00001",
+ "2021-08-19T17:30-07:00:00.00000",
+ "2021-08-19T17:30-07:00:00.000001",
+ "2021-08-19T17:30-07:00:00.000000",
+ "2021-08-19T17:30-07:00:00.0000001",
+ "2021-08-19T17:30-07:00:00.0000000",
+ "2021-08-19T17:30-07:00:00.00000001",
+ "2021-08-19T17:30-07:00:00.00000000",
+ "2021-08-19T17:30-07:00:00.000000001",
+ "2021-08-19T17:30-07:00:00.000000000",
+].forEach((timeZone) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.ZonedDateTime.compare({ year: 2000, month: 5, day: 2, timeZone }, instance),
+ `ISO string ${timeZone} with a sub-minute offset is not a valid time zone (arg 1)`
+ );
+ assert.throws(
+ RangeError,
+ () => Temporal.ZonedDateTime.compare(instance, { year: 2000, month: 5, day: 2, timeZone }),
+ `ISO string ${timeZone} with a sub-minute offset is not a valid time zone (arg 2)`
+ );
+});
+
+// The following are all valid strings so should not throw:
+
+[
+ "2021-08-19T17:30Z",
+ "2021-08-19T17:30-07:00",
+ "2021-08-19T17:30[UTC]",
+ "2021-08-19T17:30Z[UTC]",
+ "2021-08-19T17:30-07:00[UTC]",
+].forEach((timeZone) => {
+ Temporal.ZonedDateTime.compare({ year: 2000, month: 5, day: 2, timeZone }, instance);
+ Temporal.ZonedDateTime.compare(instance, { year: 2000, month: 5, day: 2, timeZone });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-string-leap-second.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-string-leap-second.js
new file mode 100644
index 0000000000..568263d96e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-string-leap-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.zoneddatetime.compare
+description: Leap second is a valid ISO string for TimeZone
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(1588377600_000_000_000n, "UTC");
+
+let timeZone = "2016-12-31T23:59:60+00:00[UTC]";
+
+const result1 = Temporal.ZonedDateTime.compare({ year: 2020, month: 5, day: 2, timeZone }, instance);
+assert.sameValue(result1, 0, "leap second is a valid ISO string for TimeZone (first argument)");
+const result2 = Temporal.ZonedDateTime.compare(instance, { year: 2020, month: 5, day: 2, timeZone });
+assert.sameValue(result2, 0, "leap second is a valid ISO string for TimeZone (second argument)");
+
+timeZone = "2021-08-19T17:30:45.123456789+23:59[+23:59:60]";
+assert.throws(RangeError, () => Temporal.ZonedDateTime.compare({ year: 2020, month: 5, day: 2, timeZone }, instance), "leap second in time zone name not valid (first argument)");
+assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(instance, { year: 2020, month: 5, day: 2, timeZone }), "leap second in time zone name not valid (second argument)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-string-multiple-offsets.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-string-multiple-offsets.js
new file mode 100644
index 0000000000..8ce09f90e6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-string-multiple-offsets.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.zoneddatetime.compare
+description: Time zone parsing from ISO strings uses the bracketed offset, not the ISO string offset
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(1588371240_000_000_000n, "+01:46");
+
+const timeZone = "2021-08-19T17:30:45.123456789-12:12[+01:46]";
+
+const result1 = Temporal.ZonedDateTime.compare({ year: 2020, month: 5, day: 2, timeZone }, instance);
+assert.sameValue(result1, 0, "Time zone string determined from bracket name (first argument)");
+const result2 = Temporal.ZonedDateTime.compare(instance, { year: 2020, month: 5, day: 2, timeZone });
+assert.sameValue(result2, 0, "Time zone string determined from bracket name (second argument)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-string-year-zero.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-string-year-zero.js
new file mode 100644
index 0000000000..5de43c258c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-string-year-zero.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.zoneddatetime.compare
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(0n, new Temporal.TimeZone("UTC"));
+const invalidStrings = [
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+invalidStrings.forEach((timeZone) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.ZonedDateTime.compare({ year: 2020, month: 5, day: 2, timeZone }, datetime),
+ "reject minus zero as extended year (first argument)"
+ );
+ assert.throws(
+ RangeError,
+ () => Temporal.ZonedDateTime.compare(datetime, { year: 2020, month: 5, day: 2, timeZone }),
+ "reject minus zero as extended year (second argument)"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-string.js
new file mode 100644
index 0000000000..6b62326966
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-string.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.zoneddatetime.compare
+description: Time zone IDs are valid input for a time zone
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getPossibleInstantsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getPossibleInstantsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be looked up");
+ },
+});
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+
+["UTC", "+01:30"].forEach((timeZone) => {
+ const epoch = new Temporal.ZonedDateTime(0n, new Temporal.TimeZone(timeZone));
+
+ // These should be valid input and not throw
+ Temporal.ZonedDateTime.compare({ year: 2020, month: 5, day: 2, timeZone }, epoch);
+ Temporal.ZonedDateTime.compare(epoch, { year: 2020, month: 5, day: 2, timeZone });
+});
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", getPossibleInstantsForOriginal);
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-wrong-type.js
new file mode 100644
index 0000000000..ec7b15d19c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-wrong-type.js
@@ -0,0 +1,48 @@
+// |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.zoneddatetime.compare
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or object for TimeZone
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(0n, new Temporal.TimeZone("UTC"));
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [19761118, "number that would convert to a valid ISO string in other contexts"],
+ [1n, "bigint"],
+];
+
+for (const [timeZone, description] of primitiveTests) {
+ assert.throws(
+ typeof timeZone === 'string' ? RangeError : TypeError,
+ () => Temporal.ZonedDateTime.compare({ year: 2020, month: 5, day: 2, timeZone }, datetime),
+ `${description} does not convert to a valid ISO string (first argument)`
+ );
+ assert.throws(
+ typeof timeZone === 'string' ? RangeError : TypeError,
+ () => Temporal.ZonedDateTime.compare(datetime, { year: 2020, month: 5, day: 2, timeZone }),
+ `${description} does not convert to a valid ISO string (second argument)`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "object not implementing time zone protocol"],
+ [new Temporal.Calendar("iso8601"), "calendar instance"],
+];
+
+for (const [timeZone, description] of typeErrorTests) {
+ assert.throws(TypeError, () => Temporal.ZonedDateTime.compare({ year: 2020, month: 5, day: 2, timeZone }, datetime), `${description} is not a valid object and does not convert to a string (first argument)`);
+ assert.throws(TypeError, () => Temporal.ZonedDateTime.compare(datetime, { year: 2020, month: 5, day: 2, timeZone }), `${description} is not a valid object and does not convert to a string (second argument)`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..62c5d7c653
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-string-calendar-annotation.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.zoneddatetime.compare
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["1970-01-01T00:00[UTC][u-ca=iso8601]", "without !"],
+ ["1970-01-01T00:00[UTC][!u-ca=iso8601]", "with !"],
+ ["1970-01-01T00:00[UTC][u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.ZonedDateTime.compare(arg, arg);
+
+ assert.sameValue(
+ result,
+ 0,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..9a945a0b99
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-string-critical-unknown-annotation.js
@@ -0,0 +1,33 @@
+// |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.zoneddatetime.compare
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01T00:00[UTC][!foo=bar]",
+ "1970-01-01T00:00[UTC][!foo=bar][u-ca=iso8601]",
+ "1970-01-01T00:00[UTC][u-ca=iso8601][!foo=bar]",
+ "1970-01-01T00:00[foo=bar][!_foo-bar0=Dont-Ignore-This-99999999999]",
+];
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.ZonedDateTime.compare(arg, datetime),
+ `reject unknown annotation with critical flag: ${arg} (first argument)`
+ );
+ assert.throws(
+ RangeError,
+ () => Temporal.ZonedDateTime.compare(datetime, arg),
+ `reject unknown annotation with critical flag: ${arg} (second argument)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..565f8a1a69
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-string-date-with-utc-offset.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.zoneddatetime.compare
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+---*/
+
+const validStrings = [
+ "1970-01-01T00Z[UTC]",
+ "1970-01-01T00Z[!UTC]",
+ "1970-01-01T00+00:00[UTC]",
+ "1970-01-01T00+00:00[!UTC]",
+];
+
+for (const arg of validStrings) {
+ const result = Temporal.ZonedDateTime.compare(arg, arg);
+
+ assert.sameValue(
+ result,
+ 0,
+ `"${arg}" is a valid UTC offset with time for ZonedDateTime`
+ );
+}
+
+const invalidStrings = [
+ "2022-09-15Z[UTC]",
+ "2022-09-15Z[Europe/Vienna]",
+ "2022-09-15+00:00[UTC]",
+ "2022-09-15-02:30[America/St_Johns]",
+];
+const datetime = new Temporal.ZonedDateTime(0n, "UTC");
+
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => Temporal.ZonedDateTime.compare(arg, datetime),
+ `"${arg}" UTC offset without time is not valid for ZonedDateTime (first argument)`
+ );
+ assert.throws(
+ RangeError,
+ () => Temporal.ZonedDateTime.compare(datetime, arg),
+ `"${arg}" UTC offset without time is not valid for ZonedDateTime (second argument)`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..4e14c60039
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-string-multiple-calendar.js
@@ -0,0 +1,34 @@
+// |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.zoneddatetime.compare
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01T00:00[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00[UTC][!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01T00:00[UTC][u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+];
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.ZonedDateTime.compare(arg, datetime),
+ `reject more than one calendar annotation if any critical: ${arg} (first argument)`
+ );
+ assert.throws(
+ RangeError,
+ () => Temporal.ZonedDateTime.compare(datetime, arg),
+ `reject more than one calendar annotation if any critical: ${arg} (second argument)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..e94aa815e0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-string-multiple-time-zone.js
@@ -0,0 +1,34 @@
+// |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.zoneddatetime.compare
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "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 datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.ZonedDateTime.compare(arg, datetime),
+ `reject more than one time zone annotation: ${arg} (first argument)`
+ );
+ assert.throws(
+ RangeError,
+ () => Temporal.ZonedDateTime.compare(datetime, arg),
+ `reject more than one time zone annotation: ${arg} (second argument)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-string-time-separators.js
new file mode 100644
index 0000000000..1f7ffdae89
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/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.zoneddatetime.compare
+description: Time separator in string argument can vary
+features: [Temporal]
+---*/
+
+const epoch = new Temporal.ZonedDateTime(0n, "UTC");
+const tests = [
+ ["1970-01-01T00:00+00:00[UTC]", "uppercase T"],
+ ["1970-01-01t00:00+00:00[UTC]", "lowercase T"],
+ ["1970-01-01 00:00+00:00[UTC]", "space between date and time"],
+];
+
+tests.forEach(([arg, description]) => {
+ assert.sameValue(
+ Temporal.ZonedDateTime.compare(arg, epoch),
+ 0,
+ `variant time separators (${description}), first argument`
+ );
+
+ assert.sameValue(
+ Temporal.ZonedDateTime.compare(epoch, arg),
+ 0,
+ `variant time separators (${description}), second argument`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..ade9a8c0ac
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-string-time-zone-annotation.js
@@ -0,0 +1,36 @@
+// |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.zoneddatetime.compare
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["1970-01-01T00:00[UTC]", "named, with no offset"],
+ ["1970-01-01T00:00[!UTC]", "named, with ! and no offset"],
+ ["1970-01-01T00:00[+00:00]", "numeric, with no offset"],
+ ["1970-01-01T00:00[!+00:00]", "numeric, with ! and no offset"],
+ ["1970-01-01T00:00Z[UTC]", "named, with Z"],
+ ["1970-01-01T00:00Z[!UTC]", "named, with Z and !"],
+ ["1970-01-01T00:00Z[+00:00]", "numeric, with Z"],
+ ["1970-01-01T00:00Z[!+00:00]", "numeric, with Z and !"],
+ ["1970-01-01T00:00+00:00[UTC]", "named, with offset"],
+ ["1970-01-01T00:00+00:00[!UTC]", "named, with offset and !"],
+ ["1970-01-01T00:00+00:00[+00:00]", "numeric, with offset"],
+ ["1970-01-01T00:00+00:00[!+00:00]", "numeric, with offset and !"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.ZonedDateTime.compare(arg, arg);
+
+ assert.sameValue(
+ result,
+ 0,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..b91d5a940a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-string-unknown-annotation.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.zoneddatetime.compare
+description: Various forms of unknown annotation
+features: [Temporal]
+---*/
+
+const tests = [
+ ["1970-01-01T00:00[UTC][foo=bar]", "with time zone"],
+ ["1970-01-01T00:00[UTC][foo=bar][u-ca=iso8601]", "before calendar"],
+ ["1970-01-01T00:00[UTC][u-ca=iso8601][foo=bar]", "after calendar"],
+ ["1970-01-01T00:00[UTC][foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.ZonedDateTime.compare(arg, arg);
+
+ assert.sameValue(
+ result,
+ 0,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-wrong-type.js
new file mode 100644
index 0000000000..2ee36a11a2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-wrong-type.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.zoneddatetime.compare
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for ZonedDateTime
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const other = new Temporal.ZonedDateTime(0n, timeZone);
+
+const primitiveTests = [
+ [undefined, "undefined"],
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [19761118, "number that would convert to a valid ISO string in other contexts"],
+ [1n, "bigint"],
+];
+
+for (const [arg, description] of primitiveTests) {
+ assert.throws(
+ typeof arg === 'string' ? RangeError : TypeError,
+ () => Temporal.ZonedDateTime.compare(arg, other),
+ `${description} does not convert to a valid ISO string (first argument)`
+ );
+ assert.throws(
+ typeof arg === 'string' ? RangeError : TypeError,
+ () => Temporal.ZonedDateTime.compare(other, arg),
+ `${description} does not convert to a valid ISO string (second argument)`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.ZonedDateTime, "Temporal.ZonedDateTime, object"],
+ [Temporal.ZonedDateTime.prototype, "Temporal.ZonedDateTime.prototype, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => Temporal.ZonedDateTime.compare(arg, other), `${description} is not a valid property bag and does not convert to a string (first argument)`);
+ assert.throws(TypeError, () => Temporal.ZonedDateTime.compare(other, arg), `${description} is not a valid property bag and does not convert to a string (second argument)`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/builtin.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/builtin.js
new file mode 100644
index 0000000000..e21ff70287
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/builtin.js
@@ -0,0 +1,33 @@
+// |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.zoneddatetime.compare
+description: Tests that Temporal.ZonedDateTime.compare meets the requirements for built-in objects
+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.ZonedDateTime.compare),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.compare),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.compare),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.ZonedDateTime.compare.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/calendar-datefromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..3132abdbdd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/calendar-datefromfields-called-with-null-prototype-fields.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.zoneddatetime.compare
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const timeZone = new Temporal.TimeZone("UTC");
+const arg1 = { year: 2000, month: 5, day: 2, timeZone, calendar };
+const arg2 = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, timeZone);
+
+Temporal.ZonedDateTime.compare(arg1, arg2);
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar (first argument)");
+
+calendar.dateFromFieldsCallCount = 0;
+
+Temporal.ZonedDateTime.compare(arg2, arg1);
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar (second argument)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/calendar-fields-iterable.js
new file mode 100644
index 0000000000..1a7101d1b8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/calendar-fields-iterable.js
@@ -0,0 +1,41 @@
+// |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.zoneddatetime.compare
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.zoneddatetime.compare steps 1–2:
+ 1. Set _one_ to ? ToTemporalZonedDateTime(_one_).
+ 2. Set _two_ to ? ToTemporalZonedDateTime(_two_).
+ sec-temporal-totemporalzoneddatetime step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"hour"*, *"microsecond"*, *"millisecond"*, *"minute"*, *"month"*, *"monthCode"*, *"nanosecond"*, *"second"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+Temporal.ZonedDateTime.compare(
+ { year: 2000, month: 5, day: 2, timeZone: "UTC", calendar: calendar1 },
+ { year: 2001, month: 6, day: 3, timeZone: "UTC", calendar: calendar2 },
+);
+
+assert.sameValue(calendar1.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar1.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar1.iteratorExhausted[0], "iterated through the whole iterable");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/calendar-temporal-object.js
new file mode 100644
index 0000000000..b3c5353d7f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/calendar-temporal-object.js
@@ -0,0 +1,32 @@
+// |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.zoneddatetime.compare
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.zoneddatetime.compare steps 1–2:
+ 1. Set _one_ to ? ToTemporalZonedDateTime(_one_).
+ 2. Set _two_ to ? ToTemporalZonedDateTime(_two_).
+ sec-temporal-totemporalzoneddatetime step 2.b:
+ b. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ Temporal.ZonedDateTime.compare(
+ { year: 2000, month: 5, day: 2, timeZone: "UTC", calendar: temporalObject },
+ { year: 2001, month: 6, day: 3, timeZone: "UTC", calendar: temporalObject },
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..15661422e1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/constructor-in-calendar-fields.js
@@ -0,0 +1,19 @@
+// |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.zoneddatetime.prototype.compare
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const timeZone = 'Europe/Paris'
+const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar, timeZone: timeZone };
+const datetime = Temporal.ZonedDateTime.from({ year: 2023, month: 5, monthCode: 'M05', day: 15, timeZone: 'Europe/Paris' });
+
+assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(arg, datetime));
+assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(datetime, arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/duplicate-calendar-fields.js
new file mode 100644
index 0000000000..ca8e7ba69b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/duplicate-calendar-fields.js
@@ -0,0 +1,22 @@
+// |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.zoneddatetime.prototype.compare
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+
+for (const extra_fields of [['foo', 'foo'], ['day'], ['hour'], ['microsecond'], ['millisecond'], ['minute'], ['month'], ['monthCode'], ['nanosecond'], ['second'], ['year'], ['timeZone'], ['offset']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const timeZone = 'Europe/Paris'
+ const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar, timeZone: timeZone };
+ const datetime = Temporal.ZonedDateTime.from({year: 2023, month: 5, monthCode: 'M05', day: 15, timeZone: 'Europe/Paris'});
+
+ assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(arg, datetime));
+ assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(datetime, arg));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/exhaustive.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/exhaustive.js
new file mode 100644
index 0000000000..f2c7bb3e29
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/exhaustive.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.zoneddatetime.compare
+description: Tests for compare() with each possible outcome
+features: [Temporal]
+---*/
+
+const tz1 = "UTC";
+const tz2 = "-00:30";
+const cal1 = "iso8601";
+const cal2 = new (class extends Temporal.Calendar { id = "custom"; })("iso8601");
+
+assert.sameValue(
+ Temporal.ZonedDateTime.compare(new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, tz1, cal1), new Temporal.ZonedDateTime(500_000_000_000_000_000n, tz2, cal2)),
+ 1,
+ ">"
+);
+assert.sameValue(
+ Temporal.ZonedDateTime.compare(new Temporal.ZonedDateTime(-1000n, tz1, cal1), new Temporal.ZonedDateTime(1000n, tz2, cal2)),
+ -1,
+ "<"
+);
+assert.sameValue(
+ Temporal.ZonedDateTime.compare(new Temporal.ZonedDateTime(123_456_789n, tz1, cal1), new Temporal.ZonedDateTime(123_456_789n, tz2, cal2)),
+ 0,
+ "="
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..34ffba6689
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/infinity-throws-rangeerror.js
@@ -0,0 +1,33 @@
+// |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.
+
+/*---
+description: Throws if any value in a property bag for either argument is Infinity or -Infinity
+esid: sec-temporal.zoneddatetime.compare
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const other = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+const base = { year: 2000, month: 5, day: 2, hour: 15, minute: 30, second: 45, millisecond: 987, microsecond: 654, nanosecond: 321, timeZone: "UTC" };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((prop) => {
+ assert.throws(RangeError, () => Temporal.ZonedDateTime.compare({ ...base, [prop]: inf }, other), `${prop} property cannot be ${inf}`);
+
+ assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(other, { ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls1 = [];
+ const obj1 = TemporalHelpers.toPrimitiveObserver(calls1, inf, prop);
+ assert.throws(RangeError, () => Temporal.ZonedDateTime.compare({ ...base, [prop]: obj1 }, other));
+ assert.compareArray(calls1, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+
+ const calls2 = [];
+ const obj2 = TemporalHelpers.toPrimitiveObserver(calls2, inf, prop);
+ assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(other, { ...base, [prop]: obj2 }));
+ assert.compareArray(calls2, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/leap-second.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/leap-second.js
new file mode 100644
index 0000000000..8e677091ce
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/leap-second.js
@@ -0,0 +1,23 @@
+// |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.zoneddatetime.compare
+description: Leap second is a valid ISO string for ZonedDateTime
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_483_228_799_000_000_000n, new Temporal.TimeZone("UTC"));
+
+let arg = "2016-12-31T23:59:60+00:00[UTC]";
+const result1 = Temporal.ZonedDateTime.compare(arg, datetime);
+assert.sameValue(result1, 0, "leap second is a valid ISO string for ZonedDateTime (first argument)");
+const result2 = Temporal.ZonedDateTime.compare(datetime, arg);
+assert.sameValue(result2, 0, "leap second is a valid ISO string for ZonedDateTime (second argument)");
+
+arg = "2000-05-02T12:34:56+23:59[+23:59:60]";
+assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(arg, datetime), "leap second in time zone name not valid (first argument)");
+assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(datetime, arg), "leap second in time zone name not valid (second argument)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/length.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/length.js
new file mode 100644
index 0000000000..24b9580c43
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/length.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.zoneddatetime.compare
+description: Temporal.ZonedDateTime.compare.length is 2
+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.ZonedDateTime.compare, "length", {
+ value: 2,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/name.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/name.js
new file mode 100644
index 0000000000..27b064c466
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/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.zoneddatetime.compare
+description: Temporal.ZonedDateTime.compare.name is "compare"
+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.ZonedDateTime.compare, "name", {
+ value: "compare",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/not-a-constructor.js
new file mode 100644
index 0000000000..41e48e7713
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/not-a-constructor.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.zoneddatetime.compare
+description: Temporal.ZonedDateTime.compare 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.ZonedDateTime.compare();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.ZonedDateTime.compare), false,
+ "isConstructor(Temporal.ZonedDateTime.compare)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/order-of-operations.js
new file mode 100644
index 0000000000..5d712d2d69
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/order-of-operations.js
@@ -0,0 +1,192 @@
+// |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.zoneddatetime.compare
+description: Properties on objects passed to compare() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "get one.calendar",
+ "has one.calendar.dateAdd",
+ "has one.calendar.dateFromFields",
+ "has one.calendar.dateUntil",
+ "has one.calendar.day",
+ "has one.calendar.dayOfWeek",
+ "has one.calendar.dayOfYear",
+ "has one.calendar.daysInMonth",
+ "has one.calendar.daysInWeek",
+ "has one.calendar.daysInYear",
+ "has one.calendar.fields",
+ "has one.calendar.id",
+ "has one.calendar.inLeapYear",
+ "has one.calendar.mergeFields",
+ "has one.calendar.month",
+ "has one.calendar.monthCode",
+ "has one.calendar.monthDayFromFields",
+ "has one.calendar.monthsInYear",
+ "has one.calendar.weekOfYear",
+ "has one.calendar.year",
+ "has one.calendar.yearMonthFromFields",
+ "has one.calendar.yearOfWeek",
+ "get one.calendar.dateFromFields",
+ "get one.calendar.fields",
+ "call one.calendar.fields",
+ // PrepareTemporalFields
+ "get one.day",
+ "get one.day.valueOf",
+ "call one.day.valueOf",
+ "get one.hour",
+ "get one.hour.valueOf",
+ "call one.hour.valueOf",
+ "get one.microsecond",
+ "get one.microsecond.valueOf",
+ "call one.microsecond.valueOf",
+ "get one.millisecond",
+ "get one.millisecond.valueOf",
+ "call one.millisecond.valueOf",
+ "get one.minute",
+ "get one.minute.valueOf",
+ "call one.minute.valueOf",
+ "get one.month",
+ "get one.month.valueOf",
+ "call one.month.valueOf",
+ "get one.monthCode",
+ "get one.monthCode.toString",
+ "call one.monthCode.toString",
+ "get one.nanosecond",
+ "get one.nanosecond.valueOf",
+ "call one.nanosecond.valueOf",
+ "get one.offset",
+ "get one.offset.toString",
+ "call one.offset.toString",
+ "get one.second",
+ "get one.second.valueOf",
+ "call one.second.valueOf",
+ "get one.timeZone",
+ "get one.year",
+ "get one.year.valueOf",
+ "call one.year.valueOf",
+ "has one.timeZone.getOffsetNanosecondsFor",
+ "has one.timeZone.getPossibleInstantsFor",
+ "has one.timeZone.id",
+ // InterpretTemporalDateTimeFields
+ "call one.calendar.dateFromFields",
+ // InterpretISODateTimeOffset
+ "get one.timeZone.getOffsetNanosecondsFor",
+ "get one.timeZone.getPossibleInstantsFor",
+ "call one.timeZone.getPossibleInstantsFor",
+ "call one.timeZone.getOffsetNanosecondsFor",
+ // Same set of operations, for the other argument:
+ "get two.calendar",
+ "has two.calendar.dateAdd",
+ "has two.calendar.dateFromFields",
+ "has two.calendar.dateUntil",
+ "has two.calendar.day",
+ "has two.calendar.dayOfWeek",
+ "has two.calendar.dayOfYear",
+ "has two.calendar.daysInMonth",
+ "has two.calendar.daysInWeek",
+ "has two.calendar.daysInYear",
+ "has two.calendar.fields",
+ "has two.calendar.id",
+ "has two.calendar.inLeapYear",
+ "has two.calendar.mergeFields",
+ "has two.calendar.month",
+ "has two.calendar.monthCode",
+ "has two.calendar.monthDayFromFields",
+ "has two.calendar.monthsInYear",
+ "has two.calendar.weekOfYear",
+ "has two.calendar.year",
+ "has two.calendar.yearMonthFromFields",
+ "has two.calendar.yearOfWeek",
+ "get two.calendar.dateFromFields",
+ "get two.calendar.fields",
+ "call two.calendar.fields",
+ // PrepareTemporalFields
+ "get two.day",
+ "get two.day.valueOf",
+ "call two.day.valueOf",
+ "get two.hour",
+ "get two.hour.valueOf",
+ "call two.hour.valueOf",
+ "get two.microsecond",
+ "get two.microsecond.valueOf",
+ "call two.microsecond.valueOf",
+ "get two.millisecond",
+ "get two.millisecond.valueOf",
+ "call two.millisecond.valueOf",
+ "get two.minute",
+ "get two.minute.valueOf",
+ "call two.minute.valueOf",
+ "get two.month",
+ "get two.month.valueOf",
+ "call two.month.valueOf",
+ "get two.monthCode",
+ "get two.monthCode.toString",
+ "call two.monthCode.toString",
+ "get two.nanosecond",
+ "get two.nanosecond.valueOf",
+ "call two.nanosecond.valueOf",
+ "get two.offset",
+ "get two.offset.toString",
+ "call two.offset.toString",
+ "get two.second",
+ "get two.second.valueOf",
+ "call two.second.valueOf",
+ "get two.timeZone",
+ "get two.year",
+ "get two.year.valueOf",
+ "call two.year.valueOf",
+ "has two.timeZone.getOffsetNanosecondsFor",
+ "has two.timeZone.getPossibleInstantsFor",
+ "has two.timeZone.id",
+ // InterpretTemporalDateTimeFields
+ "call two.calendar.dateFromFields",
+ // InterpretISODateTimeOffset
+ "get two.timeZone.getOffsetNanosecondsFor",
+ "get two.timeZone.getPossibleInstantsFor",
+ "call two.timeZone.getPossibleInstantsFor",
+ "call two.timeZone.getOffsetNanosecondsFor",
+];
+const actual = [];
+
+const one = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2001,
+ month: 5,
+ monthCode: "M05",
+ day: 2,
+ hour: 6,
+ minute: 54,
+ second: 32,
+ millisecond: 987,
+ microsecond: 654,
+ nanosecond: 321,
+ offset: "+00:00",
+ calendar: TemporalHelpers.calendarObserver(actual, "one.calendar"),
+ timeZone: TemporalHelpers.timeZoneObserver(actual, "one.timeZone"),
+}, "one");
+
+const two = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2014,
+ month: 7,
+ monthCode: "M07",
+ day: 19,
+ hour: 12,
+ minute: 34,
+ second: 56,
+ millisecond: 123,
+ microsecond: 456,
+ nanosecond: 789,
+ offset: "+00:00",
+ calendar: TemporalHelpers.calendarObserver(actual, "two.calendar"),
+ timeZone: TemporalHelpers.timeZoneObserver(actual, "two.timeZone"),
+}, "two");
+
+Temporal.ZonedDateTime.compare(one, two);
+assert.compareArray(actual, expected, "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/prop-desc.js
new file mode 100644
index 0000000000..bfe1ea9659
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/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.zoneddatetime.compare
+description: The "compare" property of Temporal.ZonedDateTime
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.ZonedDateTime.compare,
+ "function",
+ "`typeof ZonedDateTime.compare` is `function`"
+);
+
+verifyProperty(Temporal.ZonedDateTime, "compare", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/proto-in-calendar-fields.js
new file mode 100644
index 0000000000..b0d2d1427d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/proto-in-calendar-fields.js
@@ -0,0 +1,19 @@
+// |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.zoneddatetime.prototype.compare
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const timeZone = 'Europe/Paris'
+const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar, timeZone: timeZone };
+const datetime = Temporal.ZonedDateTime.from({ year: 2023, month: 5, monthCode: 'M05', day: 15, timeZone: 'Europe/Paris' });
+
+assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(arg, datetime));
+assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(datetime, arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/read-time-fields-before-datefromfields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/read-time-fields-before-datefromfields.js
new file mode 100644
index 0000000000..72195dda75
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/read-time-fields-before-datefromfields.js
@@ -0,0 +1,30 @@
+// |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.zoneddatetime.compare
+description: The time fields are read from the object before being passed to dateFromFields().
+info: |
+ sec-temporal.zoneddatetime.compare steps 1–2:
+ 1. Set _one_ to ? ToTemporalZonedDateTime(_one_).
+ 2. Set _two_ to ? ToTemporalZonedDateTime(_two_).
+ sec-temporal-totemporalzoneddatetime step 2.j:
+ j. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+ sec-temporal-interprettemporaldatetimefields steps 1–2:
+ 1. Let _timeResult_ be ? ToTemporalTimeRecord(_fields_).
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarMakeInfinityTime();
+const result = Temporal.ZonedDateTime.compare(
+ { year: 2000, month: 5, day: 2, hour: 12, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321, timeZone: "UTC", calendar },
+ { year: 2000, month: 5, day: 2, hour: 6, minute: 54, second: 32, millisecond: 123, microsecond: 456, nanosecond: 789, timeZone: "UTC", calendar },
+);
+
+// will be 0 if the time fields are coerced to their max values due to Infinity
+assert.sameValue(result, 1, "comparison result");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/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/ZonedDateTime/compare/timezone-getpossibleinstantsfor-iterable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/timezone-getpossibleinstantsfor-iterable.js
new file mode 100644
index 0000000000..d4f71338e4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/timezone-getpossibleinstantsfor-iterable.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.zoneddatetime.compare
+description: An iterable returned from timeZone.getPossibleInstantsFor is consumed after each call
+info: |
+ sec-temporal.zoneddatetime.compare steps 1–2:
+ 1. Set _one_ to ? ToTemporalZonedDateTime(_one_).
+ 2. Set _two_ to ? ToTemporalZonedDateTime(_two_).
+ sec-temporal-totemporalzoneddatetime step 7:
+ 7. Let _epochNanoseconds_ be ? InterpretISODateTimeOffset(_result_.[[Year]], [...], _result_.[[Nanosecond]], _offsetNanoseconds_, _timeZone_, _disambiguation_, _offset_).
+ sec-temporal-interpretisodatetimeoffset step 7:
+ 7. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_).
+ sec-temporal-getpossibleinstantsfor step 2:
+ 2. Let _list_ be ? IterableToList(_possibleInstants_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected1 = [
+ "2000-05-02T00:00:00",
+];
+
+TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => {
+ Temporal.ZonedDateTime.compare(
+ { year: 2000, month: 5, day: 2, timeZone },
+ { year: 2001, month: 6, day: 3, timeZone: "UTC" },
+ );
+}, expected1);
+
+// Same, but on the other operand
+
+const expected2 = [
+ "2001-06-03T00:00:00",
+];
+
+TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => {
+ Temporal.ZonedDateTime.compare(
+ { year: 2000, month: 5, day: 2, timeZone: "UTC" },
+ { year: 2001, month: 6, day: 3, timeZone },
+ );
+}, expected2);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/year-zero.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/year-zero.js
new file mode 100644
index 0000000000..4bdd4cae93
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/year-zero.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.zoneddatetime.compare
+description: Negative zero, as an extended year, fails
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(0n, "UTC");
+const invalidStrings = [
+ "-0000000-01-01T00:02Z[UTC]",
+ "-0000000-01-01T00:02+00:00[UTC]",
+ "-0000000-01-01T00:02:00.000000000+00:00[UTC]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(RangeError,
+ () => Temporal.ZonedDateTime.compare(arg, instance),
+ "cannot use negative zero as extended year (first argument)"
+ );
+ assert.throws(RangeError,
+ () => Temporal.ZonedDateTime.compare(instance, arg),
+ "cannot use negative zero as extended year (second argument)"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/zoneddatetime-string-multiple-offsets.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/zoneddatetime-string-multiple-offsets.js
new file mode 100644
index 0000000000..a99ebbbc16
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/zoneddatetime-string-multiple-offsets.js
@@ -0,0 +1,37 @@
+// |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.zoneddatetime.compare
+description: Sub-minute offset trailing zeroes allowed in ISO string but not in bracketed offset
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(30_000_000_000n, "+01:35");
+let str = "1970-01-01T01:35:30+01:35:00.000000000[+01:35]";
+
+assert.sameValue(
+ Temporal.ZonedDateTime.compare(str, datetime),
+ 0,
+ "Time zone determined from bracket name (first argument)"
+);
+assert.sameValue(
+ Temporal.ZonedDateTime.compare(datetime, str),
+ 0,
+ "Time zone determined from bracket name (second argument)"
+);
+
+str = "1970-01-01T01:35:30+01:35:00.000000000[+01:35:00.000000000]";
+assert.throws(
+ RangeError,
+ () => Temporal.ZonedDateTime.compare(str, datetime),
+ "Trailing zeroes not allowed for sub-minute time zone identifiers (first argument)"
+);
+assert.throws(
+ RangeError,
+ () => Temporal.ZonedDateTime.compare(datetime, str),
+ "Trailing zeroes not allowed for sub-minute time zone identifiers (second argument)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/zoneddatetime-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/zoneddatetime-string.js
new file mode 100644
index 0000000000..73fa389248
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/zoneddatetime-string.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.zoneddatetime.compare
+description: Conversion of ISO date-time strings to Temporal.ZonedDateTime instances
+features: [Temporal]
+---*/
+
+const epoch = new Temporal.ZonedDateTime(0n, "UTC");
+const hourBefore = new Temporal.ZonedDateTime(-3600_000_000_000n, "UTC");
+
+let str = "1970-01-01T00:00";
+assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(str, epoch), "bare date-time string is not a ZonedDateTime (first argument)");
+assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(epoch, str), "bare date-time string is not a ZonedDateTime (second argument)");
+str = "1970-01-01T00:00Z";
+assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(str, epoch), "date-time + Z is not a ZonedDateTime (first argument)");
+assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(epoch, str), "date-time + Z is not a ZonedDateTime (second argument)");
+str = "1970-01-01T00:00+01:00";
+assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(str, epoch), "date-time + offset is not a ZonedDateTime (first argument)");
+assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(epoch, str), "date-time + offset is not a ZonedDateTime (second argument)");
+
+str = "1970-01-01T00:00[+01:00]";
+const result1 = Temporal.ZonedDateTime.compare(str, hourBefore);
+assert.sameValue(result1, 0, "date-time + IANA annotation preserves wall time in the time zone (first argument)");
+const result2 = Temporal.ZonedDateTime.compare(hourBefore, str);
+assert.sameValue(result2, 0, "date-time + IANA annotation preserves wall time in the time zone (second argument)");
+
+str = "1970-01-01T00:00Z[+01:00]";
+const result3 = Temporal.ZonedDateTime.compare(str, epoch);
+assert.sameValue(result3, 0, "date-time + Z + IANA annotation preserves exact time in the time zone (first argument)");
+const result4 = Temporal.ZonedDateTime.compare(epoch, str);
+assert.sameValue(result4, 0, "date-time + Z + IANA annotation preserves exact time in the time zone (second argument)");
+
+str = "1970-01-01T00:00+01:00[+01:00]";
+const result5 = Temporal.ZonedDateTime.compare(str, hourBefore);
+assert.sameValue(result5, 0, "date-time + offset + IANA annotation ensures both exact and wall time match (first argument)");
+const result6 = Temporal.ZonedDateTime.compare(hourBefore, str);
+assert.sameValue(result6, 0, "date-time + offset + IANA annotation ensures both exact and wall time match (second argument)");
+
+str = "1970-01-01T00:00-04:15[+01:00]";
+assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(str, epoch), "date-time + offset + IANA annotation throws if wall time and exact time mismatch (first argument)");
+assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(epoch, str), "date-time + offset + IANA annotation throws if wall time and exact time mismatch (second argument)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/constructor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/constructor.js
new file mode 100644
index 0000000000..af174cc625
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/constructor.js
@@ -0,0 +1,15 @@
+// |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.zoneddatetime
+description: Temporal.ZonedDateTime constructor cannot be called as a function
+info: |
+ 1. If NewTarget is undefined, throw a TypeError exception.
+features: [Temporal]
+---*/
+
+assert.throws(TypeError, () => Temporal.ZonedDateTime(0n, "UTC"));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..18f1c49471
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-builtin-calendar-no-array-iteration.js
@@ -0,0 +1,24 @@
+// |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.zoneddatetime.from
+description: >
+ Calling the method with a property bag argument with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const timeZone = "UTC";
+const arg = { year: 2000, month: 5, day: 2, hour: 21, minute: 43, second: 5, timeZone, calendar: "iso8601" };
+Temporal.ZonedDateTime.from(arg);
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-ambiguous-wall-clock-time.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-ambiguous-wall-clock-time.js
new file mode 100644
index 0000000000..87d3c908ac
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-ambiguous-wall-clock-time.js
@@ -0,0 +1,138 @@
+// |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.zoneddatetime.from
+description: >
+ Correct time zone calls are made when converting a ZonedDateTime-like property
+ bag denoting an ambiguous wall-clock time
+includes: [temporalHelpers.js, compareArray.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+
+const dstTimeZone = TemporalHelpers.springForwardFallBackTimeZone();
+const timeZone = TemporalHelpers.timeZoneObserver(actual, "timeZone", {
+ getOffsetNanosecondsFor: dstTimeZone.getOffsetNanosecondsFor.bind(dstTimeZone),
+ getPossibleInstantsFor: dstTimeZone.getPossibleInstantsFor.bind(dstTimeZone),
+});
+const calendar = TemporalHelpers.calendarObserver(actual, "calendar");
+
+const expected = [
+ // GetTemporalCalendarSlotValueWithISODefault
+ "has calendar.dateAdd",
+ "has calendar.dateFromFields",
+ "has calendar.dateUntil",
+ "has calendar.day",
+ "has calendar.dayOfWeek",
+ "has calendar.dayOfYear",
+ "has calendar.daysInMonth",
+ "has calendar.daysInWeek",
+ "has calendar.daysInYear",
+ "has calendar.fields",
+ "has calendar.id",
+ "has calendar.inLeapYear",
+ "has calendar.mergeFields",
+ "has calendar.month",
+ "has calendar.monthCode",
+ "has calendar.monthDayFromFields",
+ "has calendar.monthsInYear",
+ "has calendar.weekOfYear",
+ "has calendar.year",
+ "has calendar.yearMonthFromFields",
+ "has calendar.yearOfWeek",
+ // lookup in ToTemporalZonedDateTime
+ "get calendar.dateFromFields",
+ "get calendar.fields",
+ // CalendarFields
+ "call calendar.fields",
+ // ToTemporalTimeZoneSlotValue
+ "has timeZone.getOffsetNanosecondsFor",
+ "has timeZone.getPossibleInstantsFor",
+ "has timeZone.id",
+ // InterpretTemporalDateTimeFields
+ "call calendar.dateFromFields",
+ // lookup in ToTemporalZonedDateTime
+ "get timeZone.getOffsetNanosecondsFor",
+ "get timeZone.getPossibleInstantsFor",
+];
+
+Temporal.ZonedDateTime.from(
+ { year: 2000, month: 4, day: 2, hour: 2, minute: 30, offset: "-08:00", timeZone, calendar },
+ { offset: "use" }
+);
+assert.compareArray(actual, expected, "order of operations converting property bag at skipped wall-clock time with offset: use");
+actual.splice(0); // clear
+
+Temporal.ZonedDateTime.from(
+ { year: 2000, month: 4, day: 2, hour: 2, minute: 30, offset: "-08:00", timeZone, calendar },
+ { offset: "ignore" }
+);
+assert.compareArray(actual, expected.concat([
+ // InterpretISODateTimeOffset
+ "call timeZone.getPossibleInstantsFor",
+ // DisambiguatePossibleInstants
+ "call timeZone.getOffsetNanosecondsFor",
+ "call timeZone.getOffsetNanosecondsFor",
+ "call timeZone.getPossibleInstantsFor",
+]), "order of operations converting property bag at skipped wall-clock time with offset: ignore");
+actual.splice(0); // clear
+
+Temporal.ZonedDateTime.from(
+ { year: 2000, month: 4, day: 2, hour: 2, minute: 30, offset: "-08:00", timeZone, calendar },
+ { offset: "prefer" }
+);
+assert.compareArray(actual, expected.concat([
+ // InterpretISODateTimeOffset
+ "call timeZone.getPossibleInstantsFor",
+ // DisambiguatePossibleInstants
+ "call timeZone.getOffsetNanosecondsFor",
+ "call timeZone.getOffsetNanosecondsFor",
+ "call timeZone.getPossibleInstantsFor",
+]), "order of operations converting property bag at skipped wall-clock time with offset: prefer");
+actual.splice(0); // clear
+
+Temporal.ZonedDateTime.from(
+ { year: 2000, month: 10, day: 29, hour: 1, minute: 30, offset: "-08:00", timeZone, calendar },
+ { offset: "use" }
+);
+assert.compareArray(actual, expected, "order of operations converting property bag at repeated wall-clock time with offset: use");
+actual.splice(0); // clear
+
+Temporal.ZonedDateTime.from(
+ { year: 2000, month: 10, day: 29, hour: 1, minute: 30, offset: "-08:00", timeZone, calendar },
+ { offset: "ignore" }
+);
+assert.compareArray(actual, expected.concat([
+ // InterpretISODateTimeOffset
+ "call timeZone.getPossibleInstantsFor",
+]), "order of operations converting property bag at repeated wall-clock time with offset: ignore");
+actual.splice(0); // clear
+
+Temporal.ZonedDateTime.from(
+ { year: 2000, month: 10, day: 29, hour: 1, minute: 30, offset: "-08:00", timeZone, calendar },
+ { offset: "prefer" }
+);
+assert.compareArray(actual, expected.concat([
+ // InterpretISODateTimeOffset
+ "call timeZone.getPossibleInstantsFor",
+ "call timeZone.getOffsetNanosecondsFor",
+ "call timeZone.getOffsetNanosecondsFor",
+]), "order of operations converting property bag at repeated wall-clock time with offset: prefer");
+actual.splice(0); // clear
+
+Temporal.ZonedDateTime.from(
+ { year: 2000, month: 10, day: 29, hour: 1, minute: 30, offset: "-08:00", timeZone, calendar },
+ { offset: "reject" }
+);
+assert.compareArray(actual, expected.concat([
+ // InterpretISODateTimeOffset
+ "call timeZone.getPossibleInstantsFor",
+ "call timeZone.getOffsetNanosecondsFor",
+ "call timeZone.getOffsetNanosecondsFor",
+]), "order of operations converting property bag at repeated wall-clock time with offset: reject");
+actual.splice(0); // clear
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-calendar-case-insensitive.js
new file mode 100644
index 0000000000..82314be3f0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-calendar-case-insensitive.js
@@ -0,0 +1,18 @@
+// |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.zoneddatetime.from
+description: The calendar name is case-insensitive
+features: [Temporal]
+---*/
+
+const calendar = "IsO8601";
+
+const timeZone = new Temporal.TimeZone("UTC");
+const arg = { year: 1970, monthCode: "M01", day: 1, timeZone, calendar };
+const result = Temporal.ZonedDateTime.from(arg);
+assert.sameValue(result.calendarId, "iso8601", "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-calendar-leap-second.js
new file mode 100644
index 0000000000..8ee6bd1929
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-calendar-leap-second.js
@@ -0,0 +1,22 @@
+// |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.zoneddatetime.from
+description: Leap second is a valid ISO string for a calendar in a property bag
+features: [Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const calendar = "2016-12-31T23:59:60+00:00[UTC]";
+
+const arg = { year: 1970, monthCode: "M01", day: 1, timeZone, calendar };
+const result = Temporal.ZonedDateTime.from(arg);
+assert.sameValue(
+ result.calendarId,
+ "iso8601",
+ "leap second is a valid ISO string for calendar"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-calendar-number.js
new file mode 100644
index 0000000000..ecc9b11882
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-calendar-number.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.zoneddatetime.from
+description: A number as calendar in a property bag is not accepted
+features: [Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+
+const numbers = [
+ 1,
+ 19970327,
+ -19970327,
+ 1234567890,
+];
+
+for (const calendar of numbers) {
+ const arg = { year: 1970, monthCode: "M01", day: 1, timeZone, calendar };
+ assert.throws(
+ TypeError,
+ () => Temporal.ZonedDateTime.from(arg),
+ "Numbers cannot be used as a calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-calendar-string.js
new file mode 100644
index 0000000000..943c87ea45
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-calendar-string.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.zoneddatetime.from
+description: A calendar ID is valid input for Calendar
+features: [Temporal]
+---*/
+
+const calendar = "iso8601";
+
+const timeZone = new Temporal.TimeZone("UTC");
+const arg = { year: 1970, monthCode: "M01", day: 1, timeZone, calendar };
+const result = Temporal.ZonedDateTime.from(arg);
+assert.sameValue(result.calendarId, "iso8601", `Calendar created from string "${calendar}"`);
+assert.sameValue(result.getISOFields().calendar, "iso8601", "calendar slot stores a string");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-calendar-wrong-type.js
new file mode 100644
index 0000000000..54838e3b88
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-calendar-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.zoneddatetime.from
+description: >
+ Appropriate error thrown when a calendar property from a property bag cannot
+ be converted to a calendar object or string
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [calendar, description] of primitiveTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ typeof calendar === 'string' ? RangeError : TypeError,
+ () => Temporal.ZonedDateTime.from(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+ [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields()
+];
+
+for (const [calendar, description] of typeErrorTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(TypeError, () => Temporal.ZonedDateTime.from(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/ZonedDateTime/from/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-calendar-year-zero.js
new file mode 100644
index 0000000000..d736edeb21
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-calendar-year-zero.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.zoneddatetime.from
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T17:45",
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+01:00",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.ZonedDateTime.from(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-getpossibleinstantsfor-called-with-iso8601-calendar.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-getpossibleinstantsfor-called-with-iso8601-calendar.js
new file mode 100644
index 0000000000..41fdcd8e56
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-getpossibleinstantsfor-called-with-iso8601-calendar.js
@@ -0,0 +1,58 @@
+// |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.zoneddatetime.prototype.from
+description: >
+ Time zone's getPossibleInstantsFor is called with a PlainDateTime with the
+ built-in ISO 8601 calendar
+features: [Temporal]
+info: |
+ DisambiguatePossibleInstants:
+ 2. Let _n_ be _possibleInstants_'s length.
+ ...
+ 5. Assert: _n_ = 0.
+ ...
+ 19. If _disambiguation_ is *"earlier"*, then
+ ...
+ c. Let _earlierDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*).
+ d. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _earlierDateTime_).
+ ...
+ 20. Assert: _disambiguation_ is *"compatible"* or *"later"*.
+ ...
+ 23. Let _laterDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*).
+ 24. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _laterDateTime_).
+---*/
+
+class SkippedDateTime extends Temporal.TimeZone {
+ constructor() {
+ super("UTC");
+ this.calls = 0;
+ }
+
+ getPossibleInstantsFor(dateTime) {
+ // Calls occur in pairs. For the first one return no possible instants so
+ // that DisambiguatePossibleInstants will call it again
+ if (this.calls++ % 2 == 0) {
+ return [];
+ }
+
+ assert.sameValue(
+ dateTime.getISOFields().calendar,
+ "iso8601",
+ "getPossibleInstantsFor called with dateTime with built-in ISO 8601 calendar"
+ );
+ return super.getPossibleInstantsFor(dateTime);
+ }
+}
+
+const nonBuiltinISOCalendar = new Temporal.Calendar("iso8601");
+const timeZone = new SkippedDateTime();
+const arg = { year: 2000, month: 5, day: 2, timeZone, calendar: nonBuiltinISOCalendar };
+
+Temporal.ZonedDateTime.from(arg);
+
+assert.sameValue(timeZone.calls, 2, "getPossibleInstantsFor should have been called 2 times");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-invalid-offset-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-invalid-offset-string.js
new file mode 100644
index 0000000000..83d3754b1e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-invalid-offset-string.js
@@ -0,0 +1,35 @@
+// |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.zoneddatetime.from
+description: Property bag with offset property is rejected if offset is in the wrong format
+features: [Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+
+const offsetOptions = ['use', 'prefer', 'ignore', 'reject'];
+
+const badOffsets = [
+ "00:00", // missing sign
+ "+0", // too short
+ "-000:00", // too long
+ 0, // must be a string
+ null, // must be a string
+ true, // must be a string
+ 1000n, // must be a string
+];
+offsetOptions.forEach((offsetOption) => {
+ badOffsets.forEach((offset) => {
+ const arg = { year: 2021, month: 10, day: 28, offset, timeZone };
+ assert.throws(
+ typeof(offset) === 'string' ? RangeError : TypeError,
+ () => Temporal.ZonedDateTime.from(arg, { offset: offsetOption }),
+ `"${offset} is not a valid offset string (with offset option ${offsetOption})`
+ );
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-offset-not-agreeing-with-timezone.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-offset-not-agreeing-with-timezone.js
new file mode 100644
index 0000000000..fe08a8c7e5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-offset-not-agreeing-with-timezone.js
@@ -0,0 +1,16 @@
+// |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.zoneddatetime.from
+description: Property bag with offset property is rejected if offset does not agree with time zone
+features: [Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("+01:00");
+
+const properties = { year: 2021, month: 10, day: 28, offset: "-07:00", timeZone };
+assert.throws(RangeError, () => Temporal.ZonedDateTime.from(properties), "offset property not matching time zone is rejected");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..ec3e613a44
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,18 @@
+// |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.zoneddatetime.from
+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].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+
+ assert.throws(RangeError, () => Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, hour: 12, timeZone }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..aee08d30bf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-getoffsetnanosecondsfor-not-callable.js
@@ -0,0 +1,35 @@
+// |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.zoneddatetime.from
+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");
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, hour: 12, offset: "+00:00", timeZone }, { offset: "prefer" }),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError (in offset=prefer and no disambiguation case)`
+ );
+
+ const badTimeZone = {
+ getPossibleInstantsFor() { return []; },
+ getOffsetNanosecondsFor: notCallable,
+ };
+ assert.throws(
+ TypeError,
+ () => Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, hour: 12, offset: "+00:00", timeZone: badTimeZone }, { offset: "ignore" }),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError (in offset=ignore and no possible instants case)`
+ );
+ assert.throws(
+ TypeError,
+ () => Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, hour: 12, offset: "+00:00", timeZone: badTimeZone }, { offset: "prefer" }),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError (in offset=prefer and no possible instants case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..1909eec0b4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,18 @@
+// |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.zoneddatetime.from
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+
+ assert.throws(RangeError, () => Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, hour: 12, timeZone }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..91995cd454
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,35 @@
+// |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.zoneddatetime.from
+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);
+
+ assert.throws(
+ TypeError,
+ () => Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, hour: 12, timeZone }),
+ `invalid offset: ${String(wrongOffset)} (${typeof wrongOffset})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-string-datetime.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-string-datetime.js
new file mode 100644
index 0000000000..53a73434f7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-string-datetime.js
@@ -0,0 +1,63 @@
+// |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.zoneddatetime.from
+description: Conversion of ISO date-time strings to Temporal.TimeZone instances
+features: [Temporal]
+---*/
+
+let timeZone = "2021-08-19T17:30";
+assert.throws(RangeError, () => Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, timeZone }), "bare date-time string is not a time zone");
+
+[
+ "2021-08-19T17:30-07:00:01",
+ "2021-08-19T17:30-07:00:00",
+ "2021-08-19T17:30-07:00:00.1",
+ "2021-08-19T17:30-07:00:00.0",
+ "2021-08-19T17:30-07:00:00.01",
+ "2021-08-19T17:30-07:00:00.00",
+ "2021-08-19T17:30-07:00:00.001",
+ "2021-08-19T17:30-07:00:00.000",
+ "2021-08-19T17:30-07:00:00.0001",
+ "2021-08-19T17:30-07:00:00.0000",
+ "2021-08-19T17:30-07:00:00.00001",
+ "2021-08-19T17:30-07:00:00.00000",
+ "2021-08-19T17:30-07:00:00.000001",
+ "2021-08-19T17:30-07:00:00.000000",
+ "2021-08-19T17:30-07:00:00.0000001",
+ "2021-08-19T17:30-07:00:00.0000000",
+ "2021-08-19T17:30-07:00:00.00000001",
+ "2021-08-19T17:30-07:00:00.00000000",
+ "2021-08-19T17:30-07:00:00.000000001",
+ "2021-08-19T17:30-07:00:00.000000000",
+].forEach((timeZone) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, timeZone }),
+ `ISO string ${timeZone} with a sub-minute offset is not a valid time zone`
+ );
+});
+
+timeZone = "2021-08-19T17:30Z";
+const result1 = Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, timeZone });
+assert.sameValue(result1.timeZoneId, "UTC", "date-time + Z is UTC time zone");
+
+timeZone = "2021-08-19T17:30-07:00";
+const result2 = Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, timeZone });
+assert.sameValue(result2.timeZoneId, "-07:00", "date-time + offset is the offset time zone");
+
+timeZone = "2021-08-19T17:30[UTC]";
+const result3 = Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, timeZone });
+assert.sameValue(result3.timeZoneId, "UTC", "date-time + IANA annotation is the IANA time zone");
+
+timeZone = "2021-08-19T17:30Z[UTC]";
+const result4 = Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, timeZone });
+assert.sameValue(result4.timeZoneId, "UTC", "date-time + Z + IANA annotation is the IANA time zone");
+
+timeZone = "2021-08-19T17:30-07:00[UTC]";
+const result5 = Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, timeZone });
+assert.sameValue(result5.timeZoneId, "UTC", "date-time + offset + IANA annotation is the IANA time zone");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-string-leap-second.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-string-leap-second.js
new file mode 100644
index 0000000000..aa0c1f9f2c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-string-leap-second.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.zoneddatetime.from
+description: Leap second is a valid ISO string for TimeZone
+features: [Temporal]
+---*/
+
+let timeZone = "2016-12-31T23:59:60+00:00[UTC]";
+
+const result = Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, timeZone });
+assert.sameValue(result.timeZoneId, "UTC", "leap second is a valid ISO string for TimeZone");
+
+timeZone = "2021-08-19T17:30:45.123456789+23:59[+23:59:60]";
+assert.throws(RangeError, () => Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, timeZone }), "leap second in time zone name not valid");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-string-multiple-offsets.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-string-multiple-offsets.js
new file mode 100644
index 0000000000..0139a3ca0e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-string-multiple-offsets.js
@@ -0,0 +1,16 @@
+// |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.zoneddatetime.from
+description: Time zone parsing from ISO strings uses the bracketed offset, not the ISO string offset
+features: [Temporal]
+---*/
+
+const timeZone = "2021-08-19T17:30:45.123456789-12:12[+01:46]";
+
+const result = Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, timeZone });
+assert.sameValue(result.timeZoneId, "+01:46", "Time zone string determined from bracket name");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-string-year-zero.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-string-year-zero.js
new file mode 100644
index 0000000000..ef70bf8463
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-string-year-zero.js
@@ -0,0 +1,23 @@
+// |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.zoneddatetime.from
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+invalidStrings.forEach((timeZone) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, timeZone }),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-string.js
new file mode 100644
index 0000000000..dbc83dc9ab
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-string.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.zoneddatetime.from
+description: Time zone IDs are valid input for a time zone
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getPossibleInstantsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getPossibleInstantsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be looked up");
+ },
+});
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+
+["UTC", "+01:30"].forEach((timeZone) => {
+ const result = Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, timeZone });
+ assert.sameValue(result.getISOFields().timeZone, timeZone, `Time zone created from string "${timeZone}"`);
+});
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", getPossibleInstantsForOriginal);
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-wrong-type.js
new file mode 100644
index 0000000000..31c0c2193c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-wrong-type.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.zoneddatetime.from
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or object for TimeZone
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [19761118, "number that would convert to a valid ISO string in other contexts"],
+ [1n, "bigint"],
+];
+
+for (const [timeZone, description] of primitiveTests) {
+ assert.throws(
+ typeof timeZone === 'string' ? RangeError : TypeError,
+ () => Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, timeZone }),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "object not implementing time zone protocol"],
+ [new Temporal.Calendar("iso8601"), "calendar instance"],
+];
+
+for (const [timeZone, description] of typeErrorTests) {
+ assert.throws(TypeError, () => Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, timeZone }), `${description} is not a valid object and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..1aacf68105
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-string-calendar-annotation.js
@@ -0,0 +1,23 @@
+// |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.zoneddatetime.from
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["1970-01-01T00:00[UTC][u-ca=iso8601]", "without !"],
+ ["1970-01-01T00:00[UTC][!u-ca=iso8601]", "with !"],
+ ["1970-01-01T00:00[UTC][u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.ZonedDateTime.from(arg);
+
+ assert.sameValue(result.calendarId, "iso8601", `calendar annotation (${description})`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..3f97fce22b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-string-critical-unknown-annotation.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.zoneddatetime.from
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01T00:00[UTC][!foo=bar]",
+ "1970-01-01T00:00[UTC][!foo=bar][u-ca=iso8601]",
+ "1970-01-01T00:00[UTC][u-ca=iso8601][!foo=bar]",
+ "1970-01-01T00:00[foo=bar][!_foo-bar0=Dont-Ignore-This-99999999999]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.ZonedDateTime.from(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..cfccdfcfb6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-string-date-with-utc-offset.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.zoneddatetime.from
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+---*/
+
+const validStrings = [
+ "1970-01-01T00Z[UTC]",
+ "1970-01-01T00Z[!UTC]",
+ "1970-01-01T00+00:00[UTC]",
+ "1970-01-01T00+00:00[!UTC]",
+];
+
+for (const arg of validStrings) {
+ const result = Temporal.ZonedDateTime.from(arg);
+
+ assert.sameValue(
+ result.timeZoneId,
+ "UTC",
+ `"${arg}" is a valid UTC offset with time for ZonedDateTime`
+ );
+}
+
+const invalidStrings = [
+ "2022-09-15Z[UTC]",
+ "2022-09-15Z[Europe/Vienna]",
+ "2022-09-15+00:00[UTC]",
+ "2022-09-15-02:30[America/St_Johns]",
+];
+
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => Temporal.ZonedDateTime.from(arg),
+ `"${arg}" UTC offset without time is not valid for ZonedDateTime`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..28132f7b22
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-string-multiple-calendar.js
@@ -0,0 +1,27 @@
+// |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.zoneddatetime.from
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01T00:00[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00[UTC][!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01T00:00[UTC][u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.ZonedDateTime.from(arg),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..c20aa8f769
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-string-multiple-time-zone.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.zoneddatetime.from
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "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]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.ZonedDateTime.from(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-string-time-separators.js
new file mode 100644
index 0000000000..80ac89f0cf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-string-time-separators.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.zoneddatetime.from
+description: Time separator in string argument can vary
+features: [Temporal]
+---*/
+
+const tests = [
+ ["1970-01-01T00:00+00:00[UTC]", "uppercase T"],
+ ["1970-01-01t00:00+00:00[UTC]", "lowercase T"],
+ ["1970-01-01 00:00+00:00[UTC]", "space between date and time"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.ZonedDateTime.from(arg);
+
+ assert.sameValue(
+ result.timeZoneId,
+ "UTC",
+ `variant time separators (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..bf8780dad0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-string-time-zone-annotation.js
@@ -0,0 +1,36 @@
+// |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.zoneddatetime.from
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["1970-01-01T00:00[UTC]", "UTC", "named, with no offset"],
+ ["1970-01-01T00:00[!UTC]", "UTC", "named, with ! and no offset"],
+ ["1970-01-01T00:00[+00:00]", "+00:00", "numeric, with no offset"],
+ ["1970-01-01T00:00[!+00:00]", "+00:00", "numeric, with ! and no offset"],
+ ["1970-01-01T00:00Z[UTC]", "UTC", "named, with Z"],
+ ["1970-01-01T00:00Z[!UTC]", "UTC", "named, with Z and !"],
+ ["1970-01-01T00:00Z[+00:00]", "+00:00", "numeric, with Z"],
+ ["1970-01-01T00:00Z[!+00:00]", "+00:00", "numeric, with Z and !"],
+ ["1970-01-01T00:00+00:00[UTC]", "UTC", "named, with offset"],
+ ["1970-01-01T00:00+00:00[!UTC]", "UTC", "named, with offset and !"],
+ ["1970-01-01T00:00+00:00[+00:00]", "+00:00", "numeric, with offset"],
+ ["1970-01-01T00:00+00:00[!+00:00]", "+00:00", "numeric, with offset and !"],
+];
+
+tests.forEach(([arg, expectedZone, description]) => {
+ const result = Temporal.ZonedDateTime.from(arg);
+
+ assert.sameValue(
+ result.timeZoneId,
+ expectedZone,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..121bfe98f7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-string-unknown-annotation.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.zoneddatetime.from
+description: Various forms of unknown annotation
+features: [Temporal]
+---*/
+
+const tests = [
+ ["1970-01-01T00:00[UTC][foo=bar]", "with time zone"],
+ ["1970-01-01T00:00[UTC][foo=bar][u-ca=iso8601]", "before calendar"],
+ ["1970-01-01T00:00[UTC][u-ca=iso8601][foo=bar]", "after calendar"],
+ ["1970-01-01T00:00[UTC][foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.ZonedDateTime.from(arg);
+
+ assert.sameValue(
+ result.epochNanoseconds,
+ 0n,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-wrong-type.js
new file mode 100644
index 0000000000..874ca33f46
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-wrong-type.js
@@ -0,0 +1,42 @@
+// |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.zoneddatetime.from
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for ZonedDateTime
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const primitiveTests = [
+ [undefined, "undefined"],
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [19761118, "number that would convert to a valid ISO string in other contexts"],
+ [1n, "bigint"],
+];
+
+for (const [arg, description] of primitiveTests) {
+ assert.throws(
+ typeof arg === 'string' ? RangeError : TypeError,
+ () => Temporal.ZonedDateTime.from(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.ZonedDateTime, "Temporal.ZonedDateTime, object"],
+ [Temporal.ZonedDateTime.prototype, "Temporal.ZonedDateTime.prototype, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => Temporal.ZonedDateTime.from(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/ZonedDateTime/from/argument-zoneddatetime.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-zoneddatetime.js
new file mode 100644
index 0000000000..bfd60836f6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-zoneddatetime.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.zoneddatetime.from
+description: A ZonedDateTime object is copied, not returned directly
+features: [Temporal]
+---*/
+
+const orig = new Temporal.ZonedDateTime(946684800_000_000_010n, new Temporal.TimeZone("UTC"));
+const result = Temporal.ZonedDateTime.from(orig);
+
+assert.sameValue(result.epochNanoseconds, 946684800_000_000_010n, "ZonedDateTime is copied");
+assert.sameValue(result.timeZone, orig.timeZone, "time zone is the same");
+assert.sameValue(result.getISOFields().calendar, orig.getISOFields().calendar, "calendar is the same");
+
+assert.notSameValue(
+ result,
+ orig,
+ "When a ZonedDateTime is given, the returned value is not the original ZonedDateTime"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/balance-negative-time-units.js
new file mode 100644
index 0000000000..a30a941797
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/balance-negative-time-units.js
@@ -0,0 +1,91 @@
+// |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.zoneddatetime.from
+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-addtime step 8:
+ 8. Return ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_).
+ sec-temporal-adddatetime step 1:
+ 1. Let _timeResult_ be ? AddTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_, _hours_, _minutes_, _seconds_, _milliseconds_, _microseconds_, _nanoseconds_).
+ sec-temporal-builtintimezonegetinstantfor step 13.a:
+ a. Let _earlier_ be ? AddDateTime(_dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], _dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], 0, 0, 0, 0, 0, 0, 0, 0, 0, −_nanoseconds_, *"constrain"*).
+ sec-temporal-interpretisodatetimeoffset steps 4–10:
+ 4. If _offsetNanoseconds_ is *null*, or _offset_ is *"ignore"*, then
+ a. Let _instant_ be ? BuiltinTimeZoneGetInstantFor(_timeZone_, _dateTime_, _disambiguation_).
+ ...
+ ...
+ 6. Assert: _offset_ is *"prefer"* or *"reject"*.
+ 7. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_).
+ ...
+ 9. If _offset_ is *"reject"*, throw a *RangeError* exception.
+ 10. Let _instant_ be ? DisambiguatePossibleInstants(_possibleInstants_, _timeZone_, _dateTime_, _disambiguation_).
+ sec-temporal-totemporalzoneddatetime step 7:
+ 7. Let _epochNanoseconds_ be ? InterpretISODateTimeOffset(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]], _offsetNanoseconds_, _timeZone_, _disambiguation_, _offset_).
+ sec-temporal.zoneddatetime.from step 3:
+ 3. Return ? ToTemporalZonedDateTime(_item_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const shiftInstant = new Temporal.Instant(3661_001_001_001n);
+const tz1 = TemporalHelpers.oneShiftTimeZone(shiftInstant, 2);
+
+// This code path is encountered if offset is `ignore` or `prefer`,
+// disambiguation is `earlier` and the shift is a spring-forward change
+Temporal.ZonedDateTime.from({
+ year: 1970,
+ month: 1,
+ day: 1,
+ hour: 1,
+ minute: 1,
+ second: 1,
+ millisecond: 1,
+ microsecond: 1,
+ nanosecond: 1,
+ timeZone: tz1,
+}, { offset: "ignore", disambiguation: "earlier" });
+
+const expected1 = [
+ "1970-01-01T01:01:01.001001001",
+ "1970-01-01T01:01:01.001000999",
+];
+assert.compareArray(tz1.getPossibleInstantsForCalledWith, expected1);
+
+const tz2 = TemporalHelpers.oneShiftTimeZone(shiftInstant, 2);
+
+Temporal.ZonedDateTime.from({
+ year: 1970,
+ month: 1,
+ day: 1,
+ hour: 1,
+ minute: 1,
+ second: 1,
+ millisecond: 1,
+ microsecond: 1,
+ nanosecond: 1,
+ timeZone: tz2,
+}, { offset: "prefer", disambiguation: "earlier" });
+
+const expected2 = [
+ "1970-01-01T01:01:01.001001001",
+ "1970-01-01T01:01:01.001000999",
+];
+assert.compareArray(tz2.getPossibleInstantsForCalledWith, expected2);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/builtin.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/builtin.js
new file mode 100644
index 0000000000..16e2a98a2b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/builtin.js
@@ -0,0 +1,33 @@
+// |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.zoneddatetime.from
+description: Tests that Temporal.ZonedDateTime.from meets the requirements for built-in objects
+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.ZonedDateTime.from),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.from),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.from),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.ZonedDateTime.from.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/calendar-datefromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..7746505700
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/calendar-datefromfields-called-with-null-prototype-fields.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.zoneddatetime.from
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const timeZone = new Temporal.TimeZone("UTC");
+const arg = { year: 2000, month: 5, day: 2, timeZone, calendar };
+Temporal.ZonedDateTime.from(arg);
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/calendar-fields-iterable.js
new file mode 100644
index 0000000000..1d2a2354c0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/calendar-fields-iterable.js
@@ -0,0 +1,33 @@
+// |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.zoneddatetime.from
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.zoneddatetime.from step 3:
+ 3. Return ? ToTemporalZonedDateTime(_item_, _options_).
+ sec-temporal-totemporalzoneddatetime step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"hour"*, *"microsecond"*, *"millisecond"*, *"minute"*, *"month"*, *"monthCode"*, *"nanosecond"*, *"second"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, timeZone: "UTC", calendar });
+
+assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/calendar-temporal-object.js
new file mode 100644
index 0000000000..bf244bde0b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/calendar-temporal-object.js
@@ -0,0 +1,29 @@
+// |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.zoneddatetime.from
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.zoneddatetime.from step 3:
+ 3. Return ? ToTemporalZonedDateTime(_item_, _options_).
+ sec-temporal-totemporalzoneddatetime step 2.b:
+ b. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject, calendar) => {
+ const result = Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, timeZone: "UTC", calendar: temporalObject });
+ assert.sameValue(result.getCalendar(), calendar, "Temporal object coerced to calendar");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..a4b9393817
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/constructor-in-calendar-fields.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.zoneddatetime.prototype.from
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const timeZone = 'Europe/Paris'
+const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar, timeZone: timeZone };
+
+assert.throws(RangeError, () => Temporal.ZonedDateTime.from(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/disambiguation-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/disambiguation-invalid-string.js
new file mode 100644
index 0000000000..5880c713bc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/disambiguation-invalid-string.js
@@ -0,0 +1,32 @@
+// |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.zoneddatetime.from
+description: RangeError thrown when disambiguation option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaldisambiguation step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"disambiguation"*, « String », « *"compatible"*, *"earlier"*, *"later"*, *"reject"* », *"compatible"*).
+ sec-temporal-totemporalzoneddatetime step 5:
+ 5. Let _disambiguation_ be ? ToTemporalDisambiguation(_options_).
+ sec-temporal.zoneddatetime.from step 2:
+ 2. If Type(_item_) is Object and _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ a. ...
+ b. Perform ? ToTemporalDisambiguation(_options_).
+ c. ...
+ d. Return ...
+ 3. Return ? ToTemporalZonedDateTime(_item_, _options_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+assert.throws(RangeError, () => Temporal.ZonedDateTime.from(datetime, { disambiguation: "other string" }));
+
+const timeZone = new Temporal.TimeZone("UTC");
+const propertyBag = { timeZone, year: 2001, month: 9, day: 9, hour: 1, minute: 46, second: 40, millisecond: 987, microsecond: 654, nanosecond: 321 };
+assert.throws(RangeError, () => Temporal.ZonedDateTime.from(propertyBag, { disambiguation: "other string" }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/disambiguation-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/disambiguation-undefined.js
new file mode 100644
index 0000000000..b8b643694b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/disambiguation-undefined.js
@@ -0,0 +1,38 @@
+// |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.zoneddatetime.from
+description: Fallback value for disambiguation option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaldisambiguation step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"disambiguation"*, « String », « *"compatible"*, *"earlier"*, *"later"*, *"reject"* », *"compatible"*).
+ sec-temporal-totemporalzoneddatetime step 5:
+ 5. Let _disambiguation_ be ? ToTemporalDisambiguation(_options_).
+ sec-temporal.zoneddatetime.from step 2:
+ 2. If Type(_item_) is Object and _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ ...
+ d. Return ...
+ 3. Return ? ToTemporalZonedDateTime(_item_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const timeZone = TemporalHelpers.springForwardFallBackTimeZone();
+const springForwardFields = { timeZone, year: 2000, month: 4, day: 2, hour: 2, minute: 30 };
+const fallBackFields = { timeZone, year: 2000, month: 10, day: 29, hour: 1, minute: 30 };
+
+[
+ [springForwardFields, 954671400_000_000_000n],
+ [fallBackFields, 972808200_000_000_000n],
+].forEach(([fields, expected]) => {
+ const explicit = Temporal.ZonedDateTime.from(fields, { disambiguation: undefined });
+ assert.sameValue(explicit.epochNanoseconds, expected, "default disambiguation is compatible (later)");
+
+ // See options-undefined.js for {}
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/disambiguation-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/disambiguation-wrong-type.js
new file mode 100644
index 0000000000..7807c1e4a9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/disambiguation-wrong-type.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.zoneddatetime.from
+description: Type conversions for disambiguation option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaldisambiguation step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"disambiguation"*, « String », « *"compatible"*, *"earlier"*, *"later"*, *"reject"* », *"compatible"*).
+ sec-temporal-totemporalzoneddatetime step 5:
+ 5. Let _disambiguation_ be ? ToTemporalDisambiguation(_options_).
+ sec-temporal.zoneddatetime.from step 2:
+ 2. If Type(_item_) is Object and _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ a. ...
+ b. Perform ? ToTemporalDisambiguation(_options_).
+ c. ...
+ d. Return ...
+ 3. Return ? ToTemporalZonedDateTime(_item_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+TemporalHelpers.checkStringOptionWrongType("disambiguation", "compatible",
+ (disambiguation) => Temporal.ZonedDateTime.from(datetime, { disambiguation }),
+ (result, descr) => assert.sameValue(result.epochNanoseconds, 1_000_000_000_987_654_321n, descr),
+);
+
+const timeZone = new Temporal.TimeZone("UTC");
+const propertyBag = { timeZone, year: 2001, month: 9, day: 9, hour: 1, minute: 46, second: 40, millisecond: 987, microsecond: 654, nanosecond: 321 };
+TemporalHelpers.checkStringOptionWrongType("disambiguation", "compatible",
+ (disambiguation) => Temporal.ZonedDateTime.from(propertyBag, { disambiguation }),
+ (result, descr) => assert.sameValue(result.epochNanoseconds, 1_000_000_000_987_654_321n, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/duplicate-calendar-fields.js
new file mode 100644
index 0000000000..a849d5c07d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/duplicate-calendar-fields.js
@@ -0,0 +1,20 @@
+// |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.zoneddatetime.prototype.from
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+
+for (const extra_fields of [['foo', 'foo'], ['day'], ['hour'], ['microsecond'], ['millisecond'], ['minute'], ['month'], ['monthCode'], ['nanosecond'], ['second'], ['year'], ['timeZone'], ['offset']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const timeZone = 'Europe/Paris'
+ const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar, timeZone: timeZone };
+
+ assert.throws(RangeError, () => Temporal.ZonedDateTime.from(arg));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..73e3cd5a1c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/infinity-throws-rangeerror.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.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.zoneddatetime.from
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const base = { year: 2000, month: 5, day: 2, hour: 15, minute: 30, second: 45, millisecond: 987, microsecond: 654, nanosecond: 321, timeZone: "UTC" };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((prop) => {
+ ["constrain", "reject"].forEach((overflow) => {
+ assert.throws(RangeError, () => Temporal.ZonedDateTime.from({ ...base, [prop]: inf }, { overflow }), `${prop} property cannot be ${inf} (overflow ${overflow}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => Temporal.ZonedDateTime.from({ ...base, [prop]: obj }, { overflow }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/leap-second.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/leap-second.js
new file mode 100644
index 0000000000..0ece307056
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/leap-second.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.zoneddatetime.from
+description: Leap second is a valid ISO string for ZonedDateTime
+features: [Temporal]
+---*/
+
+let arg = "2016-12-31T23:59:60+00:00[UTC]";
+const result = Temporal.ZonedDateTime.from(arg);
+assert.sameValue(
+ result.epochNanoseconds,
+ 1_483_228_799_000_000_000n,
+ "leap second is a valid ISO string for ZonedDateTime"
+);
+
+arg = "2000-05-02T12:34:56+23:59[+23:59:60]";
+assert.throws(
+ RangeError,
+ () => Temporal.ZonedDateTime.from(arg),
+ "leap second in time zone name not valid"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/length.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/length.js
new file mode 100644
index 0000000000..5fae89d3f3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/length.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.zoneddatetime.from
+description: Temporal.ZonedDateTime.from.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.ZonedDateTime.from, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/name.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/name.js
new file mode 100644
index 0000000000..914cb3142b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/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.zoneddatetime.from
+description: Temporal.ZonedDateTime.from.name is "from"
+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.ZonedDateTime.from, "name", {
+ value: "from",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/not-a-constructor.js
new file mode 100644
index 0000000000..151912997a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/not-a-constructor.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.zoneddatetime.from
+description: Temporal.ZonedDateTime.from 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.ZonedDateTime.from();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.ZonedDateTime.from), false,
+ "isConstructor(Temporal.ZonedDateTime.from)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/observable-get-overflow-argument-primitive.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/observable-get-overflow-argument-primitive.js
new file mode 100644
index 0000000000..48ce190761
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/observable-get-overflow-argument-primitive.js
@@ -0,0 +1,52 @@
+// |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.zoneddatetime.from
+description: options properties are extracted with string argument.
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "ownKeys options",
+ "getOwnPropertyDescriptor options.disambiguation",
+ "get options.disambiguation",
+ "getOwnPropertyDescriptor options.offset",
+ "get options.offset",
+ "getOwnPropertyDescriptor options.overflow",
+ "get options.overflow",
+ "get options.disambiguation.toString",
+ "call options.disambiguation.toString",
+ "get options.offset.toString",
+ "call options.offset.toString",
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+];
+
+let actual = [];
+const options = TemporalHelpers.propertyBagObserver(actual, {
+ disambiguation: "compatible",
+ offset: "ignore",
+ overflow: "reject",
+}, "options");
+
+const result = Temporal.ZonedDateTime.from("2001-09-09T01:46:40+00:00[UTC]", options);
+assert.compareArray(actual, expected, "Successful call");
+assert.sameValue(result.epochNanoseconds, 1_000_000_000_000_000_000n);
+
+actual.splice(0); // empty it for the next check
+const failureExpected = [
+ "ownKeys options",
+ "getOwnPropertyDescriptor options.disambiguation",
+ "get options.disambiguation",
+ "getOwnPropertyDescriptor options.offset",
+ "get options.offset",
+ "getOwnPropertyDescriptor options.overflow",
+ "get options.overflow",
+];
+assert.throws(TypeError, () => Temporal.ZonedDateTime.from(7, options));
+assert.compareArray(actual, failureExpected, "Failing call");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/observable-get-overflow-argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/observable-get-overflow-argument-string-invalid.js
new file mode 100644
index 0000000000..db9ae868af
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/observable-get-overflow-argument-string-invalid.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.zoneddatetime.from
+description: options properties are extracted with ISO-invalid string argument.
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "ownKeys options",
+ "getOwnPropertyDescriptor options.disambiguation",
+ "get options.disambiguation",
+ "getOwnPropertyDescriptor options.offset",
+ "get options.offset",
+ "getOwnPropertyDescriptor options.overflow",
+ "get options.overflow",
+];
+
+let actual = [];
+const options = TemporalHelpers.propertyBagObserver(actual, {
+ disambiguation: "compatible",
+ offset: "ignore",
+ overflow: "reject",
+}, "options");
+
+assert.throws(RangeError, () => Temporal.ZonedDateTime.from("2020-13-34T25:60:60+99:99[UTC]", options));
+assert.compareArray(actual, expected);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/offset-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/offset-invalid-string.js
new file mode 100644
index 0000000000..e87884357b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/offset-invalid-string.js
@@ -0,0 +1,31 @@
+// |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.zoneddatetime.from
+description: RangeError thrown when offset option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaloffset step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"offset"*, « String », « *"prefer"*, *"use"*, *"ignore"*, *"reject"* », _fallback_).
+ sec-temporal-totemporalzoneddatetime step 6:
+ 6. Let _offset_ be ? ToTemporalOffset(_options_, *"reject"*).
+ sec-temporal.zoneddatetime.from step 2:
+ 2. If Type(_item_) is Object and _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ ...
+ c. Perform ? ToTemporalOffset(_options_, *"reject"*).
+ d. Return ...
+ 3. Return ? ToTemporalZonedDateTime(_item_, _options_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+assert.throws(RangeError, () => Temporal.ZonedDateTime.from(datetime, { offset: "other string" }));
+
+const timeZone = new Temporal.TimeZone("UTC");
+const propertyBag = { timeZone, year: 2001, month: 9, day: 9, hour: 1, minute: 46, second: 40, millisecond: 987, microsecond: 654, nanosecond: 321 };
+assert.throws(RangeError, () => Temporal.ZonedDateTime.from(propertyBag, { offset: "other string" }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/offset-overrides-critical-flag.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/offset-overrides-critical-flag.js
new file mode 100644
index 0000000000..5309b80807
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/offset-overrides-critical-flag.js
@@ -0,0 +1,33 @@
+// |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.zoneddatetime.from
+description: >
+ The offset option always overrides the critical flag in a time zone annotation
+features: [Temporal]
+---*/
+
+const useResult = Temporal.ZonedDateTime.from("2022-10-07T18:37-07:00[!UTC]", { offset: "use" });
+assert.sameValue(
+ useResult.epochNanoseconds,
+ 1665193020000000000n,
+ "exact time is unchanged with offset = use, despite critical flag"
+);
+
+const ignoreResult = Temporal.ZonedDateTime.from("2022-10-07T18:37-07:00[!UTC]", { offset: "ignore" });
+assert.sameValue(
+ ignoreResult.epochNanoseconds,
+ 1665167820000000000n,
+ "wall time is unchanged with offset = ignore, despite critical flag"
+);
+
+const preferResult = Temporal.ZonedDateTime.from("2022-10-07T18:37-07:00[!UTC]", { offset: "prefer" });
+assert.sameValue(
+ useResult.epochNanoseconds,
+ 1665193020000000000n,
+ "offset is recalculated with offset = prefer, despite critical flag"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/offset-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/offset-undefined.js
new file mode 100644
index 0000000000..acb64f48c0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/offset-undefined.js
@@ -0,0 +1,30 @@
+// |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.zoneddatetime.from
+description: Fallback value for offset option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaloffset step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"offset"*, « String », « *"prefer"*, *"use"*, *"ignore"*, *"reject"* », _fallback_).
+ sec-temporal-totemporalzoneddatetime step 6:
+ 6. Let _offset_ be ? ToTemporalOffset(_options_, *"reject"*).
+ sec-temporal.zoneddatetime.from step 2:
+ 2. If Type(_item_) is Object and _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ ...
+ c. Perform ? ToTemporalOffset(_options_, *"reject"*).
+ d. Return ...
+ 3. Return ? ToTemporalZonedDateTime(_item_, _options_).
+features: [Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("-04:00");
+const propertyBag = { timeZone, offset: "+01:00", year: 2020, month: 2, day: 16, hour: 23, minute: 45 };
+
+assert.throws(RangeError, () => Temporal.ZonedDateTime.from(propertyBag, { offset: undefined }), "default offset is reject");
+// See options-undefined.js for {}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/offset-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/offset-wrong-type.js
new file mode 100644
index 0000000000..157c789707
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/offset-wrong-type.js
@@ -0,0 +1,38 @@
+// |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.zoneddatetime.from
+description: Type conversions for offset option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaloffset step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"offset"*, « String », « *"prefer"*, *"use"*, *"ignore"*, *"reject"* », _fallback_).
+ sec-temporal-totemporalzoneddatetime step 6:
+ 6. Let _offset_ be ? ToTemporalOffset(_options_, *"reject"*).
+ sec-temporal.zoneddatetime.from step 2:
+ 2. If Type(_item_) is Object and _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ ...
+ c. Perform ? ToTemporalOffset(_options_, *"reject"*).
+ d. Return ...
+ 3. Return ? ToTemporalZonedDateTime(_item_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+TemporalHelpers.checkStringOptionWrongType("offset", "reject",
+ (offset) => Temporal.ZonedDateTime.from(datetime, { offset }),
+ (result, descr) => assert.sameValue(result.epochNanoseconds, 1_000_000_000_987_654_321n, descr),
+);
+
+const timeZone = new Temporal.TimeZone("UTC");
+const propertyBag = { timeZone, offset: "+00:00", year: 2001, month: 9, day: 9, hour: 1, minute: 46, second: 40, millisecond: 987, microsecond: 654, nanosecond: 321 };
+TemporalHelpers.checkStringOptionWrongType("offset", "reject",
+ (offset) => Temporal.ZonedDateTime.from(propertyBag, { offset }),
+ (result, descr) => assert.sameValue(result.epochNanoseconds, 1_000_000_000_987_654_321n, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/options-object.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/options-object.js
new file mode 100644
index 0000000000..a1c9e66d59
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/options-object.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.zoneddatetime.prototype.from
+description: Empty object may be used as options
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ Temporal.ZonedDateTime.from({ year: 1976, month: 11, day: 18, timeZone: "UTC" }, {}).epochNanoseconds, 217123200000000000n, "UTC",
+ "options may be an empty plain object"
+);
+
+assert.sameValue(
+ Temporal.ZonedDateTime.from({ year: 1976, month: 11, day: 18, timeZone: "UTC" }, () => {}).epochNanoseconds, 217123200000000000n, "UTC",
+ "options may be an empty function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/options-undefined.js
new file mode 100644
index 0000000000..f6fb1c42d7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/options-undefined.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.zoneddatetime.from
+includes: [temporalHelpers.js]
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const overflowFields = { year: 2000, month: 13, day: 2, timeZone: "UTC" };
+
+const overflowExplicit = Temporal.ZonedDateTime.from(overflowFields, undefined);
+assert.sameValue(overflowExplicit.month, 12, "default overflow is constrain");
+
+const overflowPropertyImplicit = Temporal.ZonedDateTime.from(overflowFields, {});
+assert.sameValue(overflowPropertyImplicit.month, 12, "default overflow is constrain");
+
+const overflowImplicit = Temporal.ZonedDateTime.from(overflowFields);
+assert.sameValue(overflowImplicit.month, 12, "default overflow is constrain");
+
+const timeZone = TemporalHelpers.springForwardFallBackTimeZone();
+const disambiguationEarlierFields = { timeZone, year: 2000, month: 10, day: 29, hour: 1, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321 };
+const disambiguationLaterFields = { timeZone, year: 2000, month: 4, day: 2, hour: 2, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321 };
+
+[
+ [disambiguationEarlierFields, 972808496987654321n],
+ [disambiguationLaterFields, 954671696987654321n],
+].forEach(([fields, expected]) => {
+ const explicit = Temporal.ZonedDateTime.from(fields, undefined);
+ assert.sameValue(explicit.epochNanoseconds, expected, "default disambiguation is compatible");
+
+ const propertyImplicit = Temporal.ZonedDateTime.from(fields, {});
+ assert.sameValue(propertyImplicit.epochNanoseconds, expected, "default disambiguation is compatible");
+
+ const implicit = Temporal.ZonedDateTime.from(fields);
+ assert.sameValue(implicit.epochNanoseconds, expected, "default disambiguation is compatible");
+});
+
+const offsetFields = { year: 2000, month: 5, day: 2, offset: "+23:59", timeZone: "UTC" };
+assert.throws(RangeError, () => Temporal.ZonedDateTime.from(offsetFields, undefined), "default offset is reject");
+assert.throws(RangeError, () => Temporal.ZonedDateTime.from(offsetFields, {}), "default offset is reject");
+assert.throws(RangeError, () => Temporal.ZonedDateTime.from(offsetFields), "default offset is reject");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/options-wrong-type.js
new file mode 100644
index 0000000000..060df72e6f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/options-wrong-type.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.zoneddatetime.from
+description: TypeError thrown when options argument is a primitive
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const badOptions = [
+ null,
+ true,
+ "some string",
+ Symbol(),
+ 1,
+ 2n,
+];
+
+for (const value of badOptions) {
+ assert.throws(TypeError, () => Temporal.ZonedDateTime.from({ year: 1976, month: 11, day: 18, timeZone: "UTC" }, value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/order-of-operations.js
new file mode 100644
index 0000000000..96dacb4a2d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/order-of-operations.js
@@ -0,0 +1,132 @@
+// |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.zoneddatetime.from
+description: Properties on objects passed to from() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ // CopyDataProperties
+ "ownKeys options",
+ "getOwnPropertyDescriptor options.overflow",
+ "get options.overflow",
+ "getOwnPropertyDescriptor options.disambiguation",
+ "get options.disambiguation",
+ "getOwnPropertyDescriptor options.offset",
+ "get options.offset",
+ "getOwnPropertyDescriptor options.extra",
+ "get options.extra",
+ // ToTemporalCalendar
+ "get item.calendar",
+ "has item.calendar.dateAdd",
+ "has item.calendar.dateFromFields",
+ "has item.calendar.dateUntil",
+ "has item.calendar.day",
+ "has item.calendar.dayOfWeek",
+ "has item.calendar.dayOfYear",
+ "has item.calendar.daysInMonth",
+ "has item.calendar.daysInWeek",
+ "has item.calendar.daysInYear",
+ "has item.calendar.fields",
+ "has item.calendar.id",
+ "has item.calendar.inLeapYear",
+ "has item.calendar.mergeFields",
+ "has item.calendar.month",
+ "has item.calendar.monthCode",
+ "has item.calendar.monthDayFromFields",
+ "has item.calendar.monthsInYear",
+ "has item.calendar.weekOfYear",
+ "has item.calendar.year",
+ "has item.calendar.yearMonthFromFields",
+ "has item.calendar.yearOfWeek",
+ "get item.calendar.dateFromFields",
+ "get item.calendar.fields",
+ "call item.calendar.fields",
+ // PrepareTemporalFields
+ "get item.day",
+ "get item.day.valueOf",
+ "call item.day.valueOf",
+ "get item.hour",
+ "get item.hour.valueOf",
+ "call item.hour.valueOf",
+ "get item.microsecond",
+ "get item.microsecond.valueOf",
+ "call item.microsecond.valueOf",
+ "get item.millisecond",
+ "get item.millisecond.valueOf",
+ "call item.millisecond.valueOf",
+ "get item.minute",
+ "get item.minute.valueOf",
+ "call item.minute.valueOf",
+ "get item.month",
+ "get item.month.valueOf",
+ "call item.month.valueOf",
+ "get item.monthCode",
+ "get item.monthCode.toString",
+ "call item.monthCode.toString",
+ "get item.nanosecond",
+ "get item.nanosecond.valueOf",
+ "call item.nanosecond.valueOf",
+ "get item.offset",
+ "get item.offset.toString",
+ "call item.offset.toString",
+ "get item.second",
+ "get item.second.valueOf",
+ "call item.second.valueOf",
+ "get item.timeZone",
+ "get item.year",
+ "get item.year.valueOf",
+ "call item.year.valueOf",
+ "has item.timeZone.getOffsetNanosecondsFor",
+ "has item.timeZone.getPossibleInstantsFor",
+ "has item.timeZone.id",
+ // InterpretTemporalDateTimeFields
+ "get options.disambiguation.toString",
+ "call options.disambiguation.toString",
+ "get options.offset.toString",
+ "call options.offset.toString",
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+ "call item.calendar.dateFromFields",
+ // lookup in ToTemporalZonedDateTime
+ "get item.timeZone.getOffsetNanosecondsFor",
+ "get item.timeZone.getPossibleInstantsFor",
+ // InterpretISODateTimeOffset
+ "call item.timeZone.getPossibleInstantsFor",
+ "call item.timeZone.getOffsetNanosecondsFor",
+];
+const actual = [];
+
+const from = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2001,
+ month: 5,
+ monthCode: "M05",
+ day: 2,
+ hour: 6,
+ minute: 54,
+ second: 32,
+ millisecond: 987,
+ microsecond: 654,
+ nanosecond: 321,
+ offset: "+00:00",
+ calendar: TemporalHelpers.calendarObserver(actual, "item.calendar"),
+ timeZone: TemporalHelpers.timeZoneObserver(actual, "item.timeZone"),
+}, "item");
+
+function createOptionsObserver({ overflow = "constrain", disambiguation = "compatible", offset = "reject" } = {}) {
+ return TemporalHelpers.propertyBagObserver(actual, {
+ overflow,
+ disambiguation,
+ offset,
+ extra: "property",
+ }, "options");
+}
+
+Temporal.ZonedDateTime.from(from, createOptionsObserver());
+assert.compareArray(actual, expected, "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/overflow-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/overflow-invalid-string.js
new file mode 100644
index 0000000000..4131d5674c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/overflow-invalid-string.js
@@ -0,0 +1,48 @@
+// |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.zoneddatetime.from
+description: RangeError thrown when overflow option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-interprettemporaldatetimefields steps 2–3:
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+ 3. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal-totemporalzoneddatetime steps 2–3:
+ 2. If Type(_item_) is Object, then
+ ...
+ j. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+ 3. Else,
+ a. Perform ? ToTemporalOverflow(_options_).
+ sec-temporal.zoneddatetime.from steps 2–3:
+ 2. If Type(_item_) is Object and _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ a. Perform ? ToTemporalOverflow(_options_).
+ ...
+ d. Return ...
+ 3. Return ? ToTemporalZonedDateTime(_item_, _options_).
+features: [Temporal]
+---*/
+
+const validValues = [
+ new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"),
+ { year: 2000, month: 5, day: 2, hour: 12, timeZone: "UTC" },
+ "2001-09-09T01:46:40.987654321+00:00[UTC]",
+];
+
+const badOverflows = ["", "CONSTRAIN", "balance", "other string", "constra\u0131n", "reject\0"];
+for (const value of validValues) {
+ for (const overflow of badOverflows) {
+ assert.throws(
+ RangeError,
+ () => Temporal.ZonedDateTime.from(value, { overflow }),
+ `invalid overflow ("${overflow}")`
+ );
+ }
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/overflow-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/overflow-undefined.js
new file mode 100644
index 0000000000..f125f1c935
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/overflow-undefined.js
@@ -0,0 +1,48 @@
+// |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.zoneddatetime.from
+description: Fallback value for overflow option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-interprettemporaldatetimefields steps 2–3:
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+ 3. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal-totemporalzoneddatetime steps 2–3:
+ 2. If Type(_item_) is Object, then
+ ...
+ j. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+ 3. Else,
+ a. Perform ? ToTemporalOverflow(_options_).
+ sec-temporal.zoneddatetime.from steps 2–3:
+ 2. If Type(_item_) is Object and _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ a. Perform ? ToTemporalOverflow(_options_).
+ ...
+ d. Return ...
+ 3. Return ? ToTemporalZonedDateTime(_item_, _options_).
+features: [Temporal]
+---*/
+
+const validValues = [
+ new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"),
+ "2001-09-09T01:46:40.987654321+00:00[UTC]",
+];
+validValues.forEach((value) => {
+ const explicit = Temporal.ZonedDateTime.from(value, { overflow: undefined });
+ assert.sameValue(explicit.epochNanoseconds, 1_000_000_000_987_654_321n, "overflow is ignored");
+ const implicit = Temporal.ZonedDateTime.from(value, {});
+ assert.sameValue(implicit.epochNanoseconds, 1_000_000_000_987_654_321n, "overflow is ignored");
+});
+
+const propertyBag = { year: 2000, month: 15, day: 34, hour: 12, timeZone: "UTC" };
+const explicit = Temporal.ZonedDateTime.from(propertyBag, { overflow: undefined });
+assert.sameValue(explicit.epochNanoseconds, 978_264_000_000_000_000n, "default overflow is constrain");
+
+// See options-undefined for {}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/overflow-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/overflow-wrong-type.js
new file mode 100644
index 0000000000..90a17e7e90
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/overflow-wrong-type.js
@@ -0,0 +1,66 @@
+// |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.zoneddatetime.from
+description: Type conversions for overflow option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-interprettemporaldatetimefields steps 2–3:
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+ 3. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal-totemporalzoneddatetime steps 2–3:
+ 2. If Type(_item_) is Object, then
+ ...
+ j. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+ 3. Else,
+ a. Perform ? ToTemporalOverflow(_options_).
+ sec-temporal.zoneddatetime.from steps 2–3:
+ 2. If Type(_item_) is Object and _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ a. Perform ? ToTemporalOverflow(_options_).
+ ...
+ d. Return ...
+ 3. Return ? ToTemporalZonedDateTime(_item_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const validValues = [
+ new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"),
+ "2001-09-09T01:46:40.987654321+00:00[UTC]",
+];
+validValues.forEach((value) => TemporalHelpers.checkStringOptionWrongType("overflow", "constrain",
+ (overflow) => Temporal.ZonedDateTime.from(value, { overflow }),
+ (result, descr) => assert.sameValue(result.epochNanoseconds, 1_000_000_000_987_654_321n, descr),
+));
+
+// See TemporalHelpers.checkStringOptionWrongType(); this code path has
+// different expectations for observable calls
+const propertyBag = { year: 2001, month: 9, day: 9, hour: 1, minute: 46, second: 40, timeZone: "UTC" };
+
+assert.throws(RangeError, () => Temporal.ZonedDateTime.from(propertyBag, { overflow: null }), "null");
+assert.throws(RangeError, () => Temporal.ZonedDateTime.from(propertyBag, { overflow: true }), "true");
+assert.throws(RangeError, () => Temporal.ZonedDateTime.from(propertyBag, { overflow: false }), "false");
+assert.throws(TypeError, () => Temporal.ZonedDateTime.from(propertyBag, { overflow: Symbol() }), "symbol");
+assert.throws(RangeError, () => Temporal.ZonedDateTime.from(propertyBag, { overflow: 2n }), "bigint");
+assert.throws(RangeError, () => Temporal.ZonedDateTime.from(propertyBag, { overflow: 2 }), "number");
+assert.throws(RangeError, () => Temporal.ZonedDateTime.from(propertyBag, { overflow: {} }), "plain object");
+
+// toString property should only be read and converted to a string once, because
+// a copied object with the resulting string on it is passed to
+// Calendar.dateFromFields().
+const expected = [
+ "get overflow.toString",
+ "call overflow.toString",
+];
+const actual = [];
+const observer = TemporalHelpers.toPrimitiveObserver(actual, "constrain", "overflow");
+const result = Temporal.ZonedDateTime.from(propertyBag, { overflow: observer });
+assert.sameValue(result.epochNanoseconds, 1_000_000_000_000_000_000n, "object with toString");
+assert.compareArray(actual, expected, "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/prop-desc.js
new file mode 100644
index 0000000000..672d99a46b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/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.zoneddatetime.from
+description: The "from" property of Temporal.ZonedDateTime
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.ZonedDateTime.from,
+ "function",
+ "`typeof ZonedDateTime.from` is `function`"
+);
+
+verifyProperty(Temporal.ZonedDateTime, "from", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/proto-in-calendar-fields.js
new file mode 100644
index 0000000000..f05165d2ab
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/proto-in-calendar-fields.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.zoneddatetime.prototype.from
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const timeZone = 'Europe/Paris'
+const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar, timeZone: timeZone };
+
+assert.throws(RangeError, () => Temporal.ZonedDateTime.from(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/read-time-fields-before-datefromfields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/read-time-fields-before-datefromfields.js
new file mode 100644
index 0000000000..bf5aeb22d8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/read-time-fields-before-datefromfields.js
@@ -0,0 +1,30 @@
+// |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.zoneddatetime.from
+description: The time fields are read from the object before being passed to dateFromFields().
+info: |
+ sec-temporal.zoneddatetime.from step 3:
+ 3. Return ? ToTemporalDateTime(_item_, _options_).
+ sec-temporal-totemporalzoneddatetime step 2.j:
+ j. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+ sec-temporal-interprettemporaldatetimefields steps 1–2:
+ 1. Let _timeResult_ be ? ToTemporalTimeRecord(_fields_).
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarMakeInfinityTime();
+const datetime = Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, hour: 12, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321, timeZone: "UTC", calendar });
+
+assert.sameValue(datetime.hour, 12, "hour value");
+assert.sameValue(datetime.minute, 34, "minute value");
+assert.sameValue(datetime.second, 56, "second value");
+assert.sameValue(datetime.millisecond, 987, "millisecond value");
+assert.sameValue(datetime.microsecond, 654, "microsecond value");
+assert.sameValue(datetime.nanosecond, 321, "nanosecond value");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/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/ZonedDateTime/from/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/subclassing-ignored.js
new file mode 100644
index 0000000000..dd7cb1462d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/subclassing-ignored.js
@@ -0,0 +1,30 @@
+// |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.zoneddatetime.from
+description: The receiver is never called when calling from()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnoredStatic(
+ Temporal.ZonedDateTime,
+ "from",
+ ["2000-01-01T00:00:00.00000001+00:00[UTC]"],
+ (result) => {
+ assert.sameValue(result.epochNanoseconds, 946684800_000_000_010n, "epochNanoseconds result");
+ assert.sameValue(result.year, 2000, "year result");
+ assert.sameValue(result.month, 1, "month result");
+ assert.sameValue(result.day, 1, "day result");
+ assert.sameValue(result.hour, 0, "hour result");
+ assert.sameValue(result.minute, 0, "minute result");
+ assert.sameValue(result.second, 0, "second result");
+ assert.sameValue(result.millisecond, 0, "millisecond result");
+ assert.sameValue(result.microsecond, 0, "microsecond result");
+ assert.sameValue(result.nanosecond, 10, "nanosecond result");
+ },
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/timezone-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/timezone-case-insensitive.js
new file mode 100644
index 0000000000..e07176d96a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/timezone-case-insensitive.js
@@ -0,0 +1,15 @@
+// |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.zoneddatetime.prototype.from
+description: Time zone names are case insensitive
+features: [Temporal]
+---*/
+
+const timeZone = 'uTc';
+const result = Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, timeZone });
+assert.sameValue(result.timeZoneId, 'UTC', `Time zone created from string "${timeZone}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/timezone-getpossibleinstantsfor-iterable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/timezone-getpossibleinstantsfor-iterable.js
new file mode 100644
index 0000000000..94a04acb78
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/timezone-getpossibleinstantsfor-iterable.js
@@ -0,0 +1,29 @@
+// |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.zoneddatetime.from
+description: An iterable returned from timeZone.getPossibleInstantsFor is consumed after each call
+info: |
+ sec-temporal.zoneddatetime.from step 3:
+ 3. Return ? ToTemporalZonedDateTime(_item_, _options_).
+ sec-temporal-totemporalzoneddatetime step 7:
+ 7. Let _epochNanoseconds_ be ? InterpretISODateTimeOffset(_result_.[[Year]], [...], _result_.[[Nanosecond]], _offsetNanoseconds_, _timeZone_, _disambiguation_, _offset_).
+ sec-temporal-interpretisodatetimeoffset step 7:
+ 7. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_).
+ sec-temporal-getpossibleinstantsfor step 2:
+ 2. Let _list_ be ? IterableToList(_possibleInstants_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "2000-05-02T00:00:00",
+];
+
+TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => {
+ Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, timeZone });
+}, expected);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/year-zero.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/year-zero.js
new file mode 100644
index 0000000000..b1789110a8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/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.zoneddatetime.from
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-0000000-01-01T00:02Z[UTC]",
+ "-0000000-01-01T00:02+00:00[UTC]",
+ "-0000000-01-01T00:02:00.000000000+00:00[UTC]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.ZonedDateTime.from(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/zoneddatetime-string-multiple-offsets.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/zoneddatetime-string-multiple-offsets.js
new file mode 100644
index 0000000000..966415633f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/zoneddatetime-string-multiple-offsets.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.zoneddatetime.from
+description: Sub-minute offset trailing zeroes allowed in ISO string but not in bracketed offset
+features: [Temporal]
+---*/
+
+let str = "1970-01-01T01:35:30+01:35:00.000000000[+01:35]";
+
+const result = Temporal.ZonedDateTime.from(str);
+assert.sameValue(result.timeZoneId, "+01:35", "ISO offset, sub-minute offset trailing-zeroes");
+
+str = "1970-01-01T01:35:30+01:35:00.000000000[+01:35:00.000000000]";
+assert.throws(
+ RangeError,
+ () => Temporal.ZonedDateTime.from(str),
+ "Trailing zeroes not allowed for sub-minute time zone identifiers"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/zoneddatetime-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/zoneddatetime-string.js
new file mode 100644
index 0000000000..c96cf60ffb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/zoneddatetime-string.js
@@ -0,0 +1,40 @@
+// |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.zoneddatetime.from
+description: Conversion of ISO date-time strings to Temporal.ZonedDateTime instances
+features: [Temporal]
+---*/
+
+let str = "1970-01-01T00:00";
+assert.throws(RangeError, () => Temporal.ZonedDateTime.from(str), "bare date-time string is not a ZonedDateTime");
+str = "1970-01-01T00:00Z";
+assert.throws(RangeError, () => Temporal.ZonedDateTime.from(str), "date-time + Z is not a ZonedDateTime");
+str = "1970-01-01T00:00+01:00";
+assert.throws(RangeError, () => Temporal.ZonedDateTime.from(str), "date-time + offset is not a ZonedDateTime");
+
+str = "1970-01-01T00:00[+01:00]";
+const result1 = Temporal.ZonedDateTime.from(str);
+assert.sameValue(result1.epochNanoseconds, -3600_000_000_000n, "date-time + IANA annotation preserves wall time in the time zone");
+assert.sameValue(result1.timeZoneId, "+01:00", "IANA annotation is not ignored");
+
+str = "1970-01-01T00:00Z[+01:00]";
+const result2 = Temporal.ZonedDateTime.from(str);
+assert.sameValue(result2.epochNanoseconds, 0n, "date-time + Z + IANA annotation preserves exact time in the time zone");
+assert.sameValue(result2.timeZoneId, "+01:00", "IANA annotation is not ignored");
+
+str = "1970-01-01T00:00+01:00[+01:00]";
+const result3 = Temporal.ZonedDateTime.from(str);
+assert.sameValue(result3.epochNanoseconds, -3600_000_000_000n, "date-time + offset + IANA annotation ensures both exact and wall time match");
+assert.sameValue(result3.timeZoneId, "+01:00", "IANA annotation is not ignored");
+
+str = "1970-01-01T00:00-04:15[+01:00]";
+assert.throws(RangeError, () => Temporal.ZonedDateTime.from(str), "date-time + offset + IANA annotation throws if wall time and exact time mismatch");
+assert.throws(RangeError, () => Temporal.ZonedDateTime.from(str, { offset: "reject" }), "date-time + offset + IANA annotation throws if wall time and exact time mismatch (explicit reject option)");
+const result4 = Temporal.ZonedDateTime.from(str, { offset: "ignore" });
+assert.sameValue(result4.epochNanoseconds, -3600_000_000_000n, "date-time + wrong offset + IANA annotation preserves wall time in the time zone (offset: ignore option)");
+assert.sameValue(result4.timeZoneId, "+01:00", "IANA annotation is not ignored");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/length.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/length.js
new file mode 100644
index 0000000000..c07bca45c5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/length.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.zoneddatetime
+description: Temporal.ZonedDateTime.length is 2
+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.ZonedDateTime, "length", {
+ value: 2,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/missing-arguments.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/missing-arguments.js
new file mode 100644
index 0000000000..354baa210b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/missing-arguments.js
@@ -0,0 +1,13 @@
+// |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.zoneddatetime
+description: TypeError thrown when constructor invoked with no argument
+features: [Temporal]
+---*/
+
+assert.throws(TypeError, () => new Temporal.ZonedDateTime());
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/name.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/name.js
new file mode 100644
index 0000000000..3b471b5c74
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/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.zoneddatetime
+description: Temporal.ZonedDateTime.name is "ZonedDateTime"
+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.ZonedDateTime, "name", {
+ value: "ZonedDateTime",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prop-desc.js
new file mode 100644
index 0000000000..0a2e8d4f7a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/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.zoneddatetime
+description: The "ZonedDateTime" property of Temporal
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.ZonedDateTime,
+ "function",
+ "`typeof ZonedDateTime` is `function`"
+);
+
+verifyProperty(Temporal, "ZonedDateTime", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/argument-duration-max.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/argument-duration-max.js
new file mode 100644
index 0000000000..438624bc1a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/argument-duration-max.js
@@ -0,0 +1,57 @@
+// |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.zoneddatetime.prototype.add
+description: Maximum allowed duration
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(0n, "UTC");
+
+const maxCases = [
+ ["P273790Y8M12D", "string with max years"],
+ [{ years: 273790, months: 8, days: 12 }, "property bag with max years"],
+ ["P3285488M12D", "string with max months"],
+ [{ months: 3285488, days: 12 }, "property bag with max months"],
+ ["P14285714W2D", "string with max weeks"],
+ [{ weeks: 14285714, days: 2 }, "property bag with max weeks"],
+ ["P100000000D", "string with max days"],
+ [{ days: 100000000 }, "property bag with max days"],
+ ["PT2400000000H", "string with max hours"],
+ [{ hours: 2400000000 }, "property bag with max hours"],
+ ["PT144000000000M", "string with max minutes"],
+ [{ minutes: 144000000000 }, "property bag with max minutes"],
+ ["PT8640000000000S", "string with max seconds"],
+ [{ seconds: 8640000000000 }, "property bag with max seconds"],
+];
+
+for (const [arg, descr] of maxCases) {
+ const result = instance.add(arg);
+ assert.sameValue(result.epochNanoseconds, 8640000000000000000000n, `operation succeeds with ${descr}`);
+}
+
+const minCases = [
+ ["-P273790Y8M11D", "string with min years"],
+ [{ years: -273790, months: -8, days: -11 }, "property bag with min years"],
+ ["-P3285488M11D", "string with min months"],
+ [{ months: -3285488, days: -11 }, "property bag with min months"],
+ ["-P14285714W2D", "string with min weeks"],
+ [{ weeks: -14285714, days: -2 }, "property bag with min weeks"],
+ ["-P100000000D", "string with min days"],
+ [{ days: -100000000 }, "property bag with min days"],
+ ["-PT2400000000H", "string with min hours"],
+ [{ hours: -2400000000 }, "property bag with min hours"],
+ ["-PT144000000000M", "string with min minutes"],
+ [{ minutes: -144000000000 }, "property bag with min minutes"],
+ ["-PT8640000000000S", "string with min seconds"],
+ [{ seconds: -8640000000000 }, "property bag with min seconds"],
+];
+
+for (const [arg, descr] of minCases) {
+ const result = instance.add(arg);
+ assert.sameValue(result.epochNanoseconds, -8640000000000000000000n, `operation succeeds with ${descr}`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/argument-duration-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/argument-duration-out-of-range.js
new file mode 100644
index 0000000000..b7d9b32907
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/argument-duration-out-of-range.js
@@ -0,0 +1,75 @@
+// |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.zoneddatetime.prototype.add
+description: Duration-like argument that is out of range
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(0n, "UTC");
+
+const cases = [
+ // 2^32 = 4294967296
+ ["P4294967296Y", "string with years > max"],
+ [{ years: 4294967296 }, "property bag with years > max"],
+ ["-P4294967296Y", "string with years < min"],
+ [{ years: -4294967296 }, "property bag with years < min"],
+ ["P4294967296M", "string with months > max"],
+ [{ months: 4294967296 }, "property bag with months > max"],
+ ["-P4294967296M", "string with months < min"],
+ [{ months: -4294967296 }, "property bag with months < min"],
+ ["P4294967296W", "string with weeks > max"],
+ [{ weeks: 4294967296 }, "property bag with weeks > max"],
+ ["-P4294967296W", "string with weeks < min"],
+ [{ weeks: -4294967296 }, "property bag with weeks < min"],
+
+ // ceil(max safe integer / 86400) = 104249991375
+ ["P104249991375D", "string with days > max"],
+ [{ days: 104249991375 }, "property bag with days > max"],
+ ["P104249991374DT24H", "string where hours balance into days > max"],
+ [{ days: 104249991374, hours: 24 }, "property bag where hours balance into days > max"],
+ ["-P104249991375D", "string with days < min"],
+ [{ days: -104249991375 }, "property bag with days < min"],
+ ["-P104249991374DT24H", "string where hours balance into days < min"],
+ [{ days: -104249991374, hours: -24 }, "property bag where hours balance into days < min"],
+
+ // ceil(max safe integer / 3600) = 2501999792984
+ ["PT2501999792984H", "string with hours > max"],
+ [{ hours: 2501999792984 }, "property bag with hours > max"],
+ ["PT2501999792983H60M", "string where minutes balance into hours > max"],
+ [{ hours: 2501999792983, minutes: 60 }, "property bag where minutes balance into hours > max"],
+ ["-PT2501999792984H", "string with hours < min"],
+ [{ hours: -2501999792984 }, "property bag with hours < min"],
+ ["-PT2501999792983H60M", "string where minutes balance into hours < min"],
+ [{ hours: -2501999792983, minutes: -60 }, "property bag where minutes balance into hours < min"],
+
+ // ceil(max safe integer / 60) = 150119987579017
+ ["PT150119987579017M", "string with minutes > max"],
+ [{ minutes: 150119987579017 }, "property bag with minutes > max"],
+ ["PT150119987579016M60S", "string where seconds balance into minutes > max"],
+ [{ minutes: 150119987579016, seconds: 60 }, "property bag where seconds balance into minutes > max"],
+ ["-PT150119987579017M", "string with minutes < min"],
+ [{ minutes: -150119987579017 }, "property bag with minutes < min"],
+ ["-PT150119987579016M60S", "string where seconds balance into minutes < min"],
+ [{ minutes: -150119987579016, seconds: -60 }, "property bag where seconds balance into minutes < min"],
+
+ // 2^53 = 9007199254740992
+ ["PT9007199254740992S", "string with seconds > max"],
+ [{ seconds: 9007199254740992 }, "property bag with seconds > max"],
+ [{ seconds: 9007199254740991, milliseconds: 1000 }, "property bag where milliseconds balance into seconds > max"],
+ [{ seconds: 9007199254740991, microseconds: 1000000 }, "property bag where microseconds balance into seconds > max"],
+ [{ seconds: 9007199254740991, nanoseconds: 1000000000 }, "property bag where nanoseconds balance into seconds > max"],
+ ["-PT9007199254740992S", "string with seconds < min"],
+ [{ seconds: -9007199254740992 }, "property bag with seconds < min"],
+ [{ seconds: -9007199254740991, milliseconds: -1000 }, "property bag where milliseconds balance into seconds < min"],
+ [{ seconds: -9007199254740991, microseconds: -1000000 }, "property bag where microseconds balance into seconds < min"],
+ [{ seconds: -9007199254740991, nanoseconds: -1000000000 }, "property bag where nanoseconds balance into seconds < min"],
+];
+
+for (const [arg, descr] of cases) {
+ assert.throws(RangeError, () => instance.add(arg), `${descr} is out of range`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/argument-invalid-property.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/argument-invalid-property.js
new file mode 100644
index 0000000000..7cd3400b6f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/argument-invalid-property.js
@@ -0,0 +1,31 @@
+// |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.zoneddatetime.prototype.add
+description: temporalDurationLike object must contain at least one correctly spelled property
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+
+assert.throws(
+ TypeError,
+ () => instance.add({}),
+ "Throws TypeError if no property is present"
+);
+
+assert.throws(
+ TypeError,
+ () => instance.add({ nonsense: true }),
+ "Throws TypeError if no recognized property is present"
+);
+
+assert.throws(
+ TypeError,
+ () => instance.add({ sign: 1 }),
+ "Sign property is not recognized"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/argument-mixed-sign.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/argument-mixed-sign.js
new file mode 100644
index 0000000000..1a85571cac
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/argument-mixed-sign.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.zoneddatetime.prototype.add
+description: Positive and negative values in the temporalDurationLike argument are not acceptable
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+
+["constrain", "reject"].forEach((overflow) => {
+ assert.throws(
+ RangeError,
+ () => instance.add({ hours: 1, minutes: -30 }, { overflow }),
+ `mixed positive and negative values always throw (overflow = "${overflow}")`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/argument-not-object.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/argument-not-object.js
new file mode 100644
index 0000000000..352605e388
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/argument-not-object.js
@@ -0,0 +1,22 @@
+// |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.zoneddatetime.prototype.add
+description: Passing a primitive other than string to add() throws
+features: [Symbol, Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+assert.throws(TypeError, () => instance.add(undefined), "undefined");
+assert.throws(TypeError, () => instance.add(null), "null");
+assert.throws(TypeError, () => instance.add(true), "boolean");
+assert.throws(RangeError, () => instance.add(""), "empty string");
+assert.throws(TypeError, () => instance.add(Symbol()), "Symbol");
+assert.throws(TypeError, () => instance.add(7), "number");
+assert.throws(TypeError, () => instance.add(7n), "bigint");
+assert.throws(TypeError, () => instance.add([]), "array");
+assert.throws(TypeError, () => instance.add(() => {}), "function");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/argument-singular-properties.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/argument-singular-properties.js
new file mode 100644
index 0000000000..f619de4aed
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/argument-singular-properties.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.zoneddatetime.prototype.add
+description: Singular properties in the property bag are always ignored
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+
+[
+ { year: 1 },
+ { month: 2 },
+ { week: 3 },
+ { day: 4 },
+ { hour: 5 },
+ { minute: 6 },
+ { second: 7 },
+ { millisecond: 8 },
+ { microsecond: 9 },
+ { nanosecond: 10 },
+].forEach((badObject) => {
+ assert.throws(TypeError, () => instance.add(badObject),
+ "Throw TypeError if temporalDurationLike is not valid");
+});
+
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/argument-string-fractional-units-rounding-mode.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/argument-string-fractional-units-rounding-mode.js
new file mode 100644
index 0000000000..9a129e78a4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/argument-string-fractional-units-rounding-mode.js
@@ -0,0 +1,18 @@
+// |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.zoneddatetime.prototype.add
+description: Strings with fractional duration units are rounded with the correct rounding mode
+features: [Temporal]
+---*/
+
+const epoch = new Temporal.ZonedDateTime(0n, "UTC");
+
+assert.sameValue(epoch.add("PT1.03125H").epochNanoseconds, 3712_500_000_000n,
+ "positive fractional units rounded with correct rounding mode");
+assert.sameValue(epoch.add("-PT1.03125H").epochNanoseconds, -3712_500_000_000n,
+ "negative fractional units rounded with correct rounding mode");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/argument-string-negative-fractional-units.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/argument-string-negative-fractional-units.js
new file mode 100644
index 0000000000..9207d8d957
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/argument-string-negative-fractional-units.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.zoneddatetime.prototype.add
+description: Strings with fractional duration units are treated with the correct sign
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+
+const resultHours = instance.add("-PT24.567890123H");
+assert.sameValue(resultHours.epochNanoseconds, 999_911_555_595_557_200n, "negative fractional hours");
+
+const resultMinutes = instance.add("-PT1440.567890123M");
+assert.sameValue(resultMinutes.epochNanoseconds, 999_913_565_926_592_620n, "negative fractional minutes");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/branding.js
new file mode 100644
index 0000000000..434707b74c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/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.zoneddatetime.prototype.add
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const add = Temporal.ZonedDateTime.prototype.add;
+
+assert.sameValue(typeof add, "function");
+
+const args = [new Temporal.Duration(5)];
+
+assert.throws(TypeError, () => add.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => add.apply(null, args), "null");
+assert.throws(TypeError, () => add.apply(true, args), "true");
+assert.throws(TypeError, () => add.apply("", args), "empty string");
+assert.throws(TypeError, () => add.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => add.apply(1, args), "1");
+assert.throws(TypeError, () => add.apply({}, args), "plain object");
+assert.throws(TypeError, () => add.apply(Temporal.ZonedDateTime, args), "Temporal.ZonedDateTime");
+assert.throws(TypeError, () => add.apply(Temporal.ZonedDateTime.prototype, args), "Temporal.ZonedDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..c6d01713a3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.zoneddatetime.prototype.add
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dateAddOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dateAdd");
+Object.defineProperty(Temporal.Calendar.prototype, "dateAdd", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("dateAdd should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.add(new Temporal.Duration(1));
+
+Object.defineProperty(Temporal.Calendar.prototype, "dateAdd", dateAddOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/builtin-timezone-no-observable-calls.js
new file mode 100644
index 0000000000..b145bfd25c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/builtin-timezone-no-observable-calls.js
@@ -0,0 +1,37 @@
+// |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.zoneddatetime.prototype.add
+description: >
+ Calling the method on an instance constructed with a builtin time zone causes
+ no observable lookups or calls to time zone methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+const getPossibleInstantsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getPossibleInstantsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.add(new Temporal.Duration(1));
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", getPossibleInstantsForOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/builtin.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/builtin.js
new file mode 100644
index 0000000000..d075aa0649
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/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.zoneddatetime.prototype.add
+description: >
+ Tests that Temporal.ZonedDateTime.prototype.add
+ 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.ZonedDateTime.prototype.add),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.add),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.add),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.ZonedDateTime.prototype.add.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/calendar-dateadd.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/calendar-dateadd.js
new file mode 100644
index 0000000000..9cc7fd7766
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/calendar-dateadd.js
@@ -0,0 +1,33 @@
+// |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.zoneddatetime.prototype.add
+description: ZonedDateTime.prototype.add should call dateAdd with the appropriate values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ dateAdd(plainDate, duration, options) {
+ ++calls;
+ TemporalHelpers.assertPlainDate(plainDate, 2020, 3, "M03", 14, "plainDate argument");
+ TemporalHelpers.assertDuration(duration, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, "duration argument");
+ assert.sameValue(typeof options, "object", "options argument: type");
+ assert.sameValue(Object.getPrototypeOf(options), null, "options argument: prototype");
+ return super.dateAdd(plainDate, duration, options);
+ }
+}
+
+const zonedDateTime = new Temporal.ZonedDateTime(1584189296_987654321n,
+ new Temporal.TimeZone("UTC"), new CustomCalendar());
+const result = zonedDateTime.add({ months: 10, hours: 14 });
+assert.sameValue(result.epochNanoseconds, 1610678096_987654321n);
+assert.sameValue(calls, 1, "should have called dateAdd");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/length.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/length.js
new file mode 100644
index 0000000000..5b3b5b0b0e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/length.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.zoneddatetime.prototype.add
+description: Temporal.ZonedDateTime.prototype.add.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.ZonedDateTime.prototype.add, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/name.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/name.js
new file mode 100644
index 0000000000..e343ddaa3c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/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.zoneddatetime.prototype.add
+description: Temporal.ZonedDateTime.prototype.add.name is "add".
+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.ZonedDateTime.prototype.add, "name", {
+ value: "add",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/negative-epochnanoseconds.js
new file mode 100644
index 0000000000..b52a6329bd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/negative-epochnanoseconds.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.zoneddatetime.prototype.add
+description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch
+info: |
+ sec-temporal-getisopartsfromepoch step 1:
+ 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 10<sup>6</sup>.
+ sec-temporal-builtintimezonegetplaindatetimefor step 2:
+ 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]).
+features: [Temporal]
+---*/
+
+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 result = datetime.add(new Temporal.Duration(0, 0, 0, 1));
+assert.sameValue(result.epochNanoseconds, -13763364_999_999_999n);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/non-integer-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/non-integer-throws-rangeerror.js
new file mode 100644
index 0000000000..73165990f4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/non-integer-throws-rangeerror.js
@@ -0,0 +1,29 @@
+// |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.zoneddatetime.prototype.add
+description: A non-integer value for any recognized property in the property bag, throws a RangeError
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+const fields = [
+ "years",
+ "months",
+ "weeks",
+ "days",
+ "hours",
+ "minutes",
+ "seconds",
+ "milliseconds",
+ "microseconds",
+ "nanoseconds",
+];
+fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.add({ [field]: 1.5 }));
+ assert.throws(RangeError, () => instance.add({ [field]: -1.5 }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/not-a-constructor.js
new file mode 100644
index 0000000000..75195269d3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/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.zoneddatetime.prototype.add
+description: >
+ Temporal.ZonedDateTime.prototype.add 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.ZonedDateTime.prototype.add();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.add), false,
+ "isConstructor(Temporal.ZonedDateTime.prototype.add)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/options-object.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/options-object.js
new file mode 100644
index 0000000000..c38ecfff0a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/options-object.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.zoneddatetime.prototype.add
+description: Empty or a function object may be used as options
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(0n, "UTC");
+
+const result1 = instance.add({ years: 1 }, {});
+assert.sameValue(
+ result1.epochNanoseconds, 31536000000000000n, "UTC",
+ "options may be an empty plain object"
+);
+
+const result2 = instance.add({ years: 1 }, () => {});
+assert.sameValue(
+ result2.epochNanoseconds, 31536000000000000n, "UTC",
+ "options may be a function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/options-undefined.js
new file mode 100644
index 0000000000..307f1006f5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/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.zoneddatetime.prototype.add
+description: Verify that undefined options are handled correctly.
+features: [BigInt, Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(949322096_987_654_321n, "UTC");
+const duration = { months: 1 };
+
+const explicit = datetime.add(duration, undefined);
+assert.sameValue(explicit.month, 2, "default overflow is constrain");
+assert.sameValue(explicit.day, 29, "default overflow is constrain");
+
+const implicit = datetime.add(duration);
+assert.sameValue(implicit.month, 2, "default overflow is constrain");
+assert.sameValue(implicit.day, 29, "default overflow is constrain");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/options-wrong-type.js
new file mode 100644
index 0000000000..395864ed9d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/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.zoneddatetime.prototype.add
+description: TypeError thrown when options argument is a primitive
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const badOptions = [
+ null,
+ true,
+ "some string",
+ Symbol(),
+ 1,
+ 2n,
+];
+
+const instance = new Temporal.ZonedDateTime(0n, "UTC");
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.add({ years: 1 }, value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/order-of-operations.js
new file mode 100644
index 0000000000..26fc1c9299
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/order-of-operations.js
@@ -0,0 +1,84 @@
+// |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.zoneddatetime.prototype.add
+description: Properties on objects passed to add() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ // ToTemporalDurationRecord
+ "get duration.days",
+ "get duration.days.valueOf",
+ "call duration.days.valueOf",
+ "get duration.hours",
+ "get duration.hours.valueOf",
+ "call duration.hours.valueOf",
+ "get duration.microseconds",
+ "get duration.microseconds.valueOf",
+ "call duration.microseconds.valueOf",
+ "get duration.milliseconds",
+ "get duration.milliseconds.valueOf",
+ "call duration.milliseconds.valueOf",
+ "get duration.minutes",
+ "get duration.minutes.valueOf",
+ "call duration.minutes.valueOf",
+ "get duration.months",
+ "get duration.months.valueOf",
+ "call duration.months.valueOf",
+ "get duration.nanoseconds",
+ "get duration.nanoseconds.valueOf",
+ "call duration.nanoseconds.valueOf",
+ "get duration.seconds",
+ "get duration.seconds.valueOf",
+ "call duration.seconds.valueOf",
+ "get duration.weeks",
+ "get duration.weeks.valueOf",
+ "call duration.weeks.valueOf",
+ "get duration.years",
+ "get duration.years.valueOf",
+ "call duration.years.valueOf",
+ // lookup
+ "get this.timeZone.getOffsetNanosecondsFor",
+ "get this.timeZone.getPossibleInstantsFor",
+ "get this.calendar.dateAdd",
+ // AddZonedDateTime
+ "call this.timeZone.getOffsetNanosecondsFor",
+ "call this.calendar.dateAdd",
+ // ... inside Calendar.p.dateAdd
+ "get options.overflow",
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+ // AddZonedDateTime again
+ "call this.timeZone.getPossibleInstantsFor",
+];
+const actual = [];
+
+const timeZone = TemporalHelpers.timeZoneObserver(actual, "this.timeZone");
+const calendar = TemporalHelpers.calendarObserver(actual, "this.calendar");
+const instance = new Temporal.ZonedDateTime(0n, timeZone, calendar);
+// clear observable operations that occurred during the constructor call
+actual.splice(0);
+
+const duration = TemporalHelpers.propertyBagObserver(actual, {
+ years: 1,
+ months: 1,
+ weeks: 1,
+ days: 1,
+ hours: 1,
+ minutes: 1,
+ seconds: 1,
+ milliseconds: 1,
+ microseconds: 1,
+ nanoseconds: 1,
+}, "duration");
+
+const options = TemporalHelpers.propertyBagObserver(actual, { overflow: "constrain" }, "options");
+
+instance.add(duration, options);
+assert.compareArray(actual, expected, "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/overflow-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/overflow-invalid-string.js
new file mode 100644
index 0000000000..b46102175e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/overflow-invalid-string.js
@@ -0,0 +1,33 @@
+// |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.zoneddatetime.prototype.add
+description: RangeError thrown when overflow option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal.calendar.prototype.dateadd step 7:
+ 7. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal-addzoneddatetime step 6:
+ 6. Let _addedDate_ be ? CalendarDateAdd(_calendar_, _datePart_, _dateDuration_, _options_).
+ sec-temporal.zoneddatetime.prototype.add step 7:
+ 7. Let _epochNanoseconds_ be ? AddZonedDateTime(_zonedDateTime_.[[Nanoseconds]], _timeZone_, _calendar_, _duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], _options_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+const duration = new Temporal.Duration(0, 0, 0, 1);
+const badOverflows = ["", "CONSTRAIN", "balance", "other string", "constra\u0131n", "reject\0"];
+for (const overflow of badOverflows) {
+ assert.throws(
+ RangeError,
+ () => datetime.add(duration, { overflow }),
+ `invalid overflow ("${overflow}")`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/overflow-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/overflow-undefined.js
new file mode 100644
index 0000000000..9a2c129c68
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/overflow-undefined.js
@@ -0,0 +1,30 @@
+// |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.zoneddatetime.prototype.add
+description: Fallback value for overflow option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal.calendar.prototype.dateadd step 7:
+ 7. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal-addzoneddatetime step 6:
+ 6. Let _addedDate_ be ? CalendarDateAdd(_calendar_, _datePart_, _dateDuration_, _options_).
+ sec-temporal.zoneddatetime.prototype.add step 7:
+ 7. Let _epochNanoseconds_ be ? AddZonedDateTime(_zonedDateTime_.[[Nanoseconds]], _timeZone_, _calendar_, _duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], _options_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(-1n, "UTC");
+const duration = new Temporal.Duration(0, 2);
+
+const explicit = datetime.add(duration, { overflow: undefined });
+assert.sameValue(explicit.epochNanoseconds, 5097599_999_999_999n, "default overflow is constrain");
+const implicit = datetime.add(duration, {});
+assert.sameValue(implicit.epochNanoseconds, 5097599_999_999_999n, "default overflow is constrain");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/overflow-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/overflow-wrong-type.js
new file mode 100644
index 0000000000..7eeecf0643
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/overflow-wrong-type.js
@@ -0,0 +1,30 @@
+// |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.zoneddatetime.prototype.add
+description: Type conversions for overflow option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal.calendar.prototype.dateadd step 7:
+ 7. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal-addzoneddatetime step 6:
+ 6. Let _addedDate_ be ? CalendarDateAdd(_calendar_, _datePart_, _dateDuration_, _options_).
+ sec-temporal.zoneddatetime.prototype.add step 7:
+ 7. Let _epochNanoseconds_ be ? AddZonedDateTime(_zonedDateTime_.[[Nanoseconds]], _timeZone_, _calendar_, _duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+const duration = new Temporal.Duration(0, 0, 0, 1);
+TemporalHelpers.checkStringOptionWrongType("overflow", "constrain",
+ (overflow) => datetime.add(duration, { overflow }),
+ (result, descr) => assert.sameValue(result.epochNanoseconds, 1_000_086_400_987_654_321n, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/prop-desc.js
new file mode 100644
index 0000000000..b0332ada78
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/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.zoneddatetime.prototype.add
+description: The "add" property of Temporal.ZonedDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.ZonedDateTime.prototype.add,
+ "function",
+ "`typeof ZonedDateTime.prototype.add` is `function`"
+);
+
+verifyProperty(Temporal.ZonedDateTime.prototype, "add", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/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/ZonedDateTime/prototype/add/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/subclassing-ignored.js
new file mode 100644
index 0000000000..a8205a7ac3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/subclassing-ignored.js
@@ -0,0 +1,31 @@
+// |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.zoneddatetime.prototype.add
+description: Objects of a subclass are never created as return values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.ZonedDateTime,
+ [10n, "UTC"],
+ "add",
+ [{ nanoseconds: 5 }],
+ (result) => {
+ assert.sameValue(result.epochNanoseconds, 15n, "epochNanoseconds result");
+ assert.sameValue(result.year, 1970, "year result");
+ assert.sameValue(result.month, 1, "month result");
+ assert.sameValue(result.day, 1, "day result");
+ assert.sameValue(result.hour, 0, "hour result");
+ assert.sameValue(result.minute, 0, "minute result");
+ assert.sameValue(result.second, 0, "second result");
+ assert.sameValue(result.millisecond, 0, "millisecond result");
+ assert.sameValue(result.microsecond, 0, "microsecond result");
+ assert.sameValue(result.nanosecond, 15, "nanosecond result");
+ },
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..ceb2748710
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/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.zoneddatetime.prototype.add
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const duration = new Temporal.Duration(1);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.add(duration));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..d26e405e47
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/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.zoneddatetime.prototype.add
+description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable
+features: [BigInt, Symbol, Temporal, arrow-function]
+---*/
+
+[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => {
+ const timeZone = new Temporal.TimeZone("UTC");
+ const duration = new Temporal.Duration(1);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => datetime.add(duration),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..8359fc25fa
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/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.zoneddatetime.prototype.add
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const duration = new Temporal.Duration(1);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.add(duration));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..90b8633f64
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/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.zoneddatetime.prototype.add
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const duration = new Temporal.Duration(1);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.add(duration));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/calendarId/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/calendarId/branding.js
new file mode 100644
index 0000000000..b4a5359155
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/calendarId/branding.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-get-temporal.zoneddatetime.prototype.calendarid
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const calendarId = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "calendarId").get;
+
+assert.sameValue(typeof calendarId, "function");
+
+assert.throws(TypeError, () => calendarId.call(undefined), "undefined");
+assert.throws(TypeError, () => calendarId.call(null), "null");
+assert.throws(TypeError, () => calendarId.call(true), "true");
+assert.throws(TypeError, () => calendarId.call(""), "empty string");
+assert.throws(TypeError, () => calendarId.call(Symbol()), "symbol");
+assert.throws(TypeError, () => calendarId.call(1), "1");
+assert.throws(TypeError, () => calendarId.call({}), "plain object");
+assert.throws(TypeError, () => calendarId.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime");
+assert.throws(TypeError, () => calendarId.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/calendarId/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/calendarId/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/calendarId/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/calendarId/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/calendarId/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..57e69d096f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/calendarId/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.zoneddatetime.prototype.calendarid
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const idOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "id");
+Object.defineProperty(Temporal.Calendar.prototype, "id", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("id should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.calendarId;
+
+Object.defineProperty(Temporal.Calendar.prototype, "id", idOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/calendarId/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/calendarId/prop-desc.js
new file mode 100644
index 0000000000..3f846c190d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/calendarId/prop-desc.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-get-temporal.zoneddatetime.prototype.calendarid
+description: The "calendarId" property of Temporal.ZonedDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "calendarId");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/calendarId/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/calendarId/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/calendarId/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/constructor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/constructor.js
new file mode 100644
index 0000000000..469f48b369
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/constructor.js
@@ -0,0 +1,20 @@
+// |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.zoneddatetime.prototype.constructor
+description: Test for Temporal.ZonedDateTime.prototype.constructor.
+info: The initial value of Temporal.ZonedDateTime.prototype.constructor is %Temporal.ZonedDateTime%.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.ZonedDateTime.prototype, "constructor", {
+ value: Temporal.ZonedDateTime,
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/balance-negative-time-units.js
new file mode 100644
index 0000000000..d60c094ecd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/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.zoneddatetime.prototype.day
+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-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-get-temporal.zoneddatetime.prototype.day step 6:
+ 6. Let _temporalDateTime_ be ? BuiltinTimeZoneGetPlainDateTimeFor(_timeZone_, _instant_, _calendar_).
+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(86400_000_000_001n, tz);
+
+assert.sameValue(datetime.day, 1);
+assert.sameValue(datetime.hour, 23);
+assert.sameValue(datetime.minute, 59);
+assert.sameValue(datetime.second, 59);
+assert.sameValue(datetime.millisecond, 999);
+assert.sameValue(datetime.microsecond, 999);
+assert.sameValue(datetime.nanosecond, 999);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/branding.js
new file mode 100644
index 0000000000..bdd693e577
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/branding.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-get-temporal.zoneddatetime.prototype.day
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const day = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "day").get;
+
+assert.sameValue(typeof day, "function");
+
+assert.throws(TypeError, () => day.call(undefined), "undefined");
+assert.throws(TypeError, () => day.call(null), "null");
+assert.throws(TypeError, () => day.call(true), "true");
+assert.throws(TypeError, () => day.call(""), "empty string");
+assert.throws(TypeError, () => day.call(Symbol()), "symbol");
+assert.throws(TypeError, () => day.call(1), "1");
+assert.throws(TypeError, () => day.call({}), "plain object");
+assert.throws(TypeError, () => day.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime");
+assert.throws(TypeError, () => day.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..4588bd3268
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.zoneddatetime.prototype.day
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dayOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "day");
+Object.defineProperty(Temporal.Calendar.prototype, "day", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("day should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.day;
+
+Object.defineProperty(Temporal.Calendar.prototype, "day", dayOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/builtin-timezone-no-observable-calls.js
new file mode 100644
index 0000000000..9c13663214
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/builtin-timezone-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.zoneddatetime.prototype.day
+description: >
+ Calling the method on an instance constructed with a builtin time zone causes
+ no observable lookups or calls to time zone methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.day;
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/custom.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/custom.js
new file mode 100644
index 0000000000..2b66c43581
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/custom.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-get-temporal.zoneddatetime.prototype.day
+description: Custom calendar tests for day().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ day(...args) {
+ ++calls;
+ assert.compareArray(args.map(String), [instance].map((arg) => arg.toPlainDateTime().toString()), "day arguments");
+ return 7;
+ }
+}
+
+const calendar = new CustomCalendar();
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar);
+const result = instance.day;
+assert.sameValue(result, 7, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/prop-desc.js
new file mode 100644
index 0000000000..a24650e719
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/prop-desc.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-get-temporal.zoneddatetime.prototype.day
+description: The "day" property of Temporal.ZonedDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "day");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..984b2b5151
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,18 @@
+// |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-get-temporal.zoneddatetime.prototype.day
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.day);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..e9866ca2f0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/timezone-getoffsetnanosecondsfor-not-callable.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-get-temporal.zoneddatetime.prototype.day
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => datetime.day,
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..9c32c09188
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,18 @@
+// |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-get-temporal.zoneddatetime.prototype.day
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.day);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..360c9485bf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/timezone-getoffsetnanosecondsfor-wrong-type.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-get-temporal.zoneddatetime.prototype.day
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.day);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/validate-calendar-value.js
new file mode 100644
index 0000000000..4ea14ef7fa
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/validate-calendar-value.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-get-temporal.zoneddatetime.prototype.day
+description: Validate result returned from calendar day() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [null, TypeError],
+ [false, TypeError],
+ [Infinity, RangeError],
+ [-Infinity, RangeError],
+ [NaN, RangeError],
+ [-7, RangeError],
+ [-0.1, RangeError],
+ ["string", TypeError],
+ [Symbol("foo"), TypeError],
+ [7n, TypeError],
+ [{}, TypeError],
+ [true, TypeError],
+ [7.1, RangeError],
+ ["7", TypeError],
+ ["7.5", TypeError],
+ [{valueOf() { return 7; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ day() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar);
+ assert.throws(error, () => instance.day, `${typeof result} ${String(result)} not converted to positive integer`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/branding.js
new file mode 100644
index 0000000000..5dc562474e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/branding.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-get-temporal.zoneddatetime.prototype.dayofweek
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const dayOfWeek = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "dayOfWeek").get;
+
+assert.sameValue(typeof dayOfWeek, "function");
+
+assert.throws(TypeError, () => dayOfWeek.call(undefined), "undefined");
+assert.throws(TypeError, () => dayOfWeek.call(null), "null");
+assert.throws(TypeError, () => dayOfWeek.call(true), "true");
+assert.throws(TypeError, () => dayOfWeek.call(""), "empty string");
+assert.throws(TypeError, () => dayOfWeek.call(Symbol()), "symbol");
+assert.throws(TypeError, () => dayOfWeek.call(1), "1");
+assert.throws(TypeError, () => dayOfWeek.call({}), "plain object");
+assert.throws(TypeError, () => dayOfWeek.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime");
+assert.throws(TypeError, () => dayOfWeek.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..a694538559
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.zoneddatetime.prototype.dayofweek
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dayOfWeekOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dayOfWeek");
+Object.defineProperty(Temporal.Calendar.prototype, "dayOfWeek", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("dayOfWeek should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.dayOfWeek;
+
+Object.defineProperty(Temporal.Calendar.prototype, "dayOfWeek", dayOfWeekOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/builtin-timezone-no-observable-calls.js
new file mode 100644
index 0000000000..e4ebad62de
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/builtin-timezone-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.zoneddatetime.prototype.dayofweek
+description: >
+ Calling the method on an instance constructed with a builtin time zone causes
+ no observable lookups or calls to time zone methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.dayOfWeek;
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/custom.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/custom.js
new file mode 100644
index 0000000000..3904dc8f92
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/custom.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-get-temporal.zoneddatetime.prototype.dayofweek
+description: Custom calendar tests for dayOfWeek().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ dayOfWeek(...args) {
+ ++calls;
+ assert.compareArray(args.map(String), [instance].map((arg) => arg.toPlainDateTime().toString()), "dayOfWeek arguments");
+ return 7;
+ }
+}
+
+const calendar = new CustomCalendar();
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar);
+const result = instance.dayOfWeek;
+assert.sameValue(result, 7, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/prop-desc.js
new file mode 100644
index 0000000000..4092408b2f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/prop-desc.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-get-temporal.zoneddatetime.prototype.dayofweek
+description: The "dayOfWeek" property of Temporal.ZonedDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "dayOfWeek");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..d96b7aeee8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,18 @@
+// |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-get-temporal.zoneddatetime.prototype.dayofweek
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.dayOfWeek);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..0656f5826b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/timezone-getoffsetnanosecondsfor-not-callable.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-get-temporal.zoneddatetime.prototype.dayofweek
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => datetime.dayOfWeek,
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..61678b900c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,18 @@
+// |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-get-temporal.zoneddatetime.prototype.dayofweek
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.dayOfWeek);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..787e54216f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/timezone-getoffsetnanosecondsfor-wrong-type.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-get-temporal.zoneddatetime.prototype.dayofweek
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.dayOfWeek);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/validate-calendar-value.js
new file mode 100644
index 0000000000..b8d1a08b7f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/validate-calendar-value.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-get-temporal.zoneddatetime.prototype.dayofweek
+description: Validate result returned from calendar dayOfWeek() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [null, TypeError],
+ [false, TypeError],
+ [Infinity, RangeError],
+ [-Infinity, RangeError],
+ [NaN, RangeError],
+ [-7, RangeError],
+ [-0.1, RangeError],
+ ["string", TypeError],
+ [Symbol("foo"), TypeError],
+ [7n, TypeError],
+ [{}, TypeError],
+ [true, TypeError],
+ [7.1, RangeError],
+ ["7", TypeError],
+ ["7.5", TypeError],
+ [{valueOf() { return 7; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ dayOfWeek() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar);
+ assert.throws(error, () => instance.dayOfWeek, `${typeof result} ${String(result)} not converted to positive integer`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/branding.js
new file mode 100644
index 0000000000..2aeb7d146f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/branding.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-get-temporal.zoneddatetime.prototype.dayofyear
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const dayOfYear = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "dayOfYear").get;
+
+assert.sameValue(typeof dayOfYear, "function");
+
+assert.throws(TypeError, () => dayOfYear.call(undefined), "undefined");
+assert.throws(TypeError, () => dayOfYear.call(null), "null");
+assert.throws(TypeError, () => dayOfYear.call(true), "true");
+assert.throws(TypeError, () => dayOfYear.call(""), "empty string");
+assert.throws(TypeError, () => dayOfYear.call(Symbol()), "symbol");
+assert.throws(TypeError, () => dayOfYear.call(1), "1");
+assert.throws(TypeError, () => dayOfYear.call({}), "plain object");
+assert.throws(TypeError, () => dayOfYear.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime");
+assert.throws(TypeError, () => dayOfYear.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..e33ff51617
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.zoneddatetime.prototype.dayofyear
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dayOfYearOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dayOfYear");
+Object.defineProperty(Temporal.Calendar.prototype, "dayOfYear", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("dayOfYear should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.dayOfYear;
+
+Object.defineProperty(Temporal.Calendar.prototype, "dayOfYear", dayOfYearOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/builtin-timezone-no-observable-calls.js
new file mode 100644
index 0000000000..b9eb0b3f5e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/builtin-timezone-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.zoneddatetime.prototype.dayofyear
+description: >
+ Calling the method on an instance constructed with a builtin time zone causes
+ no observable lookups or calls to time zone methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.dayOfYear;
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/custom.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/custom.js
new file mode 100644
index 0000000000..7823d2e9f8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/custom.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-get-temporal.zoneddatetime.prototype.dayofyear
+description: Custom calendar tests for dayOfYear().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ dayOfYear(...args) {
+ ++calls;
+ assert.compareArray(args.map(String), [instance].map((arg) => arg.toPlainDateTime().toString()), "dayOfYear arguments");
+ return 7;
+ }
+}
+
+const calendar = new CustomCalendar();
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar);
+const result = instance.dayOfYear;
+assert.sameValue(result, 7, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/prop-desc.js
new file mode 100644
index 0000000000..f43bc5310e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/prop-desc.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-get-temporal.zoneddatetime.prototype.dayofyear
+description: The "dayOfYear" property of Temporal.ZonedDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "dayOfYear");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..7b4c325aa1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,18 @@
+// |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-get-temporal.zoneddatetime.prototype.dayofyear
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.dayOfYear);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..2adbe4f754
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/timezone-getoffsetnanosecondsfor-not-callable.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-get-temporal.zoneddatetime.prototype.dayofyear
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => datetime.dayOfYear,
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..a8ab2fc73f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,18 @@
+// |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-get-temporal.zoneddatetime.prototype.dayofyear
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.dayOfYear);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..7479cf1bc7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/timezone-getoffsetnanosecondsfor-wrong-type.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-get-temporal.zoneddatetime.prototype.dayofyear
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.dayOfYear);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/validate-calendar-value.js
new file mode 100644
index 0000000000..b2f7a1bb7c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/validate-calendar-value.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-get-temporal.zoneddatetime.prototype.dayofyear
+description: Validate result returned from calendar dayOfYear() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [null, TypeError],
+ [false, TypeError],
+ [Infinity, RangeError],
+ [-Infinity, RangeError],
+ [NaN, RangeError],
+ [-7, RangeError],
+ [-0.1, RangeError],
+ ["string", TypeError],
+ [Symbol("foo"), TypeError],
+ [7n, TypeError],
+ [{}, TypeError],
+ [true, TypeError],
+ [7.1, RangeError],
+ ["7", TypeError],
+ ["7.5", TypeError],
+ [{valueOf() { return 7; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ dayOfYear() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar);
+ assert.throws(error, () => instance.dayOfYear, `${typeof result} ${String(result)} not converted to positive integer`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/branding.js
new file mode 100644
index 0000000000..cfa594980e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/branding.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-get-temporal.zoneddatetime.prototype.daysinmonth
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const daysInMonth = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "daysInMonth").get;
+
+assert.sameValue(typeof daysInMonth, "function");
+
+assert.throws(TypeError, () => daysInMonth.call(undefined), "undefined");
+assert.throws(TypeError, () => daysInMonth.call(null), "null");
+assert.throws(TypeError, () => daysInMonth.call(true), "true");
+assert.throws(TypeError, () => daysInMonth.call(""), "empty string");
+assert.throws(TypeError, () => daysInMonth.call(Symbol()), "symbol");
+assert.throws(TypeError, () => daysInMonth.call(1), "1");
+assert.throws(TypeError, () => daysInMonth.call({}), "plain object");
+assert.throws(TypeError, () => daysInMonth.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime");
+assert.throws(TypeError, () => daysInMonth.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..4ef8abe93b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.zoneddatetime.prototype.daysinmonth
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const daysInMonthOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "daysInMonth");
+Object.defineProperty(Temporal.Calendar.prototype, "daysInMonth", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("daysInMonth should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.daysInMonth;
+
+Object.defineProperty(Temporal.Calendar.prototype, "daysInMonth", daysInMonthOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/builtin-timezone-no-observable-calls.js
new file mode 100644
index 0000000000..037834f04f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/builtin-timezone-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.zoneddatetime.prototype.daysinmonth
+description: >
+ Calling the method on an instance constructed with a builtin time zone causes
+ no observable lookups or calls to time zone methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.daysInMonth;
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/custom.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/custom.js
new file mode 100644
index 0000000000..c6c23338df
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/custom.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-get-temporal.zoneddatetime.prototype.daysinmonth
+description: Custom calendar tests for daysInMonth().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ daysInMonth(...args) {
+ ++calls;
+ assert.compareArray(args.map(String), [instance].map((arg) => arg.toPlainDateTime().toString()), "daysInMonth arguments");
+ return 7;
+ }
+}
+
+const calendar = new CustomCalendar();
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar);
+const result = instance.daysInMonth;
+assert.sameValue(result, 7, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/prop-desc.js
new file mode 100644
index 0000000000..4597630f20
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/prop-desc.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-get-temporal.zoneddatetime.prototype.daysinmonth
+description: The "daysInMonth" property of Temporal.ZonedDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "daysInMonth");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..ab7334b0f1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,18 @@
+// |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-get-temporal.zoneddatetime.prototype.daysinmonth
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.daysInMonth);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..9cca406d6a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/timezone-getoffsetnanosecondsfor-not-callable.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-get-temporal.zoneddatetime.prototype.daysinmonth
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => datetime.daysInMonth,
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..e64f3b4966
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,18 @@
+// |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-get-temporal.zoneddatetime.prototype.daysinmonth
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.daysInMonth);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..f61a214c3e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/timezone-getoffsetnanosecondsfor-wrong-type.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-get-temporal.zoneddatetime.prototype.daysinmonth
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.daysInMonth);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/validate-calendar-value.js
new file mode 100644
index 0000000000..d54b1355f3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/validate-calendar-value.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-get-temporal.zoneddatetime.prototype.daysinmonth
+description: Validate result returned from calendar daysInMonth() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [null, TypeError],
+ [false, TypeError],
+ [Infinity, RangeError],
+ [-Infinity, RangeError],
+ [NaN, RangeError],
+ [-7, RangeError],
+ [-0.1, RangeError],
+ ["string", TypeError],
+ [Symbol("foo"), TypeError],
+ [7n, TypeError],
+ [{}, TypeError],
+ [true, TypeError],
+ [7.1, RangeError],
+ ["7", TypeError],
+ ["7.5", TypeError],
+ [{valueOf() { return 7; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ daysInMonth() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar);
+ assert.throws(error, () => instance.daysInMonth, `${typeof result} ${String(result)} not converted to positive integer`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/branding.js
new file mode 100644
index 0000000000..6dcdc66d0a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/branding.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-get-temporal.zoneddatetime.prototype.daysinweek
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const daysInWeek = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "daysInWeek").get;
+
+assert.sameValue(typeof daysInWeek, "function");
+
+assert.throws(TypeError, () => daysInWeek.call(undefined), "undefined");
+assert.throws(TypeError, () => daysInWeek.call(null), "null");
+assert.throws(TypeError, () => daysInWeek.call(true), "true");
+assert.throws(TypeError, () => daysInWeek.call(""), "empty string");
+assert.throws(TypeError, () => daysInWeek.call(Symbol()), "symbol");
+assert.throws(TypeError, () => daysInWeek.call(1), "1");
+assert.throws(TypeError, () => daysInWeek.call({}), "plain object");
+assert.throws(TypeError, () => daysInWeek.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime");
+assert.throws(TypeError, () => daysInWeek.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..a411c0305a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.zoneddatetime.prototype.daysinweek
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const daysInWeekOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "daysInWeek");
+Object.defineProperty(Temporal.Calendar.prototype, "daysInWeek", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("daysInWeek should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.daysInWeek;
+
+Object.defineProperty(Temporal.Calendar.prototype, "daysInWeek", daysInWeekOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/builtin-timezone-no-observable-calls.js
new file mode 100644
index 0000000000..aae90307fa
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/builtin-timezone-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.zoneddatetime.prototype.daysinweek
+description: >
+ Calling the method on an instance constructed with a builtin time zone causes
+ no observable lookups or calls to time zone methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.daysInWeek;
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/custom.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/custom.js
new file mode 100644
index 0000000000..c47b290cd3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/custom.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-get-temporal.zoneddatetime.prototype.daysinweek
+description: Custom calendar tests for daysInWeek().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ daysInWeek(...args) {
+ ++calls;
+ assert.compareArray(args.map(String), [instance].map((arg) => arg.toPlainDateTime().toString()), "daysInWeek arguments");
+ return 7;
+ }
+}
+
+const calendar = new CustomCalendar();
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar);
+const result = instance.daysInWeek;
+assert.sameValue(result, 7, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/prop-desc.js
new file mode 100644
index 0000000000..8681bb5439
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/prop-desc.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-get-temporal.zoneddatetime.prototype.daysinweek
+description: The "daysInWeek" property of Temporal.ZonedDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "daysInWeek");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..a590eea359
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,18 @@
+// |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-get-temporal.zoneddatetime.prototype.daysinweek
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.daysInWeek);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..813b712cc8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/timezone-getoffsetnanosecondsfor-not-callable.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-get-temporal.zoneddatetime.prototype.daysinweek
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => datetime.daysInWeek,
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..1a4eaa2717
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,18 @@
+// |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-get-temporal.zoneddatetime.prototype.daysinweek
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.daysInWeek);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..a16ca75372
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/timezone-getoffsetnanosecondsfor-wrong-type.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-get-temporal.zoneddatetime.prototype.daysinweek
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.daysInWeek);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/validate-calendar-value.js
new file mode 100644
index 0000000000..41ae7b68f9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/validate-calendar-value.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-get-temporal.zoneddatetime.prototype.daysinweek
+description: Validate result returned from calendar daysInWeek() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [null, TypeError],
+ [false, TypeError],
+ [Infinity, RangeError],
+ [-Infinity, RangeError],
+ [NaN, RangeError],
+ [-7, RangeError],
+ [-0.1, RangeError],
+ ["string", TypeError],
+ [Symbol("foo"), TypeError],
+ [7n, TypeError],
+ [{}, TypeError],
+ [true, TypeError],
+ [7.1, RangeError],
+ ["7", TypeError],
+ ["7.5", TypeError],
+ [{valueOf() { return 7; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ daysInWeek() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar);
+ assert.throws(error, () => instance.daysInWeek, `${typeof result} ${String(result)} not converted to positive integer`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/basic.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/basic.js
new file mode 100644
index 0000000000..1d18d79ffd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/basic.js
@@ -0,0 +1,16 @@
+// |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-get-temporal.zoneddatetime.prototype.daysinyear
+description: Checking days in year for a "normal" case (non-undefined, non-boundary case, etc.)
+features: [Temporal]
+---*/
+
+assert.sameValue((new Temporal.ZonedDateTime(217178610123456789n, "UTC")).daysInYear,
+ 366, "leap year");
+assert.sameValue((new Temporal.ZonedDateTime(248714610123456789n, "UTC")).daysInYear,
+ 365, "non-leap year");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/branding.js
new file mode 100644
index 0000000000..7435cd0e62
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/branding.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-get-temporal.zoneddatetime.prototype.daysinyear
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const daysInYear = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "daysInYear").get;
+
+assert.sameValue(typeof daysInYear, "function");
+
+assert.throws(TypeError, () => daysInYear.call(undefined), "undefined");
+assert.throws(TypeError, () => daysInYear.call(null), "null");
+assert.throws(TypeError, () => daysInYear.call(true), "true");
+assert.throws(TypeError, () => daysInYear.call(""), "empty string");
+assert.throws(TypeError, () => daysInYear.call(Symbol()), "symbol");
+assert.throws(TypeError, () => daysInYear.call(1), "1");
+assert.throws(TypeError, () => daysInYear.call({}), "plain object");
+assert.throws(TypeError, () => daysInYear.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime");
+assert.throws(TypeError, () => daysInYear.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..ca8317011d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.zoneddatetime.prototype.daysinyear
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const daysInYearOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "daysInYear");
+Object.defineProperty(Temporal.Calendar.prototype, "daysInYear", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("daysInYear should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.daysInYear;
+
+Object.defineProperty(Temporal.Calendar.prototype, "daysInYear", daysInYearOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/builtin-timezone-no-observable-calls.js
new file mode 100644
index 0000000000..0584ccaf41
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/builtin-timezone-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.zoneddatetime.prototype.daysinyear
+description: >
+ Calling the method on an instance constructed with a builtin time zone causes
+ no observable lookups or calls to time zone methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.daysInYear;
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/custom.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/custom.js
new file mode 100644
index 0000000000..21f10a4aec
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/custom.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-get-temporal.zoneddatetime.prototype.daysinyear
+description: Custom calendar tests for daysInYear().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ daysInYear(...args) {
+ ++calls;
+ assert.compareArray(args.map(String), [instance].map((arg) => arg.toPlainDateTime().toString()), "daysInYear arguments");
+ return 7;
+ }
+}
+
+const calendar = new CustomCalendar();
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar);
+const result = instance.daysInYear;
+assert.sameValue(result, 7, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/prop-desc.js
new file mode 100644
index 0000000000..01bfaeabfc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/prop-desc.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-get-temporal.zoneddatetime.prototype.daysinyear
+description: The "daysInYear" property of Temporal.ZonedDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "daysInYear");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..721f9d20bf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,18 @@
+// |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-get-temporal.zoneddatetime.prototype.daysinyear
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.daysInYear);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..070b74b163
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/timezone-getoffsetnanosecondsfor-not-callable.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-get-temporal.zoneddatetime.prototype.daysinyear
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => datetime.daysInYear,
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..b3560091be
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,18 @@
+// |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-get-temporal.zoneddatetime.prototype.daysinyear
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.daysInYear);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..7cb221a3e8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/timezone-getoffsetnanosecondsfor-wrong-type.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-get-temporal.zoneddatetime.prototype.daysinyear
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.daysInYear);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/validate-calendar-value.js
new file mode 100644
index 0000000000..f706d136ca
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/validate-calendar-value.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-get-temporal.zoneddatetime.prototype.daysinyear
+description: Validate result returned from calendar daysInYear() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [null, TypeError],
+ [false, TypeError],
+ [Infinity, RangeError],
+ [-Infinity, RangeError],
+ [NaN, RangeError],
+ [-7, RangeError],
+ [-0.1, RangeError],
+ ["string", TypeError],
+ [Symbol("foo"), TypeError],
+ [7n, TypeError],
+ [{}, TypeError],
+ [true, TypeError],
+ [7.1, RangeError],
+ ["7", TypeError],
+ ["7.5", TypeError],
+ [{valueOf() { return 7; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ daysInYear() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar);
+ assert.throws(error, () => instance.daysInYear, `${typeof result} ${String(result)} not converted to positive integer`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMicroseconds/basic.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMicroseconds/basic.js
new file mode 100644
index 0000000000..7614771a58
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMicroseconds/basic.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-get-temporal.zoneddatetime.prototype.epochmicroseconds
+description: Basic tests for epochMicroseconds.
+features: [BigInt, Temporal]
+---*/
+
+const afterEpoch = new Temporal.ZonedDateTime(217175010_123_456_789n, "UTC");
+assert.sameValue(afterEpoch.epochMicroseconds, 217175010_123_456n, "epochMicroseconds post epoch");
+assert.sameValue(typeof afterEpoch.epochMicroseconds, "bigint", "epochMicroseconds value is a bigint");
+
+const beforeEpoch = new Temporal.ZonedDateTime(-217175010_876_543_211n, "UTC");
+assert.sameValue(beforeEpoch.epochMicroseconds, -217175010_876_544n, "epochMicroseconds pre epoch");
+assert.sameValue(typeof beforeEpoch.epochMicroseconds, "bigint", "epochMicroseconds value is a bigint");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMicroseconds/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMicroseconds/branding.js
new file mode 100644
index 0000000000..b3c21df8ff
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMicroseconds/branding.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-get-temporal.zoneddatetime.prototype.epochmicroseconds
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const epochMicroseconds = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "epochMicroseconds").get;
+
+assert.sameValue(typeof epochMicroseconds, "function");
+
+assert.throws(TypeError, () => epochMicroseconds.call(undefined), "undefined");
+assert.throws(TypeError, () => epochMicroseconds.call(null), "null");
+assert.throws(TypeError, () => epochMicroseconds.call(true), "true");
+assert.throws(TypeError, () => epochMicroseconds.call(""), "empty string");
+assert.throws(TypeError, () => epochMicroseconds.call(Symbol()), "symbol");
+assert.throws(TypeError, () => epochMicroseconds.call(1), "1");
+assert.throws(TypeError, () => epochMicroseconds.call({}), "plain object");
+assert.throws(TypeError, () => epochMicroseconds.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime");
+assert.throws(TypeError, () => epochMicroseconds.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMicroseconds/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMicroseconds/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMicroseconds/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMicroseconds/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMicroseconds/prop-desc.js
new file mode 100644
index 0000000000..148ab30b80
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMicroseconds/prop-desc.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-get-temporal.zoneddatetime.prototype.epochmicroseconds
+description: The "epochMicroseconds" property of Temporal.ZonedDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "epochMicroseconds");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMicroseconds/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMicroseconds/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMicroseconds/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMilliseconds/basic.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMilliseconds/basic.js
new file mode 100644
index 0000000000..6a463a167b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMilliseconds/basic.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-get-temporal.zoneddatetime.prototype.epochmilliseconds
+description: Basic tests for epochMilliseconds.
+features: [BigInt, Temporal]
+---*/
+
+const afterEpoch = new Temporal.ZonedDateTime(217175010_123_456_789n, "UTC");
+assert.sameValue(afterEpoch.epochMilliseconds, 217175010_123, "epochMilliseconds post epoch");
+assert.sameValue(typeof afterEpoch.epochMilliseconds, "number", "epochMilliseconds value is a number");
+
+const beforeEpoch = new Temporal.ZonedDateTime(-217175010_876_543_211n, "UTC");
+assert.sameValue(beforeEpoch.epochMilliseconds, -217175010_877, "epochMilliseconds pre epoch");
+assert.sameValue(typeof beforeEpoch.epochMilliseconds, "number", "epochMilliseconds value is a number");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMilliseconds/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMilliseconds/branding.js
new file mode 100644
index 0000000000..225ceb6860
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMilliseconds/branding.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-get-temporal.zoneddatetime.prototype.epochmilliseconds
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const epochMilliseconds = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "epochMilliseconds").get;
+
+assert.sameValue(typeof epochMilliseconds, "function");
+
+assert.throws(TypeError, () => epochMilliseconds.call(undefined), "undefined");
+assert.throws(TypeError, () => epochMilliseconds.call(null), "null");
+assert.throws(TypeError, () => epochMilliseconds.call(true), "true");
+assert.throws(TypeError, () => epochMilliseconds.call(""), "empty string");
+assert.throws(TypeError, () => epochMilliseconds.call(Symbol()), "symbol");
+assert.throws(TypeError, () => epochMilliseconds.call(1), "1");
+assert.throws(TypeError, () => epochMilliseconds.call({}), "plain object");
+assert.throws(TypeError, () => epochMilliseconds.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime");
+assert.throws(TypeError, () => epochMilliseconds.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMilliseconds/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMilliseconds/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMilliseconds/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMilliseconds/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMilliseconds/prop-desc.js
new file mode 100644
index 0000000000..35a00eaa9a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMilliseconds/prop-desc.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-get-temporal.zoneddatetime.prototype.epochmilliseconds
+description: The "epochMilliseconds" property of Temporal.ZonedDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "epochMilliseconds");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMilliseconds/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMilliseconds/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMilliseconds/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochNanoseconds/basic.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochNanoseconds/basic.js
new file mode 100644
index 0000000000..f0237adf44
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochNanoseconds/basic.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-get-temporal.zoneddatetime.prototype.epochnanoseconds
+description: Basic tests for epochNanoseconds.
+features: [BigInt, Temporal]
+---*/
+
+const afterEpoch = new Temporal.ZonedDateTime(217175010_123_456_789n, "UTC");
+assert.sameValue(afterEpoch.epochNanoseconds, 217175010_123_456_789n, "epochNanoseconds post epoch");
+assert.sameValue(typeof afterEpoch.epochNanoseconds, "bigint", "epochNanoseconds value is a bigint");
+
+const beforeEpoch = new Temporal.ZonedDateTime(-217175010_876_543_211n, "UTC");
+assert.sameValue(beforeEpoch.epochNanoseconds, -217175010_876_543_211n, "epochNanoseconds pre epoch");
+assert.sameValue(typeof beforeEpoch.epochNanoseconds, "bigint", "epochNanoseconds value is a bigint");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochNanoseconds/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochNanoseconds/branding.js
new file mode 100644
index 0000000000..6cff52795b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochNanoseconds/branding.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-get-temporal.zoneddatetime.prototype.epochnanoseconds
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const epochNanoseconds = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "epochNanoseconds").get;
+
+assert.sameValue(typeof epochNanoseconds, "function");
+
+assert.throws(TypeError, () => epochNanoseconds.call(undefined), "undefined");
+assert.throws(TypeError, () => epochNanoseconds.call(null), "null");
+assert.throws(TypeError, () => epochNanoseconds.call(true), "true");
+assert.throws(TypeError, () => epochNanoseconds.call(""), "empty string");
+assert.throws(TypeError, () => epochNanoseconds.call(Symbol()), "symbol");
+assert.throws(TypeError, () => epochNanoseconds.call(1), "1");
+assert.throws(TypeError, () => epochNanoseconds.call({}), "plain object");
+assert.throws(TypeError, () => epochNanoseconds.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime");
+assert.throws(TypeError, () => epochNanoseconds.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochNanoseconds/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochNanoseconds/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochNanoseconds/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochNanoseconds/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochNanoseconds/prop-desc.js
new file mode 100644
index 0000000000..e1339f4e60
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochNanoseconds/prop-desc.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-get-temporal.zoneddatetime.prototype.epochnanoseconds
+description: The "epochNanoseconds" property of Temporal.ZonedDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "epochNanoseconds");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochNanoseconds/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochNanoseconds/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochNanoseconds/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochSeconds/basic.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochSeconds/basic.js
new file mode 100644
index 0000000000..4e1015d977
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochSeconds/basic.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-get-temporal.zoneddatetime.prototype.epochseconds
+description: Basic tests for epochSeconds.
+features: [BigInt, Temporal]
+---*/
+
+const afterEpoch = new Temporal.ZonedDateTime(217175010_123_456_789n, "UTC");
+assert.sameValue(afterEpoch.epochSeconds, 217175010, "epochSeconds post epoch");
+assert.sameValue(typeof afterEpoch.epochSeconds, "number", "epochSeconds value is a number");
+
+const beforeEpoch = new Temporal.ZonedDateTime(-217175010_876_543_211n, "UTC");
+assert.sameValue(beforeEpoch.epochSeconds, -217175011, "epochSeconds pre epoch");
+assert.sameValue(typeof beforeEpoch.epochSeconds, "number", "epochSeconds value is a number");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochSeconds/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochSeconds/branding.js
new file mode 100644
index 0000000000..85f7a3e463
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochSeconds/branding.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-get-temporal.zoneddatetime.prototype.epochseconds
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const epochSeconds = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "epochSeconds").get;
+
+assert.sameValue(typeof epochSeconds, "function");
+
+assert.throws(TypeError, () => epochSeconds.call(undefined), "undefined");
+assert.throws(TypeError, () => epochSeconds.call(null), "null");
+assert.throws(TypeError, () => epochSeconds.call(true), "true");
+assert.throws(TypeError, () => epochSeconds.call(""), "empty string");
+assert.throws(TypeError, () => epochSeconds.call(Symbol()), "symbol");
+assert.throws(TypeError, () => epochSeconds.call(1), "1");
+assert.throws(TypeError, () => epochSeconds.call({}), "plain object");
+assert.throws(TypeError, () => epochSeconds.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime");
+assert.throws(TypeError, () => epochSeconds.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochSeconds/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochSeconds/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochSeconds/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochSeconds/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochSeconds/prop-desc.js
new file mode 100644
index 0000000000..cc70ea2d58
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochSeconds/prop-desc.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-get-temporal.zoneddatetime.prototype.epochseconds
+description: The "epochSeconds" property of Temporal.ZonedDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "epochSeconds");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochSeconds/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochSeconds/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochSeconds/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..5434ebff4d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-builtin-calendar-no-array-iteration.js
@@ -0,0 +1,25 @@
+// |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.zoneddatetime.prototype.equals
+description: >
+ Calling the method with a property bag argument with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const timeZone = "UTC";
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+const arg = { year: 2000, month: 5, day: 2, hour: 21, minute: 43, second: 5, timeZone, calendar: "iso8601" };
+instance.equals(arg);
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-ambiguous-wall-clock-time.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-ambiguous-wall-clock-time.js
new file mode 100644
index 0000000000..8cc0eeae30
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-ambiguous-wall-clock-time.js
@@ -0,0 +1,93 @@
+// |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.zoneddatetime.prototype.equals
+description: >
+ Correct time zone calls are made when converting a ZonedDateTime-like property
+ bag denoting an ambiguous wall-clock time
+includes: [temporalHelpers.js, compareArray.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+
+const dstTimeZone = TemporalHelpers.springForwardFallBackTimeZone();
+const dstTimeZoneObserver = TemporalHelpers.timeZoneObserver(actual, "timeZone", {
+ getOffsetNanosecondsFor: dstTimeZone.getOffsetNanosecondsFor.bind(dstTimeZone),
+ getPossibleInstantsFor: dstTimeZone.getPossibleInstantsFor.bind(dstTimeZone),
+});
+const calendar = TemporalHelpers.calendarObserver(actual, "calendar");
+
+const timeZone = "UTC";
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+
+let arg = { year: 2000, month: 4, day: 2, hour: 2, minute: 30, timeZone: dstTimeZoneObserver, calendar };
+instance.equals(arg);
+
+const expected = [
+ // GetTemporalCalendarSlotValueWithISODefault
+ "has calendar.dateAdd",
+ "has calendar.dateFromFields",
+ "has calendar.dateUntil",
+ "has calendar.day",
+ "has calendar.dayOfWeek",
+ "has calendar.dayOfYear",
+ "has calendar.daysInMonth",
+ "has calendar.daysInWeek",
+ "has calendar.daysInYear",
+ "has calendar.fields",
+ "has calendar.id",
+ "has calendar.inLeapYear",
+ "has calendar.mergeFields",
+ "has calendar.month",
+ "has calendar.monthCode",
+ "has calendar.monthDayFromFields",
+ "has calendar.monthsInYear",
+ "has calendar.weekOfYear",
+ "has calendar.year",
+ "has calendar.yearMonthFromFields",
+ "has calendar.yearOfWeek",
+ // lookup in ToTemporalZonedDateTime
+ "get calendar.dateFromFields",
+ "get calendar.fields",
+ // CalendarFields
+ "call calendar.fields",
+ // ToTemporalTimeZoneSlotValue
+ "has timeZone.getOffsetNanosecondsFor",
+ "has timeZone.getPossibleInstantsFor",
+ "has timeZone.id",
+ // InterpretTemporalDateTimeFields
+ "call calendar.dateFromFields",
+ // lookup in ToTemporalZonedDateTime
+ "get timeZone.getOffsetNanosecondsFor",
+ "get timeZone.getPossibleInstantsFor",
+ // InterpretISODateTimeOffset
+ "call timeZone.getPossibleInstantsFor",
+];
+
+const expectedSpringForward = expected.concat([
+ // DisambiguatePossibleInstants
+ "call timeZone.getOffsetNanosecondsFor",
+ "call timeZone.getOffsetNanosecondsFor",
+ "call timeZone.getPossibleInstantsFor",
+]);
+assert.compareArray(
+ actual.slice(0, expectedSpringForward.length), // ignore operations after ToTemporalZonedDateTime
+ expectedSpringForward,
+ "order of operations converting property bag at skipped wall-clock time"
+);
+actual.splice(0); // clear
+
+arg = { year: 2000, month: 10, day: 29, hour: 1, minute: 30, timeZone: dstTimeZoneObserver, calendar };
+instance.equals(arg);
+
+assert.compareArray(
+ actual.slice(0, expected.length), // ignore operations after ToTemporalZonedDateTime
+ expected,
+ "order of operations converting property bag at repeated wall-clock time"
+);
+actual.splice(0); // clear
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-calendar-case-insensitive.js
new file mode 100644
index 0000000000..7700d1ab41
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-calendar-case-insensitive.js
@@ -0,0 +1,20 @@
+// |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.zoneddatetime.prototype.equals
+description: The calendar name is case-insensitive
+features: [Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+
+const calendar = "IsO8601";
+
+const arg = { year: 1970, monthCode: "M01", day: 1, timeZone, calendar };
+const result = instance.equals(arg);
+assert.sameValue(result, true, "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-calendar-leap-second.js
new file mode 100644
index 0000000000..453535b8f0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-calendar-leap-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.zoneddatetime.prototype.equals
+description: Leap second is a valid ISO string for a calendar in a property bag
+features: [Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+
+const calendar = "2016-12-31T23:59:60+00:00[UTC]";
+
+const arg = { year: 1970, monthCode: "M01", day: 1, timeZone, calendar };
+const result = instance.equals(arg);
+assert.sameValue(
+ result,
+ true,
+ "leap second is a valid ISO string for calendar"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-calendar-number.js
new file mode 100644
index 0000000000..a5fdb1d028
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-calendar-number.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.zoneddatetime.prototype.equals
+description: A number as calendar in a property bag is not accepted
+features: [Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+
+const numbers = [
+ 1,
+ 19970327,
+ -19970327,
+ 1234567890,
+];
+
+for (const calendar of numbers) {
+ const arg = { year: 1970, monthCode: "M01", day: 1, timeZone, calendar };
+ assert.throws(
+ TypeError,
+ () => instance.equals(arg),
+ "Numbers cannot be used as a calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-calendar-string.js
new file mode 100644
index 0000000000..b12b721272
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-calendar-string.js
@@ -0,0 +1,20 @@
+// |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.zoneddatetime.prototype.equals
+description: A calendar ID is valid input for Calendar
+features: [Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+
+const calendar = "iso8601";
+
+const arg = { year: 1970, monthCode: "M01", day: 1, timeZone, calendar };
+const result = instance.equals(arg);
+assert.sameValue(result, true, `Calendar created from string "${calendar}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-calendar-wrong-type.js
new file mode 100644
index 0000000000..dc6dcf3094
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-calendar-wrong-type.js
@@ -0,0 +1,46 @@
+// |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.zoneddatetime.prototype.equals
+description: >
+ Appropriate error thrown when a calendar property from a property bag cannot
+ be converted to a calendar object or string
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [calendar, description] of primitiveTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ typeof calendar === 'string' ? RangeError : TypeError,
+ () => instance.equals(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+ [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields()
+];
+
+for (const [calendar, description] of typeErrorTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(TypeError, () => instance.equals(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/ZonedDateTime/prototype/equals/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-calendar-year-zero.js
new file mode 100644
index 0000000000..c0230f2f3e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-calendar-year-zero.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.zoneddatetime.prototype.equals
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T17:45",
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+01:00",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-getpossibleinstantsfor-called-with-iso8601-calendar.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-getpossibleinstantsfor-called-with-iso8601-calendar.js
new file mode 100644
index 0000000000..ddda8b9c96
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-getpossibleinstantsfor-called-with-iso8601-calendar.js
@@ -0,0 +1,59 @@
+// |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.zoneddatetime.prototype.equals
+description: >
+ Time zone's getPossibleInstantsFor is called with a PlainDateTime with the
+ built-in ISO 8601 calendar
+features: [Temporal]
+info: |
+ DisambiguatePossibleInstants:
+ 2. Let _n_ be _possibleInstants_'s length.
+ ...
+ 5. Assert: _n_ = 0.
+ ...
+ 19. If _disambiguation_ is *"earlier"*, then
+ ...
+ c. Let _earlierDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*).
+ d. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _earlierDateTime_).
+ ...
+ 20. Assert: _disambiguation_ is *"compatible"* or *"later"*.
+ ...
+ 23. Let _laterDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*).
+ 24. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _laterDateTime_).
+---*/
+
+class SkippedDateTime extends Temporal.TimeZone {
+ constructor() {
+ super("UTC");
+ this.calls = 0;
+ }
+
+ getPossibleInstantsFor(dateTime) {
+ // Calls occur in pairs. For the first one return no possible instants so
+ // that DisambiguatePossibleInstants will call it again
+ if (this.calls++ % 2 == 0) {
+ return [];
+ }
+
+ assert.sameValue(
+ dateTime.getISOFields().calendar,
+ "iso8601",
+ "getPossibleInstantsFor called with dateTime with built-in ISO 8601 calendar"
+ );
+ return super.getPossibleInstantsFor(dateTime);
+ }
+}
+
+const nonBuiltinISOCalendar = new Temporal.Calendar("iso8601");
+const timeZone = new SkippedDateTime();
+const arg = { year: 2000, month: 5, day: 2, timeZone, calendar: nonBuiltinISOCalendar };
+
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+instance.equals(arg);
+
+assert.sameValue(timeZone.calls, 2, "getPossibleInstantsFor should have been called 2 times");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-invalid-offset-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-invalid-offset-string.js
new file mode 100644
index 0000000000..c3dda0033e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-invalid-offset-string.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.zoneddatetime.prototype.equals
+description: Property bag with offset property is rejected if offset is in the wrong format
+features: [Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+
+const badOffsets = [
+ "00:00", // missing sign
+ "+0", // too short
+ "-000:00", // too long
+ 0, // must be a string
+ null, // must be a string
+ true, // must be a string
+ 1000n, // must be a string
+];
+badOffsets.forEach((offset) => {
+ const arg = { year: 2021, month: 10, day: 28, offset, timeZone };
+ assert.throws(
+ typeof(offset) === 'string' ? RangeError : TypeError,
+ () => instance.equals(arg),
+ `"${offset} is not a valid offset string`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-offset-not-agreeing-with-timezone.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-offset-not-agreeing-with-timezone.js
new file mode 100644
index 0000000000..a986ff58d1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-offset-not-agreeing-with-timezone.js
@@ -0,0 +1,17 @@
+// |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.zoneddatetime.prototype.equals
+description: Property bag with offset property is rejected if offset does not agree with time zone
+features: [Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("+01:00");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+
+const properties = { year: 2021, month: 10, day: 28, offset: "-07:00", timeZone };
+assert.throws(RangeError, () => instance.equals(properties), "offset property not matching time zone is rejected");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-case-insensitive.js
new file mode 100644
index 0000000000..d8001c7719
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-case-insensitive.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Justin Grant. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.equals
+description: The time zone identifier is case-insensitive
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(0n, "UTC");
+
+const bag = { year: 1970, monthCode: "M01", day: 1, timeZone: "utC" };
+const result1 = instance.equals(bag);
+assert.sameValue(result1, true, "Time zone is case-insensitive with property bag argument");
+
+const str = "1970-01-01[UtC]";
+const result2 = instance.equals(str);
+assert.sameValue(result2, true, "Time zone is case-insensitive with string argument");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..ab429616bf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-getoffsetnanosecondsfor-non-integer.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.zoneddatetime.prototype.equals
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+ timeZone.getPossibleInstantsFor = function () {
+ return [];
+ };
+ assert.throws(RangeError, () => datetime.equals({ year: 2000, month: 5, day: 2, hour: 12, timeZone }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..84360d9b7b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-getoffsetnanosecondsfor-not-callable.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.zoneddatetime.prototype.equals
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+ timeZone.getPossibleInstantsFor = function () {
+ return [];
+ };
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => datetime.equals({ year: 2000, month: 5, day: 2, hour: 12, timeZone }),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..a8e6622c8b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.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.zoneddatetime.prototype.equals
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+ timeZone.getPossibleInstantsFor = function () {
+ return [];
+ };
+ assert.throws(RangeError, () => datetime.equals({ year: 2000, month: 5, day: 2, hour: 12, timeZone }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..0839506f4c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,30 @@
+// |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.zoneddatetime.prototype.equals
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+ timeZone.getPossibleInstantsFor = function () {
+ return [];
+ };
+ assert.throws(TypeError, () => datetime.equals({ year: 2000, month: 5, day: 2, hour: 12, timeZone }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-id-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-id-wrong-type.js
new file mode 100644
index 0000000000..82dc1d0519
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-id-wrong-type.js
@@ -0,0 +1,40 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Justin Grant. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.equals
+description: TypeError thrown if time zone reports an id that is not a String
+features: [Temporal]
+---*/
+
+class CustomTimeZone extends Temporal.TimeZone {
+ constructor(id) {
+ super("UTC");
+ this._id = id;
+ }
+ get id() {
+ return this._id;
+ }
+}
+
+[
+ undefined,
+ null,
+ true,
+ -1000,
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ {
+ valueOf() {
+ return 3600_000_000_000;
+ }
+ }
+].forEach((wrongId) => {
+ const timeZone = new CustomTimeZone(wrongId);
+ const datetime = new Temporal.ZonedDateTime(0n, "UTC");
+ assert.throws(TypeError, () => datetime.equals({ year: 1970, month: 1, day: 1, timeZone }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-normalize-offset-strings.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-normalize-offset-strings.js
new file mode 100644
index 0000000000..eef37bd42f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-normalize-offset-strings.js
@@ -0,0 +1,43 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Justin Grant. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.equals
+description: Offset time zone identifiers are compared using their normal form, ignoring syntax differences in offset strings
+features: [Temporal]
+---*/
+
+const tests = [
+ { idToTest: "+0000", description: "colon-less" },
+ { idToTest: "+00", description: "hours-only" }
+];
+
+for (const test of tests) {
+ const {idToTest, description} = test;
+ const instance = new Temporal.ZonedDateTime(0n, "+00:00");
+
+ const bag1 = { year: 1970, monthCode: "M01", day: 1, timeZone: idToTest };
+ assert.sameValue(instance.equals(bag1), true, `Offset time zones are equal despite ${description} syntax in property bag argument`);
+
+ const str = "1970-01-01[+00:00]";
+ assert.sameValue(instance.equals(str), true, `Offset time zones are equal despite ${description} syntax in ISO string argument`);
+
+ const getPossibleInstantsFor = (pdt) => [Temporal.Instant.from(`${pdt.toString()}Z`)]
+ const plainObj = { id: idToTest, getPossibleInstantsFor, getOffsetNanosecondsFor: () => 0 };
+ const bag2 = { year: 1970, monthCode: "M01", day: 1, timeZone: plainObj };
+ assert.sameValue(instance.equals(bag2), true, `Offset time zones are equal despite ${description} syntax in plain object time zone ID`);
+
+ class CustomTimeZone extends Temporal.TimeZone {
+ constructor() {
+ super(idToTest);
+ }
+ get id() { return idToTest; }
+ }
+ const customTimeZoneInstance = new CustomTimeZone();
+ assert.sameValue(customTimeZoneInstance.id, idToTest);
+ const bag3 = { year: 1970, monthCode: "M01", day: 1, timeZone: customTimeZoneInstance };
+ assert.sameValue(instance.equals(bag3), true, `Offset time zones are equal despite ${description} syntax in custom object time zone ID`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-string-datetime.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-string-datetime.js
new file mode 100644
index 0000000000..bdf04fbafe
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-string-datetime.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.zoneddatetime.prototype.equals
+description: Conversion of ISO date-time strings to Temporal.TimeZone instances
+features: [Temporal]
+---*/
+
+let expectedTimeZone = "UTC";
+const instance1 = new Temporal.ZonedDateTime(0n, expectedTimeZone);
+
+let timeZone = "2021-02-19T17:30";
+assert.throws(RangeError, () => instance1.equals({ year: 1970, month: 1, day: 1, timeZone }), "bare date-time string is not a time zone");
+
+// The following are all valid strings so should not throw. They should produce
+// expectedTimeZone, so additionally the operation should return true, because
+// the property bag will produce an instance that's equal to the receiver.
+
+timeZone = "2021-02-19T17:30Z";
+assert(instance1.equals({ year: 1970, month: 1, day: 1, timeZone }), "date-time + Z is UTC time zone");
+
+expectedTimeZone = "-08:00";
+const instance2 = new Temporal.ZonedDateTime(0n, expectedTimeZone);
+timeZone = "2021-02-19T17:30-08:00";
+assert(instance2.equals({ year: 1969, month: 12, day: 31, hour: 16, timeZone }), "date-time + offset is the offset time zone");
+
+const instance3 = new Temporal.ZonedDateTime(0n, expectedTimeZone);
+timeZone = "2021-02-19T17:30[-08:00]";
+assert(instance3.equals({ year: 1969, month: 12, day: 31, hour: 16, timeZone }), "date-time + IANA annotation is the IANA time zone");
+
+timeZone = "2021-02-19T17:30Z[-08:00]";
+assert(instance3.equals({ year: 1969, month: 12, day: 31, hour: 16, timeZone }), "date-time + Z + IANA annotation is the IANA time zone");
+
+timeZone = "2021-02-19T17:30-08:00[-08:00]";
+assert(instance3.equals({ year: 1969, month: 12, day: 31, hour: 16, timeZone }), "date-time + offset + IANA annotation is the IANA time zone");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-string-leap-second.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-string-leap-second.js
new file mode 100644
index 0000000000..30a964479e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-string-leap-second.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.zoneddatetime.prototype.equals
+description: Leap second is a valid ISO string for TimeZone
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(1588377600_000_000_000n, new Temporal.TimeZone("UTC"));
+let timeZone = "2016-12-31T23:59:60+00:00[UTC]";
+
+assert(instance.equals({ year: 2020, month: 5, day: 2, timeZone }), "leap second is a valid ISO string for TimeZone");
+
+timeZone = "2021-08-19T17:30:45.123456789+23:59[+23:59:60]";
+assert.throws(RangeError, () => instance.equals({ year: 2020, month: 5, day: 2, timeZone }), "leap second in time zone name not valid");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-string-multiple-offsets.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-string-multiple-offsets.js
new file mode 100644
index 0000000000..8987fd3b5c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-string-multiple-offsets.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.zoneddatetime.prototype.equals
+description: Time zone parsing from ISO strings uses the bracketed offset, not the ISO string offset
+features: [Temporal]
+---*/
+
+const expectedTimeZone = "+01:46";
+const instance = new Temporal.ZonedDateTime(0n, expectedTimeZone);
+const timeZone = "2021-08-19T17:30:45.123456789-12:12[+01:46]";
+
+// This operation should produce expectedTimeZone, so the following should
+// be equal due to the time zones being different on the receiver and
+// the argument.
+
+const properties = { year: 1970, month: 1, day: 1, hour: 1, minute: 46 };
+assert(instance.equals({ ...properties, timeZone }), "time zone string should produce expected time zone");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-string-year-zero.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-string-year-zero.js
new file mode 100644
index 0000000000..48cafc8c09
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-string-year-zero.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.zoneddatetime.prototype.equals
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+const instance = new Temporal.ZonedDateTime(0n, new Temporal.TimeZone("UTC"));
+invalidStrings.forEach((timeZone) => {
+ assert.throws(
+ RangeError,
+ () => instance.equals({ year: 2020, month: 5, day: 2, timeZone }),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-string.js
new file mode 100644
index 0000000000..45c5c3c1bd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-string.js
@@ -0,0 +1,38 @@
+// |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.zoneddatetime.prototype.equals
+description: Time zone IDs are valid input for a time zone
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getPossibleInstantsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getPossibleInstantsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be looked up");
+ },
+});
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+
+const instance1 = new Temporal.ZonedDateTime(0n, new Temporal.TimeZone("UTC"));
+assert(instance1.equals({ year: 1970, month: 1, day: 1, timeZone: "UTC" }), "Time zone created from string 'UTC'");
+
+const instance2 = new Temporal.ZonedDateTime(0n, new Temporal.TimeZone("-01:30"));
+assert(instance2.equals({ year: 1969, month: 12, day: 31, hour: 22, minute: 30, timeZone: "-01:30" }), "Time zone created from string '-01:30'");
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", getPossibleInstantsForOriginal);
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-wrong-type.js
new file mode 100644
index 0000000000..49a778ec3d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-wrong-type.js
@@ -0,0 +1,42 @@
+// |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.zoneddatetime.prototype.equals
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or object for TimeZone
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(0n, new Temporal.TimeZone("UTC"));
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [19761118, "number that would convert to a valid ISO string in other contexts"],
+ [1n, "bigint"],
+];
+
+for (const [timeZone, description] of primitiveTests) {
+ assert.throws(
+ typeof timeZone === 'string' ? RangeError : TypeError,
+ () => instance.equals({ year: 2020, month: 5, day: 2, timeZone }),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "object not implementing time zone protocol"],
+ [new Temporal.Calendar("iso8601"), "calendar instance"],
+];
+
+for (const [timeZone, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.equals({ year: 2020, month: 5, day: 2, timeZone }), `${description} is not a valid object and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..9ef4d9a5b3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-string-calendar-annotation.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.zoneddatetime.prototype.equals
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["1970-01-01T00:00[UTC][u-ca=iso8601]", "without !"],
+ ["1970-01-01T00:00[UTC][!u-ca=iso8601]", "with !"],
+ ["1970-01-01T00:00[UTC][u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.equals(arg);
+
+ assert.sameValue(
+ result,
+ true,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..fb0d1d457c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-string-critical-unknown-annotation.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.zoneddatetime.prototype.equals
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01T00:00[UTC][!foo=bar]",
+ "1970-01-01T00:00[UTC][!foo=bar][u-ca=iso8601]",
+ "1970-01-01T00:00[UTC][u-ca=iso8601][!foo=bar]",
+ "1970-01-01T00:00[foo=bar][!_foo-bar0=Dont-Ignore-This-99999999999]",
+];
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..3a4a83d679
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-string-date-with-utc-offset.js
@@ -0,0 +1,46 @@
+// |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.zoneddatetime.prototype.equals
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+
+const validStrings = [
+ "1970-01-01T00Z[UTC]",
+ "1970-01-01T00Z[!UTC]",
+ "1970-01-01T00+00:00[UTC]",
+ "1970-01-01T00+00:00[!UTC]",
+];
+
+for (const arg of validStrings) {
+ const result = instance.equals(arg);
+
+ assert.sameValue(
+ result,
+ true,
+ `"${arg}" is a valid UTC offset with time for ZonedDateTime`
+ );
+}
+
+const invalidStrings = [
+ "2022-09-15Z[UTC]",
+ "2022-09-15Z[Europe/Vienna]",
+ "2022-09-15+00:00[UTC]",
+ "2022-09-15-02:30[America/St_Johns]",
+];
+
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ `"${arg}" UTC offset without time is not valid for ZonedDateTime`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..d46ea8d2b7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-string-multiple-calendar.js
@@ -0,0 +1,28 @@
+// |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.zoneddatetime.prototype.equals
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01T00:00[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00[UTC][!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01T00:00[UTC][u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+];
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..494248df4c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-string-multiple-time-zone.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.zoneddatetime.prototype.equals
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "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 timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-string-time-separators.js
new file mode 100644
index 0000000000..1d02e64ae6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-string-time-separators.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.zoneddatetime.prototype.equals
+description: Time separator in string argument can vary
+features: [Temporal]
+---*/
+
+const tests = [
+ ["1970-01-01T00:00+00:00[UTC]", "uppercase T"],
+ ["1970-01-01t00:00+00:00[UTC]", "lowercase T"],
+ ["1970-01-01 00:00+00:00[UTC]", "space between date and time"],
+];
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.equals(arg);
+
+ assert.sameValue(
+ result,
+ true,
+ `variant time separators (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..94d43d9c67
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-string-time-zone-annotation.js
@@ -0,0 +1,38 @@
+// |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.zoneddatetime.prototype.equals
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["1970-01-01T00:00[UTC]", "UTC", "named, with no offset"],
+ ["1970-01-01T00:00[!UTC]", "UTC", "named, with ! and no offset"],
+ ["1970-01-01T00:00[+00:00]", "+00:00", "numeric, with no offset"],
+ ["1970-01-01T00:00[!+00:00]", "+00:00", "numeric, with ! and no offset"],
+ ["1970-01-01T00:00Z[UTC]", "UTC", "named, with Z"],
+ ["1970-01-01T00:00Z[!UTC]", "UTC", "named, with Z and !"],
+ ["1970-01-01T00:00Z[+00:00]", "+00:00", "numeric, with Z"],
+ ["1970-01-01T00:00Z[!+00:00]", "+00:00", "numeric, with Z and !"],
+ ["1970-01-01T00:00+00:00[UTC]", "UTC", "named, with offset"],
+ ["1970-01-01T00:00+00:00[!UTC]", "UTC", "named, with offset and !"],
+ ["1970-01-01T00:00+00:00[+00:00]", "+00:00", "numeric, with offset"],
+ ["1970-01-01T00:00+00:00[!+00:00]", "+00:00", "numeric, with offset and !"],
+];
+
+tests.forEach(([arg, expectedZone, description]) => {
+ const timeZone = new Temporal.TimeZone(expectedZone);
+ const instance = new Temporal.ZonedDateTime(0n, timeZone);
+ const result = instance.equals(arg);
+
+ assert.sameValue(
+ result,
+ true,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..832d4472b3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-string-unknown-annotation.js
@@ -0,0 +1,31 @@
+// |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.zoneddatetime.prototype.equals
+description: Various forms of unknown annotation
+features: [Temporal]
+---*/
+
+const tests = [
+ ["1970-01-01T00:00[UTC][foo=bar]", "with time zone"],
+ ["1970-01-01T00:00[UTC][foo=bar][u-ca=iso8601]", "before calendar"],
+ ["1970-01-01T00:00[UTC][u-ca=iso8601][foo=bar]", "after calendar"],
+ ["1970-01-01T00:00[UTC][foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.equals(arg);
+
+ assert.sameValue(
+ result,
+ true,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-wrong-type.js
new file mode 100644
index 0000000000..87d65dda7b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-wrong-type.js
@@ -0,0 +1,45 @@
+// |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.zoneddatetime.prototype.equals
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for ZonedDateTime
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+
+const primitiveTests = [
+ [undefined, "undefined"],
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [19761118, "number that would convert to a valid ISO string in other contexts"],
+ [1n, "bigint"],
+];
+
+for (const [arg, description] of primitiveTests) {
+ assert.throws(
+ typeof arg === 'string' ? RangeError : TypeError,
+ () => instance.equals(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.ZonedDateTime, "Temporal.ZonedDateTime, object"],
+ [Temporal.ZonedDateTime.prototype, "Temporal.ZonedDateTime.prototype, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.equals(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/ZonedDateTime/prototype/equals/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/branding.js
new file mode 100644
index 0000000000..3a3999338b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/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.zoneddatetime.prototype.equals
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const equals = Temporal.ZonedDateTime.prototype.equals;
+
+assert.sameValue(typeof equals, "function");
+
+const args = [new Temporal.ZonedDateTime(123456n, new Temporal.TimeZone("UTC"))];
+
+assert.throws(TypeError, () => equals.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => equals.apply(null, args), "null");
+assert.throws(TypeError, () => equals.apply(true, args), "true");
+assert.throws(TypeError, () => equals.apply("", args), "empty string");
+assert.throws(TypeError, () => equals.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => equals.apply(1, args), "1");
+assert.throws(TypeError, () => equals.apply({}, args), "plain object");
+assert.throws(TypeError, () => equals.apply(Temporal.ZonedDateTime, args), "Temporal.ZonedDateTime");
+assert.throws(TypeError, () => equals.apply(Temporal.ZonedDateTime.prototype, args), "Temporal.ZonedDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..0412da051c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.zoneddatetime.prototype.equals
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const idOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "id");
+Object.defineProperty(Temporal.Calendar.prototype, "id", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("id should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.equals(new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"));
+
+Object.defineProperty(Temporal.Calendar.prototype, "id", idOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/builtin-timezone-no-observable-calls.js
new file mode 100644
index 0000000000..4c98559604
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/builtin-timezone-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.zoneddatetime.prototype.equals
+description: >
+ Calling the method on an instance constructed with a builtin time zone causes
+ no observable lookups or calls to time zone methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const idOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "id");
+Object.defineProperty(Temporal.TimeZone.prototype, "id", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("id should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.equals(new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"));
+
+Object.defineProperty(Temporal.TimeZone.prototype, "id", idOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/builtin.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/builtin.js
new file mode 100644
index 0000000000..866556891b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/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.zoneddatetime.prototype.equals
+description: >
+ Tests that Temporal.ZonedDateTime.prototype.equals
+ 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.ZonedDateTime.prototype.equals),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.equals),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.equals),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.ZonedDateTime.prototype.equals.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/calendar-datefromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..4596ac881b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/calendar-datefromfields-called-with-null-prototype-fields.js
@@ -0,0 +1,20 @@
+// |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.zoneddatetime.prototype.equals
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+const arg = { year: 2000, month: 5, day: 2, timeZone, calendar };
+instance.equals(arg);
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/calendar-fields-iterable.js
new file mode 100644
index 0000000000..81dbe43ab2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/calendar-fields-iterable.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.zoneddatetime.prototype.equals
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.zoneddatetime.prototype.equals step 3:
+ 3. Set _other_ to ? ToTemporalZonedDateTime(_other_).
+ sec-temporal-totemporalzoneddatetime step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"hour"*, *"microsecond"*, *"millisecond"*, *"minute"*, *"month"*, *"monthCode"*, *"nanosecond"*, *"second"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar1);
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+datetime.equals({ year: 2005, month: 6, day: 2, timeZone: "UTC", calendar: calendar2 });
+
+assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/calendar-temporal-object.js
new file mode 100644
index 0000000000..34a6f9be8f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/calendar-temporal-object.js
@@ -0,0 +1,29 @@
+// |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.zoneddatetime.prototype.equals
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.zoneddatetime.prototype.equals step 3:
+ 3. Set _other_ to ? ToTemporalZonedDateTime(_other_).
+ sec-temporal-totemporalzoneddatetime step 2.b:
+ b. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+ datetime.equals({ year: 2005, month: 6, day: 2, timeZone: "UTC", calendar: temporalObject });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..6dc6ba7ff5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/constructor-in-calendar-fields.js
@@ -0,0 +1,18 @@
+// |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.zoneddatetime.prototype.equals
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const timeZone = 'Europe/Paris'
+const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar, timeZone: timeZone };
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+
+assert.throws(RangeError, () => instance.equals(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/duplicate-calendar-fields.js
new file mode 100644
index 0000000000..f21747aacf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/duplicate-calendar-fields.js
@@ -0,0 +1,20 @@
+// |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.zoneddatetime.prototype.equals
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['day'], ['hour'], ['microsecond'], ['millisecond'], ['minute'], ['month'], ['monthCode'], ['nanosecond'], ['second'], ['year'], ['timeZone'], ['offset']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const timeZone = 'Europe/Paris'
+ const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar, timeZone: timeZone };
+ const instance = new Temporal.ZonedDateTime(0n, timeZone);
+
+ assert.throws(RangeError, () => instance.equals(arg));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..82539840f2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/infinity-throws-rangeerror.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.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.zoneddatetime.prototype.equals
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+const base = { year: 2000, month: 5, day: 2, hour: 15, minute: 30, second: 45, millisecond: 987, microsecond: 654, nanosecond: 321, timeZone: "UTC" };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.equals({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.equals({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/leap-second.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/leap-second.js
new file mode 100644
index 0000000000..d66e7731ae
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/leap-second.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.zoneddatetime.prototype.equals
+description: Leap second is a valid ISO string for ZonedDateTime
+features: [Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(1_483_228_799_000_000_000n, timeZone);
+
+let arg = "2016-12-31T23:59:60+00:00[UTC]";
+const result = instance.equals(arg);
+assert.sameValue(
+ result,
+ true,
+ "leap second is a valid ISO string for ZonedDateTime"
+);
+
+arg = "2000-05-02T12:34:56+23:59[+23:59:60]";
+assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ "leap second in time zone name not valid"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/length.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/length.js
new file mode 100644
index 0000000000..8981fa9ccd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/length.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.zoneddatetime.prototype.equals
+description: Temporal.ZonedDateTime.prototype.equals.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.ZonedDateTime.prototype.equals, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/name.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/name.js
new file mode 100644
index 0000000000..7deb2d03c5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/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.zoneddatetime.prototype.equals
+description: Temporal.ZonedDateTime.prototype.equals.name is "equals".
+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.ZonedDateTime.prototype.equals, "name", {
+ value: "equals",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/not-a-constructor.js
new file mode 100644
index 0000000000..f8e17fcd3a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/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.zoneddatetime.prototype.equals
+description: >
+ Temporal.ZonedDateTime.prototype.equals 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.ZonedDateTime.prototype.equals();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.equals), false,
+ "isConstructor(Temporal.ZonedDateTime.prototype.equals)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/order-of-operations.js
new file mode 100644
index 0000000000..ff48724957
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/order-of-operations.js
@@ -0,0 +1,121 @@
+// |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.zoneddatetime.prototype.equals
+description: Properties on objects passed to equals() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "get other.calendar",
+ "has other.calendar.dateAdd",
+ "has other.calendar.dateFromFields",
+ "has other.calendar.dateUntil",
+ "has other.calendar.day",
+ "has other.calendar.dayOfWeek",
+ "has other.calendar.dayOfYear",
+ "has other.calendar.daysInMonth",
+ "has other.calendar.daysInWeek",
+ "has other.calendar.daysInYear",
+ "has other.calendar.fields",
+ "has other.calendar.id",
+ "has other.calendar.inLeapYear",
+ "has other.calendar.mergeFields",
+ "has other.calendar.month",
+ "has other.calendar.monthCode",
+ "has other.calendar.monthDayFromFields",
+ "has other.calendar.monthsInYear",
+ "has other.calendar.weekOfYear",
+ "has other.calendar.year",
+ "has other.calendar.yearMonthFromFields",
+ "has other.calendar.yearOfWeek",
+ "get other.calendar.dateFromFields",
+ "get other.calendar.fields",
+ "call other.calendar.fields",
+ // PrepareTemporalFields
+ "get other.day",
+ "get other.day.valueOf",
+ "call other.day.valueOf",
+ "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.month",
+ "get other.month.valueOf",
+ "call other.month.valueOf",
+ "get other.monthCode",
+ "get other.monthCode.toString",
+ "call other.monthCode.toString",
+ "get other.nanosecond",
+ "get other.nanosecond.valueOf",
+ "call other.nanosecond.valueOf",
+ "get other.offset",
+ "get other.offset.toString",
+ "call other.offset.toString",
+ "get other.second",
+ "get other.second.valueOf",
+ "call other.second.valueOf",
+ "get other.timeZone",
+ "get other.year",
+ "get other.year.valueOf",
+ "call other.year.valueOf",
+ "has other.timeZone.getOffsetNanosecondsFor",
+ "has other.timeZone.getPossibleInstantsFor",
+ "has other.timeZone.id",
+ // InterpretTemporalDateTimeFields
+ "call other.calendar.dateFromFields",
+ // lookup in ToTemporalZonedDateTime
+ "get other.timeZone.getOffsetNanosecondsFor",
+ "get other.timeZone.getPossibleInstantsFor",
+ // InterpretISODateTimeOffset
+ "call other.timeZone.getPossibleInstantsFor",
+ "call other.timeZone.getOffsetNanosecondsFor",
+ // TimeZoneEquals
+ "get this.timeZone.id",
+ "get other.timeZone.id",
+ // CalendarEquals
+ "get this.calendar.id",
+ "get other.calendar.id",
+];
+const actual = [];
+
+const other = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2001,
+ month: 5,
+ monthCode: "M05",
+ day: 2,
+ hour: 6,
+ minute: 54,
+ second: 32,
+ millisecond: 987,
+ microsecond: 654,
+ nanosecond: 321,
+ offset: "+00:00",
+ calendar: TemporalHelpers.calendarObserver(actual, "other.calendar"),
+ timeZone: TemporalHelpers.timeZoneObserver(actual, "other.timeZone"),
+}, "other");
+
+const instance = new Temporal.ZonedDateTime(
+ 988786472_987_654_321n, /* 2001-05-02T06:54:32.987654321Z */
+ TemporalHelpers.timeZoneObserver(actual, "this.timeZone"),
+ TemporalHelpers.calendarObserver(actual, "this.calendar"),
+);
+// clear any observable operations that happen due to time zone or calendar
+// calls on the constructor
+actual.splice(0);
+
+instance.equals(other);
+assert.compareArray(actual, expected, "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/prop-desc.js
new file mode 100644
index 0000000000..28b0b5c0ac
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/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.zoneddatetime.prototype.equals
+description: The "equals" property of Temporal.ZonedDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.ZonedDateTime.prototype.equals,
+ "function",
+ "`typeof ZonedDateTime.prototype.equals` is `function`"
+);
+
+verifyProperty(Temporal.ZonedDateTime.prototype, "equals", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/proto-in-calendar-fields.js
new file mode 100644
index 0000000000..6d6adb89c4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/proto-in-calendar-fields.js
@@ -0,0 +1,18 @@
+// |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.zoneddatetime.prototype.equals
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const timeZone = 'Europe/Paris'
+const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar, timeZone: timeZone };
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+
+assert.throws(RangeError, () => instance.equals(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/read-time-fields-before-datefromfields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/read-time-fields-before-datefromfields.js
new file mode 100644
index 0000000000..5827450d71
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/read-time-fields-before-datefromfields.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.zoneddatetime.prototype.equals
+description: The time fields are read from the object before being passed to dateFromFields().
+info: |
+ sec-temporal.zoneddatetime.prototype.equals step 3:
+ 3. Set _other_ to ? ToTemporalZonedDateTime(_other_).
+ sec-temporal-totemporalzoneddatetime step 2.j:
+ j. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+ sec-temporal-interprettemporaldatetimefields steps 1–2:
+ 1. Let _timeResult_ be ? ToTemporalTimeRecord(_fields_).
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarMakeInfinityTime();
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC", calendar);
+const result = datetime.equals({ year: 2001, month: 9, day: 9, hour: 1, minute: 46, second: 40, millisecond: 987, microsecond: 654, nanosecond: 321, timeZone: "UTC", calendar });
+
+assert(result, "time fields are not modified");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/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/ZonedDateTime/prototype/equals/timezone-getpossibleinstantsfor-iterable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/timezone-getpossibleinstantsfor-iterable.js
new file mode 100644
index 0000000000..48f46ac772
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/timezone-getpossibleinstantsfor-iterable.js
@@ -0,0 +1,41 @@
+// |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.zoneddatetime.prototype.equals
+description: An iterable returned from timeZone.getPossibleInstantsFor is consumed after each call
+info: |
+ sec-temporal.zoneddatetime.prototype.equals step 3:
+ 3. Set _other_ to ? ToTemporalZonedDateTime(_other_).
+ sec-temporal-totemporalzoneddatetime step 7:
+ 7. Let _epochNanoseconds_ be ? InterpretISODateTimeOffset(_result_.[[Year]], [...], _result_.[[Nanosecond]], _offsetNanoseconds_, _timeZone_, _disambiguation_, _offset_).
+ sec-temporal-interpretisodatetimeoffset step 7:
+ 7. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_).
+ sec-temporal-getpossibleinstantsfor step 2:
+ 2. Let _list_ be ? IterableToList(_possibleInstants_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+// Not called on the instance's time zone
+
+const expected1 = [];
+
+TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => {
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, timeZone);
+ datetime.equals({ year: 2005, month: 6, day: 2, timeZone: "UTC" });
+}, expected1);
+
+// Called on the argument's time zone
+
+const expected2 = [
+ "2005-06-02T00:00:00",
+];
+
+TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => {
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+ datetime.equals({ year: 2005, month: 6, day: 2, timeZone });
+}, expected2);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/year-zero.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/year-zero.js
new file mode 100644
index 0000000000..cae89644b1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/year-zero.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.zoneddatetime.prototype.equals
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-0000000-01-01T00:02Z[UTC]",
+ "-0000000-01-01T00:02+00:00[UTC]",
+ "-0000000-01-01T00:02:00.000000000+00:00[UTC]",
+];
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/zoneddatetime-string-multiple-offsets.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/zoneddatetime-string-multiple-offsets.js
new file mode 100644
index 0000000000..f3a5eb6f15
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/zoneddatetime-string-multiple-offsets.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.zoneddatetime.prototype.equals
+description: Sub-minute offset trailing zeroes allowed in ISO string but not in bracketed offset
+features: [Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("+01:35");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+let str = "1970-01-01T01:35:30+01:35:00.000000000[+01:35]";
+
+const result = instance.equals(str);
+assert.sameValue(result, false, "ISO offset, sub-minute offset trailing-zeroes");
+
+str = "1970-01-01T01:35:30+01:35:00.000000000[+01:35:00.000000000]";
+assert.throws(
+ RangeError,
+ () => instance.equals(str),
+ "Trailing zeroes not allowed for sub-minute time zone identifiers"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/zoneddatetime-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/zoneddatetime-string.js
new file mode 100644
index 0000000000..127adda14b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/zoneddatetime-string.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.zoneddatetime.prototype.equals
+description: Conversion of ISO date-time strings to Temporal.ZonedDateTime instances
+features: [Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("+01:00");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+
+let str = "1970-01-01T00:00";
+assert.throws(RangeError, () => instance.equals(str), "bare date-time string is not a ZonedDateTime");
+str = "1970-01-01T00:00Z";
+assert.throws(RangeError, () => instance.equals(str), "date-time + Z is not a ZonedDateTime");
+str = "1970-01-01T00:00+01:00";
+assert.throws(RangeError, () => instance.equals(str), "date-time + offset is not a ZonedDateTime");
+
+str = "1970-01-01T00:00[+01:00]";
+const result1 = instance.equals(str);
+assert.sameValue(result1, false, "date-time + IANA annotation preserves wall time in the time zone");
+
+str = "1970-01-01T00:00Z[+01:00]";
+const result2 = instance.equals(str);
+assert.sameValue(result2, true, "date-time + Z + IANA annotation preserves exact time in the time zone");
+
+str = "1970-01-01T00:00+01:00[+01:00]";
+const result3 = instance.equals(str);
+assert.sameValue(result3, false, "date-time + offset + IANA annotation ensures both exact and wall time match");
+
+str = "1970-01-01T00:00-04:15[+01:00]";
+assert.throws(RangeError, () => instance.equals(str), "date-time + offset + IANA annotation throws if wall time and exact time mismatch");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getCalendar/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getCalendar/branding.js
new file mode 100644
index 0000000000..4814815a2d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getCalendar/branding.js
@@ -0,0 +1,25 @@
+// |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.zoneddatetime.prototype.getcalendar
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const getCalendar = Temporal.ZonedDateTime.prototype.getCalendar;
+
+assert.sameValue(typeof getCalendar, "function");
+
+assert.throws(TypeError, () => getCalendar.call(undefined), "undefined");
+assert.throws(TypeError, () => getCalendar.call(null), "null");
+assert.throws(TypeError, () => getCalendar.call(true), "true");
+assert.throws(TypeError, () => getCalendar.call(""), "empty string");
+assert.throws(TypeError, () => getCalendar.call(Symbol()), "symbol");
+assert.throws(TypeError, () => getCalendar.call(1), "1");
+assert.throws(TypeError, () => getCalendar.call({}), "plain object");
+assert.throws(TypeError, () => getCalendar.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime");
+assert.throws(TypeError, () => getCalendar.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getCalendar/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getCalendar/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getCalendar/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getCalendar/builtin.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getCalendar/builtin.js
new file mode 100644
index 0000000000..0e6750f2a1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getCalendar/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.zoneddatetime.prototype.getcalendar
+description: >
+ Tests that Temporal.ZonedDateTime.prototype.getCalendar
+ 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.ZonedDateTime.prototype.getCalendar),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.getCalendar),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.getCalendar),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.ZonedDateTime.prototype.getCalendar.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getCalendar/length.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getCalendar/length.js
new file mode 100644
index 0000000000..9ae1bf5fd4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getCalendar/length.js
@@ -0,0 +1,28 @@
+// |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.zoneddatetime.prototype.getcalendar
+description: Temporal.ZonedDateTime.prototype.getCalendar.length is 0
+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.ZonedDateTime.prototype.getCalendar, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getCalendar/name.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getCalendar/name.js
new file mode 100644
index 0000000000..30dbcb35c0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getCalendar/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.zoneddatetime.prototype.getcalendar
+description: Temporal.ZonedDateTime.prototype.getCalendar.name is "getCalendar".
+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.ZonedDateTime.prototype.getCalendar, "name", {
+ value: "getCalendar",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getCalendar/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getCalendar/not-a-constructor.js
new file mode 100644
index 0000000000..8bdcc3c8e5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getCalendar/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.zoneddatetime.prototype.getcalendar
+description: >
+ Temporal.ZonedDateTime.prototype.getCalendar 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.ZonedDateTime.prototype.getCalendar();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.getCalendar), false,
+ "isConstructor(Temporal.ZonedDateTime.prototype.getCalendar)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getCalendar/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getCalendar/prop-desc.js
new file mode 100644
index 0000000000..ad707eeb7b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getCalendar/prop-desc.js
@@ -0,0 +1,24 @@
+// |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.zoneddatetime.prototype.getcalendar
+description: The "getCalendar" property of Temporal.ZonedDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.ZonedDateTime.prototype.getCalendar,
+ "function",
+ "`typeof ZonedDateTime.prototype.getCalendar` is `function`"
+);
+
+verifyProperty(Temporal.ZonedDateTime.prototype, "getCalendar", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getCalendar/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getCalendar/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getCalendar/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/ZonedDateTime/prototype/getISOFields/balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/balance-negative-time-units.js
new file mode 100644
index 0000000000..5a33aae7ce
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/balance-negative-time-units.js
@@ -0,0 +1,43 @@
+// |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.zoneddatetime.prototype.getisofields
+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-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-get-temporal.zoneddatetime.prototype.getisofields step 7:
+ 7. Let _dateTime_ be ? BuiltinTimeZoneGetPlainDateTimeFor(_timeZone_, _instant_, _calendar_).
+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
+// ZonedDateTime
+const tz = TemporalHelpers.specificOffsetTimeZone(-2);
+const datetime = new Temporal.ZonedDateTime(1001n, tz);
+
+const fields = datetime.getISOFields();
+
+assert.sameValue(fields.isoMicrosecond, 0);
+assert.sameValue(fields.isoNanosecond, 999);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/branding.js
new file mode 100644
index 0000000000..5fd93d96d4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/branding.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.zoneddatetime.prototype.getisofields
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const getISOFields = Temporal.ZonedDateTime.prototype.getISOFields;
+
+assert.sameValue(typeof getISOFields, "function");
+
+assert.throws(TypeError, () => getISOFields.call(undefined), "undefined");
+assert.throws(TypeError, () => getISOFields.call(null), "null");
+assert.throws(TypeError, () => getISOFields.call(true), "true");
+assert.throws(TypeError, () => getISOFields.call(""), "empty string");
+assert.throws(TypeError, () => getISOFields.call(Symbol()), "symbol");
+assert.throws(TypeError, () => getISOFields.call(1), "1");
+assert.throws(TypeError, () => getISOFields.call({}), "plain object");
+assert.throws(TypeError, () => getISOFields.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime");
+assert.throws(TypeError, () => getISOFields.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/builtin-timezone-no-observable-calls.js
new file mode 100644
index 0000000000..51ed036417
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/builtin-timezone-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.zoneddatetime.prototype.getisofields
+description: >
+ Calling the method on an instance constructed with a builtin time zone causes
+ no observable lookups or calls to time zone methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.getISOFields();
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/builtin.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/builtin.js
new file mode 100644
index 0000000000..53b24e2db4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/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.zoneddatetime.prototype.getisofields
+description: >
+ Tests that Temporal.ZonedDateTime.prototype.getISOFields
+ 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.ZonedDateTime.prototype.getISOFields),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.getISOFields),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.getISOFields),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.ZonedDateTime.prototype.getISOFields.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/custom.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/custom.js
new file mode 100644
index 0000000000..c7e5fb7f8c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/custom.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.zoneddatetime.prototype.getisofields
+description: getISOFields does not call into user code.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarThrowEverything();
+const instance = new Temporal.ZonedDateTime(1_000_086_400_987_654_321n, "UTC", calendar);
+const result = instance.getISOFields();
+
+assert.sameValue(result.isoYear, 2001, "isoYear result");
+assert.sameValue(result.isoMonth, 9, "isoMonth result");
+assert.sameValue(result.isoDay, 10, "isoDay result");
+assert.sameValue(result.isoHour, 1, "isoHour result");
+assert.sameValue(result.isoMinute, 46, "isoMinute result");
+assert.sameValue(result.isoSecond, 40, "isoSecond result");
+assert.sameValue(result.isoMillisecond, 987, "isoMillisecond result");
+assert.sameValue(result.isoMicrosecond, 654, "isoMicrosecond result");
+assert.sameValue(result.isoNanosecond, 321, "isoNanosecond result");
+assert.sameValue(result.offset, "+00:00", "offset result");
+assert.sameValue(result.calendar, calendar, "calendar result");
+assert.sameValue(result.timeZone, "UTC", "timeZone result");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/field-names.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/field-names.js
new file mode 100644
index 0000000000..fd916b95c7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/field-names.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.zoneddatetime.prototype.getisofields
+description: Correct field names on the object returned from getISOFields
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_086_400_987_654_321n, "UTC");
+
+const result = datetime.getISOFields();
+assert.sameValue(result.isoYear, 2001, "isoYear result");
+assert.sameValue(result.isoMonth, 9, "isoMonth result");
+assert.sameValue(result.isoDay, 10, "isoDay result");
+assert.sameValue(result.isoHour, 1, "isoHour result");
+assert.sameValue(result.isoMinute, 46, "isoMinute result");
+assert.sameValue(result.isoSecond, 40, "isoSecond result");
+assert.sameValue(result.isoMillisecond, 987, "isoMillisecond result");
+assert.sameValue(result.isoMicrosecond, 654, "isoMicrosecond result");
+assert.sameValue(result.isoNanosecond, 321, "isoNanosecond result");
+assert.sameValue(result.offset, "+00:00", "offset result");
+assert.sameValue(result.calendar, "iso8601", "calendar result");
+assert.sameValue(result.timeZone, "UTC", "timeZone result");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/field-prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/field-prop-desc.js
new file mode 100644
index 0000000000..7a1b879b24
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/field-prop-desc.js
@@ -0,0 +1,38 @@
+// |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.zoneddatetime.prototype.getisofields
+description: Properties on the returned object have the correct descriptor
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "calendar",
+ "isoDay",
+ "isoHour",
+ "isoMicrosecond",
+ "isoMillisecond",
+ "isoMinute",
+ "isoMonth",
+ "isoNanosecond",
+ "isoSecond",
+ "isoYear",
+ "offset",
+ "timeZone",
+];
+
+const datetime = new Temporal.ZonedDateTime(1_000_086_400_987_654_321n, "UTC");
+const result = datetime.getISOFields();
+
+for (const property of expected) {
+ verifyProperty(result, property, {
+ writable: true,
+ enumerable: true,
+ configurable: true,
+ });
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/field-traversal-order.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/field-traversal-order.js
new file mode 100644
index 0000000000..3f312f352a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/field-traversal-order.js
@@ -0,0 +1,32 @@
+// |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.zoneddatetime.prototype.getisofields
+description: Properties added in correct order to object returned from getISOFields
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "calendar",
+ "isoDay",
+ "isoHour",
+ "isoMicrosecond",
+ "isoMillisecond",
+ "isoMinute",
+ "isoMonth",
+ "isoNanosecond",
+ "isoSecond",
+ "isoYear",
+ "offset",
+ "timeZone",
+];
+
+const datetime = new Temporal.ZonedDateTime(1_000_086_400_987_654_321n, "UTC");
+const result = datetime.getISOFields();
+
+assert.compareArray(Object.keys(result), expected);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/length.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/length.js
new file mode 100644
index 0000000000..010653ec3d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/length.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.zoneddatetime.prototype.getisofields
+description: Temporal.ZonedDateTime.prototype.getISOFields.length is 0
+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.ZonedDateTime.prototype.getISOFields, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/name.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/name.js
new file mode 100644
index 0000000000..2b26a1f5b6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/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.zoneddatetime.prototype.getisofields
+description: Temporal.ZonedDateTime.prototype.getISOFields.name is "getISOFields".
+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.ZonedDateTime.prototype.getISOFields, "name", {
+ value: "getISOFields",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/negative-epochnanoseconds.js
new file mode 100644
index 0000000000..cd6f5a134d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/negative-epochnanoseconds.js
@@ -0,0 +1,40 @@
+// |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.zoneddatetime.prototype.getisofields
+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: [deepEqual.js]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const calendar = new Temporal.Calendar("iso8601");
+const datetime = new Temporal.ZonedDateTime(-13849764_999_999_999n, timeZone, calendar);
+
+// 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 result = datetime.getISOFields();
+assert.deepEqual(result, {
+ calendar,
+ isoDay: 24,
+ isoHour: 16,
+ isoMicrosecond: 0,
+ isoMillisecond: 0,
+ isoMinute: 50,
+ isoMonth: 7,
+ isoNanosecond: 1,
+ isoSecond: 35,
+ isoYear: 1969,
+ offset: "+00:00",
+ timeZone,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/not-a-constructor.js
new file mode 100644
index 0000000000..8f70d1c859
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/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.zoneddatetime.prototype.getisofields
+description: >
+ Temporal.ZonedDateTime.prototype.getISOFields 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.ZonedDateTime.prototype.getISOFields();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.getISOFields), false,
+ "isConstructor(Temporal.ZonedDateTime.prototype.getISOFields)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/offset.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/offset.js
new file mode 100644
index 0000000000..f4e0b8687d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/offset.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.zoneddatetime.prototype.getisofields
+description: The offset property of returned object
+features: [BigInt, Temporal]
+---*/
+
+function test(timeZoneIdentifier, expectedOffsetString, description) {
+ const timeZone = new Temporal.TimeZone(timeZoneIdentifier);
+ const datetime = new Temporal.ZonedDateTime(0n, timeZone);
+ const fields = datetime.getISOFields();
+ assert.sameValue(fields.offset, expectedOffsetString, description);
+}
+
+test("UTC", "+00:00", "offset of UTC is +00:00");
+test("+01:00", "+01:00", "positive offset");
+test("-05:00", "-05:00", "negative offset");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/order-of-operations.js
new file mode 100644
index 0000000000..69c0c3cd05
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/order-of-operations.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.zoneddatetime.prototype.getisofields
+description: >
+ Properties on the calendar or time zone of the receiver of getISOFields()
+ are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "get this.timeZone.getOffsetNanosecondsFor",
+ "call this.timeZone.getOffsetNanosecondsFor",
+];
+const actual = [];
+
+const instance = new Temporal.ZonedDateTime(
+ 988786472_987_654_321n, /* 2001-05-02T06:54:32.987654321Z */
+ TemporalHelpers.timeZoneObserver(actual, "this.timeZone"),
+ TemporalHelpers.calendarObserver(actual, "this.calendar"),
+);
+// clear any observable operations that happen due to time zone or calendar
+// calls on the constructor
+actual.splice(0);
+
+instance.getISOFields();
+assert.compareArray(actual, expected, "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/prop-desc.js
new file mode 100644
index 0000000000..afb99c7d9e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/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.zoneddatetime.prototype.getisofields
+description: The "getISOFields" property of Temporal.ZonedDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.ZonedDateTime.prototype.getISOFields,
+ "function",
+ "`typeof ZonedDateTime.prototype.getISOFields` is `function`"
+);
+
+verifyProperty(Temporal.ZonedDateTime.prototype, "getISOFields", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/prototype.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/prototype.js
new file mode 100644
index 0000000000..591c6f23c5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/prototype.js
@@ -0,0 +1,15 @@
+// |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.zoneddatetime.prototype.getisofields
+description: Correct prototype on the object returned from getISOFields
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(1_000_086_400_987_654_321n, "UTC");
+const result = instance.getISOFields();
+assert.sameValue(Object.getPrototypeOf(result), Object.prototype, "prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/shell.js
new file mode 100644
index 0000000000..346758ebd5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/shell.js
@@ -0,0 +1,353 @@
+// GENERATED, DO NOT EDIT
+// file: deepEqual.js
+// Copyright 2019 Ron Buckton. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+description: >
+ Compare two values structurally
+defines: [assert.deepEqual]
+---*/
+
+assert.deepEqual = function(actual, expected, message) {
+ var format = assert.deepEqual.format;
+ assert(
+ assert.deepEqual._compare(actual, expected),
+ `Expected ${format(actual)} to be structurally equal to ${format(expected)}. ${(message || '')}`
+ );
+};
+
+assert.deepEqual.format = function(value, seen) {
+ switch (typeof value) {
+ case 'string':
+ return typeof JSON !== "undefined" ? JSON.stringify(value) : `"${value}"`;
+ case 'number':
+ case 'boolean':
+ case 'symbol':
+ case 'bigint':
+ return value.toString();
+ case 'undefined':
+ return 'undefined';
+ case 'function':
+ return `[Function${value.name ? `: ${value.name}` : ''}]`;
+ case 'object':
+ if (value === null) return 'null';
+ if (value instanceof Date) return `Date "${value.toISOString()}"`;
+ if (value instanceof RegExp) return value.toString();
+ if (!seen) {
+ seen = {
+ counter: 0,
+ map: new Map()
+ };
+ }
+
+ let usage = seen.map.get(value);
+ if (usage) {
+ usage.used = true;
+ return `[Ref: #${usage.id}]`;
+ }
+
+ usage = { id: ++seen.counter, used: false };
+ seen.map.set(value, usage);
+
+ if (typeof Set !== "undefined" && value instanceof Set) {
+ return `Set {${Array.from(value).map(value => assert.deepEqual.format(value, seen)).join(', ')}}${usage.used ? ` as #${usage.id}` : ''}`;
+ }
+ if (typeof Map !== "undefined" && value instanceof Map) {
+ return `Map {${Array.from(value).map(pair => `${assert.deepEqual.format(pair[0], seen)} => ${assert.deepEqual.format(pair[1], seen)}}`).join(', ')}}${usage.used ? ` as #${usage.id}` : ''}`;
+ }
+ if (Array.isArray ? Array.isArray(value) : value instanceof Array) {
+ return `[${value.map(value => assert.deepEqual.format(value, seen)).join(', ')}]${usage.used ? ` as #${usage.id}` : ''}`;
+ }
+ let tag = Symbol.toStringTag in value ? value[Symbol.toStringTag] : 'Object';
+ if (tag === 'Object' && Object.getPrototypeOf(value) === null) {
+ tag = '[Object: null prototype]';
+ }
+ return `${tag ? `${tag} ` : ''}{ ${Object.keys(value).map(key => `${key.toString()}: ${assert.deepEqual.format(value[key], seen)}`).join(', ')} }${usage.used ? ` as #${usage.id}` : ''}`;
+ default:
+ return typeof value;
+ }
+};
+
+assert.deepEqual._compare = (function () {
+ var EQUAL = 1;
+ var NOT_EQUAL = -1;
+ var UNKNOWN = 0;
+
+ function deepEqual(a, b) {
+ return compareEquality(a, b) === EQUAL;
+ }
+
+ function compareEquality(a, b, cache) {
+ return compareIf(a, b, isOptional, compareOptionality)
+ || compareIf(a, b, isPrimitiveEquatable, comparePrimitiveEquality)
+ || compareIf(a, b, isObjectEquatable, compareObjectEquality, cache)
+ || NOT_EQUAL;
+ }
+
+ function compareIf(a, b, test, compare, cache) {
+ return !test(a)
+ ? !test(b) ? UNKNOWN : NOT_EQUAL
+ : !test(b) ? NOT_EQUAL : cacheComparison(a, b, compare, cache);
+ }
+
+ function tryCompareStrictEquality(a, b) {
+ return a === b ? EQUAL : UNKNOWN;
+ }
+
+ function tryCompareTypeOfEquality(a, b) {
+ return typeof a !== typeof b ? NOT_EQUAL : UNKNOWN;
+ }
+
+ function tryCompareToStringTagEquality(a, b) {
+ var aTag = Symbol.toStringTag in a ? a[Symbol.toStringTag] : undefined;
+ var bTag = Symbol.toStringTag in b ? b[Symbol.toStringTag] : undefined;
+ return aTag !== bTag ? NOT_EQUAL : UNKNOWN;
+ }
+
+ function isOptional(value) {
+ return value === undefined
+ || value === null;
+ }
+
+ function compareOptionality(a, b) {
+ return tryCompareStrictEquality(a, b)
+ || NOT_EQUAL;
+ }
+
+ function isPrimitiveEquatable(value) {
+ switch (typeof value) {
+ case 'string':
+ case 'number':
+ case 'bigint':
+ case 'boolean':
+ case 'symbol':
+ return true;
+ default:
+ return isBoxed(value);
+ }
+ }
+
+ function comparePrimitiveEquality(a, b) {
+ if (isBoxed(a)) a = a.valueOf();
+ if (isBoxed(b)) b = b.valueOf();
+ return tryCompareStrictEquality(a, b)
+ || tryCompareTypeOfEquality(a, b)
+ || compareIf(a, b, isNaNEquatable, compareNaNEquality)
+ || NOT_EQUAL;
+ }
+
+ function isNaNEquatable(value) {
+ return typeof value === 'number';
+ }
+
+ function compareNaNEquality(a, b) {
+ return isNaN(a) && isNaN(b) ? EQUAL : NOT_EQUAL;
+ }
+
+ function isObjectEquatable(value) {
+ return typeof value === 'object';
+ }
+
+ function compareObjectEquality(a, b, cache) {
+ if (!cache) cache = new Map();
+ return getCache(cache, a, b)
+ || setCache(cache, a, b, EQUAL) // consider equal for now
+ || cacheComparison(a, b, tryCompareStrictEquality, cache)
+ || cacheComparison(a, b, tryCompareToStringTagEquality, cache)
+ || compareIf(a, b, isValueOfEquatable, compareValueOfEquality)
+ || compareIf(a, b, isToStringEquatable, compareToStringEquality)
+ || compareIf(a, b, isArrayLikeEquatable, compareArrayLikeEquality, cache)
+ || compareIf(a, b, isStructurallyEquatable, compareStructuralEquality, cache)
+ || compareIf(a, b, isIterableEquatable, compareIterableEquality, cache)
+ || cacheComparison(a, b, fail, cache);
+ }
+
+ function isBoxed(value) {
+ return value instanceof String
+ || value instanceof Number
+ || value instanceof Boolean
+ || typeof Symbol === 'function' && value instanceof Symbol
+ || typeof BigInt === 'function' && value instanceof BigInt;
+ }
+
+ function isValueOfEquatable(value) {
+ return value instanceof Date;
+ }
+
+ function compareValueOfEquality(a, b) {
+ return compareIf(a.valueOf(), b.valueOf(), isPrimitiveEquatable, comparePrimitiveEquality)
+ || NOT_EQUAL;
+ }
+
+ function isToStringEquatable(value) {
+ return value instanceof RegExp;
+ }
+
+ function compareToStringEquality(a, b) {
+ return compareIf(a.toString(), b.toString(), isPrimitiveEquatable, comparePrimitiveEquality)
+ || NOT_EQUAL;
+ }
+
+ function isArrayLikeEquatable(value) {
+ return (Array.isArray ? Array.isArray(value) : value instanceof Array)
+ || (typeof Uint8Array === 'function' && value instanceof Uint8Array)
+ || (typeof Uint8ClampedArray === 'function' && value instanceof Uint8ClampedArray)
+ || (typeof Uint16Array === 'function' && value instanceof Uint16Array)
+ || (typeof Uint32Array === 'function' && value instanceof Uint32Array)
+ || (typeof Int8Array === 'function' && value instanceof Int8Array)
+ || (typeof Int16Array === 'function' && value instanceof Int16Array)
+ || (typeof Int32Array === 'function' && value instanceof Int32Array)
+ || (typeof Float32Array === 'function' && value instanceof Float32Array)
+ || (typeof Float64Array === 'function' && value instanceof Float64Array)
+ || (typeof BigUint64Array === 'function' && value instanceof BigUint64Array)
+ || (typeof BigInt64Array === 'function' && value instanceof BigInt64Array);
+ }
+
+ function compareArrayLikeEquality(a, b, cache) {
+ if (a.length !== b.length) return NOT_EQUAL;
+ for (var i = 0; i < a.length; i++) {
+ if (compareEquality(a[i], b[i], cache) === NOT_EQUAL) {
+ return NOT_EQUAL;
+ }
+ }
+ return EQUAL;
+ }
+
+ function isStructurallyEquatable(value) {
+ return !(typeof Promise === 'function' && value instanceof Promise // only comparable by reference
+ || typeof WeakMap === 'function' && value instanceof WeakMap // only comparable by reference
+ || typeof WeakSet === 'function' && value instanceof WeakSet // only comparable by reference
+ || typeof Map === 'function' && value instanceof Map // comparable via @@iterator
+ || typeof Set === 'function' && value instanceof Set); // comparable via @@iterator
+ }
+
+ function compareStructuralEquality(a, b, cache) {
+ var aKeys = [];
+ for (var key in a) aKeys.push(key);
+
+ var bKeys = [];
+ for (var key in b) bKeys.push(key);
+
+ if (aKeys.length !== bKeys.length) {
+ return NOT_EQUAL;
+ }
+
+ aKeys.sort();
+ bKeys.sort();
+
+ for (var i = 0; i < aKeys.length; i++) {
+ var aKey = aKeys[i];
+ var bKey = bKeys[i];
+ if (compareEquality(aKey, bKey, cache) === NOT_EQUAL) {
+ return NOT_EQUAL;
+ }
+ if (compareEquality(a[aKey], b[bKey], cache) === NOT_EQUAL) {
+ return NOT_EQUAL;
+ }
+ }
+
+ return compareIf(a, b, isIterableEquatable, compareIterableEquality, cache)
+ || EQUAL;
+ }
+
+ function isIterableEquatable(value) {
+ return typeof Symbol === 'function'
+ && typeof value[Symbol.iterator] === 'function';
+ }
+
+ function compareIteratorEquality(a, b, cache) {
+ if (typeof Map === 'function' && a instanceof Map && b instanceof Map ||
+ typeof Set === 'function' && a instanceof Set && b instanceof Set) {
+ if (a.size !== b.size) return NOT_EQUAL; // exit early if we detect a difference in size
+ }
+
+ var ar, br;
+ while (true) {
+ ar = a.next();
+ br = b.next();
+ if (ar.done) {
+ if (br.done) return EQUAL;
+ if (b.return) b.return();
+ return NOT_EQUAL;
+ }
+ if (br.done) {
+ if (a.return) a.return();
+ return NOT_EQUAL;
+ }
+ if (compareEquality(ar.value, br.value, cache) === NOT_EQUAL) {
+ if (a.return) a.return();
+ if (b.return) b.return();
+ return NOT_EQUAL;
+ }
+ }
+ }
+
+ function compareIterableEquality(a, b, cache) {
+ return compareIteratorEquality(a[Symbol.iterator](), b[Symbol.iterator](), cache);
+ }
+
+ function cacheComparison(a, b, compare, cache) {
+ var result = compare(a, b, cache);
+ if (cache && (result === EQUAL || result === NOT_EQUAL)) {
+ setCache(cache, a, b, /** @type {EQUAL | NOT_EQUAL} */(result));
+ }
+ return result;
+ }
+
+ function fail() {
+ return NOT_EQUAL;
+ }
+
+ function setCache(cache, left, right, result) {
+ var otherCache;
+
+ otherCache = cache.get(left);
+ if (!otherCache) cache.set(left, otherCache = new Map());
+ otherCache.set(right, result);
+
+ otherCache = cache.get(right);
+ if (!otherCache) cache.set(right, otherCache = new Map());
+ otherCache.set(left, result);
+ }
+
+ function getCache(cache, left, right) {
+ var otherCache;
+ var result;
+
+ otherCache = cache.get(left);
+ result = otherCache && otherCache.get(right);
+ if (result) return result;
+
+ otherCache = cache.get(right);
+ result = otherCache && otherCache.get(left);
+ if (result) return result;
+
+ return UNKNOWN;
+ }
+
+ return deepEqual;
+})();
+
+// 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/ZonedDateTime/prototype/getISOFields/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..8ca60795a2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,18 @@
+// |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.zoneddatetime.prototype.getisofields
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.getISOFields());
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..5b1f63a8ad
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/timezone-getoffsetnanosecondsfor-not-callable.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.zoneddatetime.prototype.getisofields
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => datetime.getISOFields(),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..84ae74e176
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,18 @@
+// |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.zoneddatetime.prototype.getisofields
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.getISOFields());
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..6187d63d78
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/timezone-getoffsetnanosecondsfor-wrong-type.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.zoneddatetime.prototype.getisofields
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.getISOFields());
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getTimeZone/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getTimeZone/branding.js
new file mode 100644
index 0000000000..74f62bf43d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getTimeZone/branding.js
@@ -0,0 +1,25 @@
+// |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.zoneddatetime.prototype.gettimezone
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const getTimeZone = Temporal.ZonedDateTime.prototype.getTimeZone;
+
+assert.sameValue(typeof getTimeZone, "function");
+
+assert.throws(TypeError, () => getTimeZone.call(undefined), "undefined");
+assert.throws(TypeError, () => getTimeZone.call(null), "null");
+assert.throws(TypeError, () => getTimeZone.call(true), "true");
+assert.throws(TypeError, () => getTimeZone.call(""), "empty string");
+assert.throws(TypeError, () => getTimeZone.call(Symbol()), "symbol");
+assert.throws(TypeError, () => getTimeZone.call(1), "1");
+assert.throws(TypeError, () => getTimeZone.call({}), "plain object");
+assert.throws(TypeError, () => getTimeZone.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime");
+assert.throws(TypeError, () => getTimeZone.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getTimeZone/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getTimeZone/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getTimeZone/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getTimeZone/builtin.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getTimeZone/builtin.js
new file mode 100644
index 0000000000..25806fa1db
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getTimeZone/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.zoneddatetime.prototype.gettimezone
+description: >
+ Tests that Temporal.ZonedDateTime.prototype.getTimeZone
+ 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.ZonedDateTime.prototype.getTimeZone),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.getTimeZone),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.getTimeZone),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.ZonedDateTime.prototype.getTimeZone.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getTimeZone/length.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getTimeZone/length.js
new file mode 100644
index 0000000000..31dfb7d877
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getTimeZone/length.js
@@ -0,0 +1,28 @@
+// |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.zoneddatetime.prototype.gettimezone
+description: Temporal.ZonedDateTime.prototype.getTimeZone.length is 0
+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.ZonedDateTime.prototype.getTimeZone, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getTimeZone/name.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getTimeZone/name.js
new file mode 100644
index 0000000000..dfc2650802
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getTimeZone/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.zoneddatetime.prototype.gettimezone
+description: Temporal.ZonedDateTime.prototype.getTimeZone.name is "getTimeZone".
+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.ZonedDateTime.prototype.getTimeZone, "name", {
+ value: "getTimeZone",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getTimeZone/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getTimeZone/not-a-constructor.js
new file mode 100644
index 0000000000..c92caf8a95
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getTimeZone/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.zoneddatetime.prototype.gettimezone
+description: >
+ Temporal.ZonedDateTime.prototype.getTimeZone 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.ZonedDateTime.prototype.getTimeZone();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.getTimeZone), false,
+ "isConstructor(Temporal.ZonedDateTime.prototype.getTimeZone)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getTimeZone/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getTimeZone/prop-desc.js
new file mode 100644
index 0000000000..f05fb77817
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getTimeZone/prop-desc.js
@@ -0,0 +1,24 @@
+// |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.zoneddatetime.prototype.gettimezone
+description: The "getTimeZone" property of Temporal.ZonedDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.ZonedDateTime.prototype.getTimeZone,
+ "function",
+ "`typeof ZonedDateTime.prototype.getTimeZone` is `function`"
+);
+
+verifyProperty(Temporal.ZonedDateTime.prototype, "getTimeZone", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getTimeZone/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getTimeZone/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getTimeZone/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/ZonedDateTime/prototype/hour/balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/balance-negative-time-units.js
new file mode 100644
index 0000000000..30915178c9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/balance-negative-time-units.js
@@ -0,0 +1,43 @@
+// |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.zoneddatetime.prototype.hour
+description: Negative time fields are balanced upwards
+info: |
+ sec-temporal-balancetime steps 3–12:
+ 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.
+ 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-get-temporal.zoneddatetime.prototype.hour step 6:
+ 6. Let _temporalDateTime_ be ? BuiltinTimeZoneGetPlainDateTimeFor(_timeZone_, _instant_, _calendar_).
+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(3600_000_000_001n, tz);
+
+assert.sameValue(datetime.hour, 0);
+assert.sameValue(datetime.minute, 59);
+assert.sameValue(datetime.second, 59);
+assert.sameValue(datetime.millisecond, 999);
+assert.sameValue(datetime.microsecond, 999);
+assert.sameValue(datetime.nanosecond, 999);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/branding.js
new file mode 100644
index 0000000000..2f8b13621b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/branding.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-get-temporal.zoneddatetime.prototype.hour
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const hour = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "hour").get;
+
+assert.sameValue(typeof hour, "function");
+
+assert.throws(TypeError, () => hour.call(undefined), "undefined");
+assert.throws(TypeError, () => hour.call(null), "null");
+assert.throws(TypeError, () => hour.call(true), "true");
+assert.throws(TypeError, () => hour.call(""), "empty string");
+assert.throws(TypeError, () => hour.call(Symbol()), "symbol");
+assert.throws(TypeError, () => hour.call(1), "1");
+assert.throws(TypeError, () => hour.call({}), "plain object");
+assert.throws(TypeError, () => hour.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime");
+assert.throws(TypeError, () => hour.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/builtin-timezone-no-observable-calls.js
new file mode 100644
index 0000000000..278f2627f0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/builtin-timezone-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.zoneddatetime.prototype.hour
+description: >
+ Calling the method on an instance constructed with a builtin time zone causes
+ no observable lookups or calls to time zone methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.hour;
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/prop-desc.js
new file mode 100644
index 0000000000..481cf5a02a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/prop-desc.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-get-temporal.zoneddatetime.prototype.hour
+description: The "hour" property of Temporal.ZonedDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "hour");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..e550d8ca6a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,18 @@
+// |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-get-temporal.zoneddatetime.prototype.hour
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.hour);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..15a7390489
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/timezone-getoffsetnanosecondsfor-not-callable.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-get-temporal.zoneddatetime.prototype.hour
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => datetime.hour,
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..e62fbfe41b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,18 @@
+// |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-get-temporal.zoneddatetime.prototype.hour
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.hour);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..12136a348c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/timezone-getoffsetnanosecondsfor-wrong-type.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-get-temporal.zoneddatetime.prototype.hour
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.hour);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/branding.js
new file mode 100644
index 0000000000..44fb5d444e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/branding.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-get-temporal.zoneddatetime.prototype.hoursinday
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const hoursInDay = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "hoursInDay").get;
+
+assert.sameValue(typeof hoursInDay, "function");
+
+assert.throws(TypeError, () => hoursInDay.call(undefined), "undefined");
+assert.throws(TypeError, () => hoursInDay.call(null), "null");
+assert.throws(TypeError, () => hoursInDay.call(true), "true");
+assert.throws(TypeError, () => hoursInDay.call(""), "empty string");
+assert.throws(TypeError, () => hoursInDay.call(Symbol()), "symbol");
+assert.throws(TypeError, () => hoursInDay.call(1), "1");
+assert.throws(TypeError, () => hoursInDay.call({}), "plain object");
+assert.throws(TypeError, () => hoursInDay.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime");
+assert.throws(TypeError, () => hoursInDay.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/getpossibleinstantsfor-called-with-iso8601-calendar.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/getpossibleinstantsfor-called-with-iso8601-calendar.js
new file mode 100644
index 0000000000..7c78776910
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/getpossibleinstantsfor-called-with-iso8601-calendar.js
@@ -0,0 +1,58 @@
+// |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.zoneddatetime.prototype.hoursinday
+description: >
+ Time zone's getPossibleInstantsFor is called with a PlainDateTime with the
+ built-in ISO 8601 calendar
+features: [Temporal]
+info: |
+ DisambiguatePossibleInstants:
+ 2. Let _n_ be _possibleInstants_'s length.
+ ...
+ 5. Assert: _n_ = 0.
+ ...
+ 19. If _disambiguation_ is *"earlier"*, then
+ ...
+ c. Let _earlierDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*).
+ d. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _earlierDateTime_).
+ ...
+ 20. Assert: _disambiguation_ is *"compatible"* or *"later"*.
+ ...
+ 23. Let _laterDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*).
+ 24. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _laterDateTime_).
+---*/
+
+class SkippedDateTime extends Temporal.TimeZone {
+ constructor() {
+ super("UTC");
+ this.calls = 0;
+ }
+
+ getPossibleInstantsFor(dateTime) {
+ // Calls occur in pairs. For the first one return no possible instants so
+ // that DisambiguatePossibleInstants will call it again
+ if (this.calls++ % 2 == 0) {
+ return [];
+ }
+
+ assert.sameValue(
+ dateTime.getISOFields().calendar,
+ "iso8601",
+ "getPossibleInstantsFor called with dateTime with built-in ISO 8601 calendar"
+ );
+ return super.getPossibleInstantsFor(dateTime);
+ }
+}
+
+const nonBuiltinISOCalendar = new Temporal.Calendar("iso8601");
+const timeZone = new SkippedDateTime();
+
+const instance = new Temporal.ZonedDateTime(0n, timeZone, nonBuiltinISOCalendar);
+instance.hoursInDay;
+
+assert.sameValue(timeZone.calls, 4, "getPossibleInstantsFor should have been called 4 times");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/order-of-operations.js
new file mode 100644
index 0000000000..93779e3265
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/order-of-operations.js
@@ -0,0 +1,108 @@
+// |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-get-temporal.zoneddatetime.prototype.hoursinday
+description: User code calls happen in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const expected = [
+ // lookup
+ "get this.timeZone.getOffsetNanosecondsFor",
+ "get this.timeZone.getPossibleInstantsFor",
+ // GetPlainDateTimeFor
+ "call this.timeZone.getOffsetNanosecondsFor",
+ // GetInstantFor
+ "call this.timeZone.getPossibleInstantsFor",
+ // GetInstantFor
+ "call this.timeZone.getPossibleInstantsFor",
+];
+
+// Time zone with special requirements for testing DisambiguatePossibleInstants:
+// midnight 1970-01-01 and 1970-01-02 are each in the middle of a fall-back
+// transition of 1 h. Midnight 1970-01-03 and 1970-01-04 are each in the middle
+// of a spring-forward transition of 1 h.
+// The fall-back transitions occur 30 minutes after the day boundaries at local
+// time: at epoch seconds 1800 and 91800. the spring-forward transitions occur
+// 30 minutes before the day boundaries at local time: at epoch seconds 167400
+// and 257400.
+// This is because calculating the hours in the instance's day requires calling
+// getPossibleInstantsFor on both the preceding local midnight and the following
+// local midnight.
+const timeZone = TemporalHelpers.timeZoneObserver(actual, "this.timeZone", {
+ getOffsetNanosecondsFor(instant) {
+ const epochNs = instant.epochNanoseconds;
+ if (epochNs < 1800_000_000_000n) return 0;
+ if (epochNs < 91800_000_000_000n) return 3600_000_000_000;
+ if (epochNs < 167400_000_000_000n) return 7200_000_000_000;
+ if (epochNs < 257400_000_000_000n) return 3600_000_000_000;
+ return 0;
+ },
+ getPossibleInstantsFor(dt) {
+ const cmp = Temporal.PlainDateTime.compare;
+
+ const zero = new Temporal.TimeZone("+00:00").getInstantFor(dt);
+ const one = new Temporal.TimeZone("+01:00").getInstantFor(dt);
+ const two = new Temporal.TimeZone("+02:00").getInstantFor(dt);
+
+ const fallBackLocalOne = new Temporal.PlainDateTime(1970, 1, 1, 0, 30);
+ const fallBackLocalTwo = new Temporal.PlainDateTime(1970, 1, 2, 0, 30);
+ const springForwardLocalOne = new Temporal.PlainDateTime(1970, 1, 2, 23, 30);
+ const springForwardLocalTwo = new Temporal.PlainDateTime(1970, 1, 3, 23, 30);
+
+ if (cmp(dt, fallBackLocalOne) < 0) return [zero];
+ if (cmp(dt, fallBackLocalOne.add({ hours: 1 })) < 0) return [zero, one];
+ if (cmp(dt, fallBackLocalTwo) < 0) return [one];
+ if (cmp(dt, fallBackLocalTwo.add({ hours: 1 })) < 0) return [one, two];
+ if (cmp(dt, springForwardLocalOne) < 0) return [two];
+ if (cmp(dt, springForwardLocalOne.add({ hours: 1 })) < 0) return [];
+ if (cmp(dt, springForwardLocalTwo) < 0) return [one];
+ if (cmp(dt, springForwardLocalTwo.add({ hours: 1 })) < 0) return [];
+ return [zero];
+ },
+});
+
+const calendar = TemporalHelpers.calendarObserver(actual, "this.calendar");
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, timeZone, calendar);
+const fallBackInstance = new Temporal.ZonedDateTime(43200_000_000_000n /* 1970-01-01T12:00 */, timeZone, calendar);
+const springForwardInstance = new Temporal.ZonedDateTime(216000_000_000_000n /* 1970-01-03T12:00 */, timeZone, calendar);
+actual.splice(0); // clear calls that happened in constructors
+
+instance.hoursInDay;
+assert.compareArray(actual, expected, "order of operations with both midnights at normal wall-clock times");
+actual.splice(0); // clear
+
+fallBackInstance.hoursInDay;
+assert.compareArray(actual, expected, "order of operations with both midnights at repeated wall-clock times");
+actual.splice(0); // clear
+
+springForwardInstance.hoursInDay;
+assert.compareArray(actual, [
+ // lookup
+ "get this.timeZone.getOffsetNanosecondsFor",
+ "get this.timeZone.getPossibleInstantsFor",
+ // GetPlainDateTimeFor
+ "call this.timeZone.getOffsetNanosecondsFor",
+ // GetInstantFor
+ "call this.timeZone.getPossibleInstantsFor",
+ // DisambiguatePossibleInstants
+ "call this.timeZone.getOffsetNanosecondsFor",
+ "call this.timeZone.getOffsetNanosecondsFor",
+ // Note, no call to dateAdd as addition takes place in the ISO calendar
+ "call this.timeZone.getPossibleInstantsFor",
+ // GetInstantFor
+ "call this.timeZone.getPossibleInstantsFor",
+ // DisambiguatePossibleInstants
+ "call this.timeZone.getOffsetNanosecondsFor",
+ "call this.timeZone.getOffsetNanosecondsFor",
+ // Note, no call to dateAdd here either
+ "call this.timeZone.getPossibleInstantsFor",
+], "order of operations with both midnights at skipped wall-clock times");
+actual.splice(0); // clear
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/precision-exact-mathematical-values-2.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/precision-exact-mathematical-values-2.js
new file mode 100644
index 0000000000..7f34328d18
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/precision-exact-mathematical-values-2.js
@@ -0,0 +1,142 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2024 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.hoursinday
+description: >
+ Hours in day is correctly rounded using precise mathematical values.
+info: |
+ get Temporal.ZonedDateTime.prototype.hoursInDay
+
+ ...
+ 14. Let diffNs be tomorrowInstant.[[Nanoseconds]] - todayInstant.[[Nanoseconds]].
+ 15. Return 𝔽(diffNs / (3.6 × 10^12)).
+features: [Temporal]
+---*/
+
+// Randomly generated test data.
+const data = [
+ {
+ hours: 816,
+ nanoseconds: 2049_187_497_660,
+ },
+ {
+ hours: 7825,
+ nanoseconds: 1865_665_040_770,
+ },
+ {
+ hours: 0,
+ nanoseconds: 1049_560_584_034,
+ },
+ {
+ hours: 2055144,
+ nanoseconds: 2502_078_444_371,
+ },
+ {
+ hours: 31,
+ nanoseconds: 1010_734_758_745,
+ },
+ {
+ hours: 24,
+ nanoseconds: 2958_999_560_387,
+ },
+ {
+ hours: 0,
+ nanoseconds: 342_058_521_588,
+ },
+ {
+ hours: 17746,
+ nanoseconds: 3009_093_506_309,
+ },
+ {
+ hours: 4,
+ nanoseconds: 892_480_914_569,
+ },
+ {
+ hours: 3954,
+ nanoseconds: 571_647_777_618,
+ },
+ {
+ hours: 27,
+ nanoseconds: 2322_199_502_640,
+ },
+ {
+ hours: 258054064,
+ nanoseconds: 2782_411_891_222,
+ },
+ {
+ hours: 1485,
+ nanoseconds: 2422_559_903_100,
+ },
+ {
+ hours: 0,
+ nanoseconds: 1461_068_214_153,
+ },
+ {
+ hours: 393,
+ nanoseconds: 1250_229_561_658,
+ },
+ {
+ hours: 0,
+ nanoseconds: 91_035_820,
+ },
+ {
+ hours: 0,
+ nanoseconds: 790_982_655,
+ },
+ {
+ hours: 150,
+ nanoseconds: 608_531_524,
+ },
+ {
+ hours: 5469,
+ nanoseconds: 889_204_952,
+ },
+ {
+ hours: 7870,
+ nanoseconds: 680_042_770,
+ },
+];
+
+const nsPerHour = 3600_000_000_000;
+
+const fractionDigits = Math.log10(nsPerHour) + Math.log10(100_000_000_000) - Math.log10(36);
+assert.sameValue(fractionDigits, 22);
+
+for (let {hours, nanoseconds} of data) {
+ assert(nanoseconds < nsPerHour);
+
+ // Compute enough fractional digits to approximate the exact result. Use BigInts
+ // to avoid floating point precision loss. Fill to the left with implicit zeros.
+ let fraction = ((BigInt(nanoseconds) * 100_000_000_000n) / 36n).toString().padStart(fractionDigits, "0");
+
+ // Get the Number approximation from the string representation.
+ let expected = Number(`${hours}.${fraction}`);
+
+ let todayInstant = 0n;
+ let tomorrowInstant = BigInt(hours) * BigInt(nsPerHour) + BigInt(nanoseconds);
+
+ let timeZone = new class extends Temporal.TimeZone {
+ #getPossibleInstantsFor = 0;
+
+ getPossibleInstantsFor() {
+ if (++this.#getPossibleInstantsFor === 1) {
+ return [new Temporal.Instant(todayInstant)];
+ }
+ assert.sameValue(this.#getPossibleInstantsFor, 2);
+ return [new Temporal.Instant(tomorrowInstant)];
+ }
+ }("UTC");
+
+ let zdt = new Temporal.ZonedDateTime(0n, timeZone);
+ let actual = zdt.hoursInDay;
+
+ assert.sameValue(
+ actual,
+ expected,
+ `hours=${hours}, nanoseconds=${nanoseconds}`,
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/precision-exact-mathematical-values.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/precision-exact-mathematical-values.js
new file mode 100644
index 0000000000..020324f35c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/precision-exact-mathematical-values.js
@@ -0,0 +1,133 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.hoursinday
+description: >
+ Hours in day is correctly rounded using precise mathematical values.
+info: |
+ get Temporal.ZonedDateTime.prototype.hoursInDay
+
+ ...
+ 15. Let diffNs be tomorrowInstant.[[Nanoseconds]] - todayInstant.[[Nanoseconds]].
+ 16. Return 𝔽(diffNs / (3.6 × 10^12)).
+features: [Temporal]
+---*/
+
+const maxInstant = 86_40000_00000_00000_00000n;
+
+function nextUp(num) {
+ if (!Number.isFinite(num)) {
+ return num;
+ }
+ if (num === 0) {
+ return Number.MIN_VALUE;
+ }
+
+ var f64 = new Float64Array([num]);
+ var u64 = new BigUint64Array(f64.buffer);
+ u64[0] += (num < 0 ? -1n : 1n);
+ return f64[0];
+}
+
+function nextDown(num) {
+ if (!Number.isFinite(num)) {
+ return num;
+ }
+ if (num === 0) {
+ return -Number.MIN_VALUE;
+ }
+
+ var f64 = new Float64Array([num]);
+ var u64 = new BigUint64Array(f64.buffer);
+ u64[0] += (num < 0 ? 1n : -1n);
+ return f64[0];
+}
+
+function BigIntAbs(n) {
+ return n >= 0 ? n : -n;
+}
+
+function test(todayInstant, tomorrowInstant, expected) {
+ let timeZone = new class extends Temporal.TimeZone {
+ #getPossibleInstantsFor = 0;
+
+ getPossibleInstantsFor() {
+ if (++this.#getPossibleInstantsFor === 1) {
+ return [new Temporal.Instant(todayInstant)];
+ }
+ assert.sameValue(this.#getPossibleInstantsFor, 2);
+ return [new Temporal.Instant(tomorrowInstant)];
+ }
+ }("UTC");
+
+ let zdt = new Temporal.ZonedDateTime(0n, timeZone);
+ let zdt_hoursInDay = zdt.hoursInDay;
+
+ assert.sameValue(zdt_hoursInDay, expected, "hoursInDay return value");
+
+ // Ensure the |expected| value is actually correctly rounded.
+
+ const nsPerSec = 1000n * 1000n * 1000n;
+ const secPerHour = 60n * 60n;
+ const nsPerHour = secPerHour * nsPerSec;
+
+ function toNanoseconds(hours) {
+ let wholeHours = BigInt(Math.trunc(hours)) * nsPerHour;
+ let fractionalHours = BigInt(Math.trunc(hours % 1 * Number(nsPerHour)));
+ return wholeHours + fractionalHours;
+ }
+
+ let diff = tomorrowInstant - todayInstant;
+ let nanosInDay = toNanoseconds(zdt_hoursInDay);
+
+ // The next number gives a less precise result.
+ let next = toNanoseconds(nextUp(zdt_hoursInDay));
+ assert(BigIntAbs(diff - nanosInDay) <= BigIntAbs(diff - next));
+
+ // The previous number gives a less precise result.
+ let prev = toNanoseconds(nextDown(zdt_hoursInDay));
+ assert(BigIntAbs(diff - nanosInDay) <= BigIntAbs(diff - prev));
+
+ // This computation can be inaccurate.
+ let inaccurate = Number(diff) / Number(nsPerHour);
+
+ // Doing it component-wise can produce more accurate results.
+ let hours = Number(diff / nsPerSec) / Number(secPerHour);
+ let fractionalHours = Number(diff % nsPerSec) / Number(nsPerHour);
+ assert.sameValue(hours + fractionalHours, expected);
+
+ // Ensure the result is more precise than the inaccurate result.
+ let inaccurateNanosInDay = toNanoseconds(inaccurate);
+ assert(BigIntAbs(diff - nanosInDay) <= BigIntAbs(diff - inaccurateNanosInDay));
+}
+
+test(-maxInstant, 0n, 2400000000);
+test(-maxInstant, 1n, 2400000000);
+test(-maxInstant, 10n, 2400000000);
+test(-maxInstant, 100n, 2400000000);
+
+test(-maxInstant, 1_000n, 2400000000);
+test(-maxInstant, 10_000n, 2400000000);
+test(-maxInstant, 100_000n, 2400000000);
+
+test(-maxInstant, 1_000_000n, 2400000000.0000005);
+test(-maxInstant, 10_000_000n, 2400000000.000003);
+test(-maxInstant, 100_000_000n, 2400000000.0000277);
+
+test(-maxInstant, 1_000_000_000n, 2400000000.000278);
+test(-maxInstant, 10_000_000_000n, 2400000000.0027776);
+test(-maxInstant, 100_000_000_000n, 2400000000.0277777);
+
+test(-maxInstant, maxInstant, 4800000000);
+test(-maxInstant, maxInstant - 10_000_000_000n, 4799999999.997222);
+test(-maxInstant, maxInstant - 10_000_000_000n + 1n, 4799999999.997222);
+test(-maxInstant, maxInstant - 10_000_000_000n - 1n, 4799999999.997222);
+
+test(maxInstant, -maxInstant, -4800000000);
+test(maxInstant, -(maxInstant - 10_000_000_000n), -4799999999.997222);
+test(maxInstant, -(maxInstant - 10_000_000_000n + 1n), -4799999999.997222);
+test(maxInstant, -(maxInstant - 10_000_000_000n - 1n), -4799999999.997222);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/prop-desc.js
new file mode 100644
index 0000000000..ab35491c4f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/prop-desc.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-get-temporal.zoneddatetime.prototype.hoursinday
+description: The "hoursInDay" property of Temporal.ZonedDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "hoursInDay");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..7dc8591bd9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,18 @@
+// |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-get-temporal.zoneddatetime.prototype.hoursinday
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.hoursInDay);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..f6af7e270b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/timezone-getoffsetnanosecondsfor-not-callable.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-get-temporal.zoneddatetime.prototype.hoursinday
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => datetime.hoursInDay,
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..4a88f1a582
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,18 @@
+// |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-get-temporal.zoneddatetime.prototype.hoursinday
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.hoursInDay);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..057bdca141
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/timezone-getoffsetnanosecondsfor-wrong-type.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-get-temporal.zoneddatetime.prototype.hoursinday
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.hoursInDay);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/timezone-getpossibleinstantsfor-iterable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/timezone-getpossibleinstantsfor-iterable.js
new file mode 100644
index 0000000000..bab068de3a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/timezone-getpossibleinstantsfor-iterable.js
@@ -0,0 +1,48 @@
+// |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-get-temporal.zoneddatetime.prototype.hoursinday
+description: An iterable returned from timeZone.getPossibleInstantsFor is consumed after each call
+info: |
+ sec-get-temporal.zoneddatetime.prototype.hoursinday steps 13–14:
+ 13. Let _todayInstant_ be ? BuiltinTimeZoneGetInstantFor(_timeZone_, _today_, *"compatible"*).
+ 14. Let _tomorrowInstant_ be ? BuiltinTimeZoneGetInstantFor(_timeZone_, _tomorrow_, *"compatible"*).
+ sec-temporal-builtintimezonegetinstantfor step 1:
+ 1. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_).
+ sec-temporal-builtintimezonegetinstantfor step 14:
+ 14. Assert: _disambiguation_ is *"compatible"* or *"later"*.
+ sec-temporal-builtintimezonegetinstantfor step 16:
+ 16. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _later_).
+ sec-temporal-getpossibleinstantsfor step 2:
+ 2. Let _list_ be ? IterableToList(_possibleInstants_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected1 = [
+ "2001-09-09T00:00:00",
+ "2001-09-10T00:00:00",
+];
+
+TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => {
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, timeZone);
+ datetime.hoursInDay;
+}, expected1);
+
+// Same, but test the other path where the time doesn't exist and
+// GetPossibleInstantsFor is called again on a later time
+
+const expected2 = [
+ "2030-01-01T00:00:00",
+ "2030-01-01T01:00:00",
+ "2030-01-02T00:00:00",
+];
+
+TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => {
+ const datetime = new Temporal.ZonedDateTime(1_893_457_800_000_000_000n, timeZone);
+ datetime.hoursInDay;
+}, expected2);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/basic.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/basic.js
new file mode 100644
index 0000000000..1b745bc8f4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/basic.js
@@ -0,0 +1,16 @@
+// |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-get-temporal.zoneddatetime.prototype.inleapyear
+description: Basic test for inLeapYear
+features: [Temporal]
+---*/
+
+assert.sameValue((new Temporal.ZonedDateTime(217178610123456789n, "UTC")).inLeapYear,
+ true, "leap year");
+assert.sameValue((new Temporal.ZonedDateTime(248714610123456789n, "UTC")).inLeapYear,
+ false, "non-leap year");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/branding.js
new file mode 100644
index 0000000000..628ce75143
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/branding.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-get-temporal.zoneddatetime.prototype.inleapyear
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const inLeapYear = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "inLeapYear").get;
+
+assert.sameValue(typeof inLeapYear, "function");
+
+assert.throws(TypeError, () => inLeapYear.call(undefined), "undefined");
+assert.throws(TypeError, () => inLeapYear.call(null), "null");
+assert.throws(TypeError, () => inLeapYear.call(true), "true");
+assert.throws(TypeError, () => inLeapYear.call(""), "empty string");
+assert.throws(TypeError, () => inLeapYear.call(Symbol()), "symbol");
+assert.throws(TypeError, () => inLeapYear.call(1), "1");
+assert.throws(TypeError, () => inLeapYear.call({}), "plain object");
+assert.throws(TypeError, () => inLeapYear.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime");
+assert.throws(TypeError, () => inLeapYear.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..0554a9eba1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.zoneddatetime.prototype.inleapyear
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const inLeapYearOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "inLeapYear");
+Object.defineProperty(Temporal.Calendar.prototype, "inLeapYear", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("inLeapYear should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.inLeapYear;
+
+Object.defineProperty(Temporal.Calendar.prototype, "inLeapYear", inLeapYearOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/builtin-timezone-no-observable-calls.js
new file mode 100644
index 0000000000..d3c41c92f1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/builtin-timezone-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.zoneddatetime.prototype.inleapyear
+description: >
+ Calling the method on an instance constructed with a builtin time zone causes
+ no observable lookups or calls to time zone methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.inLeapYear;
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/custom.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/custom.js
new file mode 100644
index 0000000000..b91fe2b043
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/custom.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-get-temporal.zoneddatetime.prototype.inleapyear
+description: Custom calendar tests for inLeapYear().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ inLeapYear(...args) {
+ ++calls;
+ assert.compareArray(args.map(String), [instance].map((arg) => arg.toPlainDateTime().toString()), "inLeapYear arguments");
+ return true;
+ }
+}
+
+const calendar = new CustomCalendar();
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar);
+const result = instance.inLeapYear;
+assert.sameValue(result, true, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/prop-desc.js
new file mode 100644
index 0000000000..512c2bdf8f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/prop-desc.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-get-temporal.zoneddatetime.prototype.inleapyear
+description: The "inLeapYear" property of Temporal.ZonedDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "inLeapYear");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..e30672adae
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,18 @@
+// |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-get-temporal.zoneddatetime.prototype.inleapyear
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.inLeapYear);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..7667eb1548
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/timezone-getoffsetnanosecondsfor-not-callable.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-get-temporal.zoneddatetime.prototype.inleapyear
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => datetime.inLeapYear,
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..8a73653452
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,18 @@
+// |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-get-temporal.zoneddatetime.prototype.inleapyear
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.inLeapYear);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..542b349b62
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/timezone-getoffsetnanosecondsfor-wrong-type.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-get-temporal.zoneddatetime.prototype.inleapyear
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.inLeapYear);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/validate-calendar-value.js
new file mode 100644
index 0000000000..f60b463dc8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/validate-calendar-value.js
@@ -0,0 +1,56 @@
+// |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-get-temporal.zoneddatetime.prototype.inleapyear
+description: Validate result returned from calendar inLeapYear() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [null, TypeError],
+ [0, TypeError],
+ [-0, TypeError],
+ [42, TypeError],
+ [7.1, TypeError],
+ [NaN, TypeError],
+ [Infinity, TypeError],
+ [-Infinity, TypeError],
+ ["", TypeError],
+ ["a string", TypeError],
+ ["0", TypeError],
+ [Symbol("foo"), TypeError],
+ [0n, TypeError],
+ [42n, TypeError],
+ [{}, TypeError],
+ [{valueOf() { return false; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ inLeapYear() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar);
+ assert.throws(error, () => instance.inLeapYear, `${typeof result} ${String(result)} not converted to boolean`);
+});
+
+const preservedResults = [
+ true,
+ false,
+];
+
+preservedResults.forEach(result => {
+ const calendar = new class extends Temporal.Calendar {
+ inLeapYear() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar);
+ assert.sameValue(instance.inLeapYear, result, `${typeof result} ${String(result)} preserved`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/balance-negative-time-units.js
new file mode 100644
index 0000000000..c3c65270f7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/balance-negative-time-units.js
@@ -0,0 +1,31 @@
+// |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.zoneddatetime.prototype.microsecond
+description: Negative time fields are balanced upwards
+info: |
+ sec-temporal-balancetime steps 3–4:
+ 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000).
+ 4. Set _nanosecond_ to _nanosecond_ modulo 1000.
+ 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-get-temporal.zoneddatetime.prototype.microsecond step 6:
+ 6. Let _temporalDateTime_ be ? BuiltinTimeZoneGetPlainDateTimeFor(_timeZone_, _instant_, _calendar_).
+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(1001n, tz);
+
+assert.sameValue(datetime.microsecond, 0);
+assert.sameValue(datetime.nanosecond, 999);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/branding.js
new file mode 100644
index 0000000000..598c583d72
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/branding.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-get-temporal.zoneddatetime.prototype.microsecond
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const microsecond = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "microsecond").get;
+
+assert.sameValue(typeof microsecond, "function");
+
+assert.throws(TypeError, () => microsecond.call(undefined), "undefined");
+assert.throws(TypeError, () => microsecond.call(null), "null");
+assert.throws(TypeError, () => microsecond.call(true), "true");
+assert.throws(TypeError, () => microsecond.call(""), "empty string");
+assert.throws(TypeError, () => microsecond.call(Symbol()), "symbol");
+assert.throws(TypeError, () => microsecond.call(1), "1");
+assert.throws(TypeError, () => microsecond.call({}), "plain object");
+assert.throws(TypeError, () => microsecond.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime");
+assert.throws(TypeError, () => microsecond.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/builtin-timezone-no-observable-calls.js
new file mode 100644
index 0000000000..35639e06cc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/builtin-timezone-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.zoneddatetime.prototype.microsecond
+description: >
+ Calling the method on an instance constructed with a builtin time zone causes
+ no observable lookups or calls to time zone methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.microsecond;
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/negative-epochnanoseconds.js
new file mode 100644
index 0000000000..0948f3578c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/negative-epochnanoseconds.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.zoneddatetime.prototype.microsecond
+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]
+---*/
+
+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.
+
+assert.sameValue(datetime.microsecond, 0);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/prop-desc.js
new file mode 100644
index 0000000000..f9d4cd67d3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/prop-desc.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-get-temporal.zoneddatetime.prototype.microsecond
+description: The "microsecond" property of Temporal.ZonedDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "microsecond");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..027044911b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,18 @@
+// |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-get-temporal.zoneddatetime.prototype.microsecond
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.microsecond);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..ab74ef0eb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/timezone-getoffsetnanosecondsfor-not-callable.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-get-temporal.zoneddatetime.prototype.microsecond
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => datetime.microsecond,
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..38560f2714
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,18 @@
+// |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-get-temporal.zoneddatetime.prototype.microsecond
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.microsecond);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..fd35a38fd8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/timezone-getoffsetnanosecondsfor-wrong-type.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-get-temporal.zoneddatetime.prototype.microsecond
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.microsecond);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/balance-negative-time-units.js
new file mode 100644
index 0000000000..0c9a387d10
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/balance-negative-time-units.js
@@ -0,0 +1,34 @@
+// |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.zoneddatetime.prototype.millisecond
+description: Negative time fields are balanced upwards
+info: |
+ sec-temporal-balancetime steps 3–6:
+ 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.
+ 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-get-temporal.zoneddatetime.prototype.millisecond step 6:
+ 6. Let _temporalDateTime_ be ? BuiltinTimeZoneGetPlainDateTimeFor(_timeZone_, _instant_, _calendar_).
+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(1_000_001n, tz);
+
+assert.sameValue(datetime.millisecond, 0);
+assert.sameValue(datetime.microsecond, 999);
+assert.sameValue(datetime.nanosecond, 999);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/branding.js
new file mode 100644
index 0000000000..22d91cf12c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/branding.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-get-temporal.zoneddatetime.prototype.millisecond
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const millisecond = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "millisecond").get;
+
+assert.sameValue(typeof millisecond, "function");
+
+assert.throws(TypeError, () => millisecond.call(undefined), "undefined");
+assert.throws(TypeError, () => millisecond.call(null), "null");
+assert.throws(TypeError, () => millisecond.call(true), "true");
+assert.throws(TypeError, () => millisecond.call(""), "empty string");
+assert.throws(TypeError, () => millisecond.call(Symbol()), "symbol");
+assert.throws(TypeError, () => millisecond.call(1), "1");
+assert.throws(TypeError, () => millisecond.call({}), "plain object");
+assert.throws(TypeError, () => millisecond.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime");
+assert.throws(TypeError, () => millisecond.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/builtin-timezone-no-observable-calls.js
new file mode 100644
index 0000000000..7e38b494bc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/builtin-timezone-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.zoneddatetime.prototype.millisecond
+description: >
+ Calling the method on an instance constructed with a builtin time zone causes
+ no observable lookups or calls to time zone methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.millisecond;
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/negative-epochnanoseconds.js
new file mode 100644
index 0000000000..b489d8d9a2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/negative-epochnanoseconds.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.zoneddatetime.prototype.millisecond
+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]
+---*/
+
+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.
+
+assert.sameValue(datetime.millisecond, 0);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/prop-desc.js
new file mode 100644
index 0000000000..7ea0edc462
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/prop-desc.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-get-temporal.zoneddatetime.prototype.millisecond
+description: The "millisecond" property of Temporal.ZonedDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "millisecond");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..d1aeed81d0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,18 @@
+// |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-get-temporal.zoneddatetime.prototype.millisecond
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.millisecond);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..b357f6ac48
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/timezone-getoffsetnanosecondsfor-not-callable.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-get-temporal.zoneddatetime.prototype.millisecond
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => datetime.millisecond,
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..b90f3a2799
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,18 @@
+// |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-get-temporal.zoneddatetime.prototype.millisecond
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.millisecond);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..0f09e240aa
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/timezone-getoffsetnanosecondsfor-wrong-type.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-get-temporal.zoneddatetime.prototype.millisecond
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.millisecond);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/balance-negative-time-units.js
new file mode 100644
index 0000000000..90ca2b62f1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/balance-negative-time-units.js
@@ -0,0 +1,42 @@
+// |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.zoneddatetime.prototype.minute
+description: Negative time fields are balanced upwards
+info: |
+ sec-temporal-balancetime steps 3–10:
+ 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.
+ 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-get-temporal.zoneddatetime.prototype.minute step 6:
+ 6. Let _temporalDateTime_ be ? BuiltinTimeZoneGetPlainDateTimeFor(_timeZone_, _instant_, _calendar_).
+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(60_000_000_001n, tz);
+
+assert.sameValue(datetime.minute, 0);
+assert.sameValue(datetime.second, 59);
+assert.sameValue(datetime.millisecond, 999);
+assert.sameValue(datetime.microsecond, 999);
+assert.sameValue(datetime.nanosecond, 999);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/branding.js
new file mode 100644
index 0000000000..6f3cd8715d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/branding.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-get-temporal.zoneddatetime.prototype.minute
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const minute = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "minute").get;
+
+assert.sameValue(typeof minute, "function");
+
+assert.throws(TypeError, () => minute.call(undefined), "undefined");
+assert.throws(TypeError, () => minute.call(null), "null");
+assert.throws(TypeError, () => minute.call(true), "true");
+assert.throws(TypeError, () => minute.call(""), "empty string");
+assert.throws(TypeError, () => minute.call(Symbol()), "symbol");
+assert.throws(TypeError, () => minute.call(1), "1");
+assert.throws(TypeError, () => minute.call({}), "plain object");
+assert.throws(TypeError, () => minute.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime");
+assert.throws(TypeError, () => minute.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/builtin-timezone-no-observable-calls.js
new file mode 100644
index 0000000000..9425752d0c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/builtin-timezone-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.zoneddatetime.prototype.minute
+description: >
+ Calling the method on an instance constructed with a builtin time zone causes
+ no observable lookups or calls to time zone methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.minute;
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/prop-desc.js
new file mode 100644
index 0000000000..d67ef59b2a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/prop-desc.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-get-temporal.zoneddatetime.prototype.minute
+description: The "minute" property of Temporal.ZonedDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "minute");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..907f510ed9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,18 @@
+// |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-get-temporal.zoneddatetime.prototype.minute
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.minute);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..16116a21a9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/timezone-getoffsetnanosecondsfor-not-callable.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-get-temporal.zoneddatetime.prototype.minute
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => datetime.minute,
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..37e77f338d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,18 @@
+// |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-get-temporal.zoneddatetime.prototype.minute
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.minute);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..a043b13ad7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/timezone-getoffsetnanosecondsfor-wrong-type.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-get-temporal.zoneddatetime.prototype.minute
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.minute);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/branding.js
new file mode 100644
index 0000000000..d35fbe41dd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/branding.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-get-temporal.zoneddatetime.prototype.month
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const month = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "month").get;
+
+assert.sameValue(typeof month, "function");
+
+assert.throws(TypeError, () => month.call(undefined), "undefined");
+assert.throws(TypeError, () => month.call(null), "null");
+assert.throws(TypeError, () => month.call(true), "true");
+assert.throws(TypeError, () => month.call(""), "empty string");
+assert.throws(TypeError, () => month.call(Symbol()), "symbol");
+assert.throws(TypeError, () => month.call(1), "1");
+assert.throws(TypeError, () => month.call({}), "plain object");
+assert.throws(TypeError, () => month.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime");
+assert.throws(TypeError, () => month.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..f754556f08
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.zoneddatetime.prototype.month
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const monthOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "month");
+Object.defineProperty(Temporal.Calendar.prototype, "month", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("month should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.month;
+
+Object.defineProperty(Temporal.Calendar.prototype, "month", monthOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/builtin-timezone-no-observable-calls.js
new file mode 100644
index 0000000000..fd396d9685
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/builtin-timezone-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.zoneddatetime.prototype.month
+description: >
+ Calling the method on an instance constructed with a builtin time zone causes
+ no observable lookups or calls to time zone methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.month;
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/custom.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/custom.js
new file mode 100644
index 0000000000..16071b8041
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/custom.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-get-temporal.zoneddatetime.prototype.month
+description: Custom calendar tests for month().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ month(...args) {
+ ++calls;
+ assert.compareArray(args.map(String), [instance].map((arg) => arg.toPlainDateTime().toString()), "month arguments");
+ return 7;
+ }
+}
+
+const calendar = new CustomCalendar();
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar);
+const result = instance.month;
+assert.sameValue(result, 7, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/prop-desc.js
new file mode 100644
index 0000000000..f60d4b541f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/prop-desc.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-get-temporal.zoneddatetime.prototype.month
+description: The "month" property of Temporal.ZonedDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "month");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..797b041649
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,18 @@
+// |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-get-temporal.zoneddatetime.prototype.month
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.month);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..3ae559d72f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/timezone-getoffsetnanosecondsfor-not-callable.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-get-temporal.zoneddatetime.prototype.month
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => datetime.month,
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..fcc5c7d50a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,18 @@
+// |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-get-temporal.zoneddatetime.prototype.month
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.month);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..7ddf852475
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/timezone-getoffsetnanosecondsfor-wrong-type.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-get-temporal.zoneddatetime.prototype.month
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.month);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/validate-calendar-value.js
new file mode 100644
index 0000000000..d43ee61f98
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/validate-calendar-value.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-get-temporal.zoneddatetime.prototype.month
+description: Validate result returned from calendar month() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [null, TypeError],
+ [false, TypeError],
+ [Infinity, RangeError],
+ [-Infinity, RangeError],
+ [NaN, RangeError],
+ [-7, RangeError],
+ [-0.1, RangeError],
+ ["string", TypeError],
+ [Symbol("foo"), TypeError],
+ [7n, TypeError],
+ [{}, TypeError],
+ [true, TypeError],
+ [7.1, RangeError],
+ ["7", TypeError],
+ ["7.5", TypeError],
+ [{valueOf() { return 7; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ month() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar);
+ assert.throws(error, () => instance.month, `${typeof result} ${String(result)} not converted to positive integer`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/branding.js
new file mode 100644
index 0000000000..a11ac57133
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/branding.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-get-temporal.zoneddatetime.prototype.monthcode
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const monthCode = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "monthCode").get;
+
+assert.sameValue(typeof monthCode, "function");
+
+assert.throws(TypeError, () => monthCode.call(undefined), "undefined");
+assert.throws(TypeError, () => monthCode.call(null), "null");
+assert.throws(TypeError, () => monthCode.call(true), "true");
+assert.throws(TypeError, () => monthCode.call(""), "empty string");
+assert.throws(TypeError, () => monthCode.call(Symbol()), "symbol");
+assert.throws(TypeError, () => monthCode.call(1), "1");
+assert.throws(TypeError, () => monthCode.call({}), "plain object");
+assert.throws(TypeError, () => monthCode.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime");
+assert.throws(TypeError, () => monthCode.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..861ad18b66
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.zoneddatetime.prototype.monthcode
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const monthCodeOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "monthCode");
+Object.defineProperty(Temporal.Calendar.prototype, "monthCode", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("monthCode should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.monthCode;
+
+Object.defineProperty(Temporal.Calendar.prototype, "monthCode", monthCodeOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/builtin-timezone-no-observable-calls.js
new file mode 100644
index 0000000000..9527493caa
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/builtin-timezone-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.zoneddatetime.prototype.monthcode
+description: >
+ Calling the method on an instance constructed with a builtin time zone causes
+ no observable lookups or calls to time zone methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.monthCode;
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/custom.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/custom.js
new file mode 100644
index 0000000000..60531203bf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/custom.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-get-temporal.zoneddatetime.prototype.monthcode
+description: Custom calendar tests for monthCode().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ monthCode(...args) {
+ ++calls;
+ assert.compareArray(args.map(String), [instance].map((arg) => arg.toPlainDateTime().toString()), "monthCode arguments");
+ return "M01";
+ }
+}
+
+const calendar = new CustomCalendar();
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar);
+const result = instance.monthCode;
+assert.sameValue(result, "M01", "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/prop-desc.js
new file mode 100644
index 0000000000..5dd6d071c9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/prop-desc.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-get-temporal.zoneddatetime.prototype.monthcode
+description: The "monthCode" property of Temporal.ZonedDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "monthCode");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..f2b7a6477c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,18 @@
+// |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-get-temporal.zoneddatetime.prototype.monthcode
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.monthCode);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..688409ca27
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/timezone-getoffsetnanosecondsfor-not-callable.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-get-temporal.zoneddatetime.prototype.monthcode
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => datetime.monthCode,
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..9d82bef0fe
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,18 @@
+// |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-get-temporal.zoneddatetime.prototype.monthcode
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.monthCode);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..50e670522c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/timezone-getoffsetnanosecondsfor-wrong-type.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-get-temporal.zoneddatetime.prototype.monthcode
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.monthCode);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/validate-calendar-value.js
new file mode 100644
index 0000000000..901d158707
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/validate-calendar-value.js
@@ -0,0 +1,31 @@
+// |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-get-temporal.zoneddatetime.prototype.monthcode
+description: Validate result returned from calendar monthCode() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [Symbol("foo"), TypeError],
+ [null, TypeError],
+ [true, TypeError],
+ [false, TypeError],
+ [7.1, TypeError],
+ [{toString() { return "M01"; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ monthCode() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar);
+ assert.throws(error, () => instance.monthCode, `${typeof result} ${String(result)} not converted to string`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/branding.js
new file mode 100644
index 0000000000..4c46612d39
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/branding.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-get-temporal.zoneddatetime.prototype.monthsinyear
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const monthsInYear = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "monthsInYear").get;
+
+assert.sameValue(typeof monthsInYear, "function");
+
+assert.throws(TypeError, () => monthsInYear.call(undefined), "undefined");
+assert.throws(TypeError, () => monthsInYear.call(null), "null");
+assert.throws(TypeError, () => monthsInYear.call(true), "true");
+assert.throws(TypeError, () => monthsInYear.call(""), "empty string");
+assert.throws(TypeError, () => monthsInYear.call(Symbol()), "symbol");
+assert.throws(TypeError, () => monthsInYear.call(1), "1");
+assert.throws(TypeError, () => monthsInYear.call({}), "plain object");
+assert.throws(TypeError, () => monthsInYear.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime");
+assert.throws(TypeError, () => monthsInYear.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..16dd9910c7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.zoneddatetime.prototype.monthsinyear
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const monthsInYearOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "monthsInYear");
+Object.defineProperty(Temporal.Calendar.prototype, "monthsInYear", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("monthsInYear should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.monthsInYear;
+
+Object.defineProperty(Temporal.Calendar.prototype, "monthsInYear", monthsInYearOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/builtin-timezone-no-observable-calls.js
new file mode 100644
index 0000000000..cf715f9643
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/builtin-timezone-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.zoneddatetime.prototype.monthsinyear
+description: >
+ Calling the method on an instance constructed with a builtin time zone causes
+ no observable lookups or calls to time zone methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.monthsInYear;
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/custom.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/custom.js
new file mode 100644
index 0000000000..07df89ecd8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/custom.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-get-temporal.zoneddatetime.prototype.monthsinyear
+description: Custom calendar tests for monthsInYear().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ monthsInYear(...args) {
+ ++calls;
+ assert.compareArray(args.map(String), [instance].map((arg) => arg.toPlainDateTime().toString()), "monthsInYear arguments");
+ return 7;
+ }
+}
+
+const calendar = new CustomCalendar();
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar);
+const result = instance.monthsInYear;
+assert.sameValue(result, 7, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/prop-desc.js
new file mode 100644
index 0000000000..bcad139c78
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/prop-desc.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-get-temporal.zoneddatetime.prototype.monthsinyear
+description: The "monthsInYear" property of Temporal.ZonedDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "monthsInYear");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..f5e3794a47
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,18 @@
+// |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-get-temporal.zoneddatetime.prototype.monthsinyear
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.monthsInYear);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..b6ade0c6e7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/timezone-getoffsetnanosecondsfor-not-callable.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-get-temporal.zoneddatetime.prototype.monthsinyear
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => datetime.monthsInYear,
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..19298d27bb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,18 @@
+// |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-get-temporal.zoneddatetime.prototype.monthsinyear
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.monthsInYear);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..c086a9c140
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/timezone-getoffsetnanosecondsfor-wrong-type.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-get-temporal.zoneddatetime.prototype.monthsinyear
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.monthsInYear);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/validate-calendar-value.js
new file mode 100644
index 0000000000..140e7ffebe
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/validate-calendar-value.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-get-temporal.zoneddatetime.prototype.monthsinyear
+description: Validate result returned from calendar monthsInYear() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [null, TypeError],
+ [false, TypeError],
+ [Infinity, RangeError],
+ [-Infinity, RangeError],
+ [NaN, RangeError],
+ [-7, RangeError],
+ [-0.1, RangeError],
+ ["string", TypeError],
+ [Symbol("foo"), TypeError],
+ [7n, TypeError],
+ [{}, TypeError],
+ [true, TypeError],
+ [7.1, RangeError],
+ ["7", TypeError],
+ ["7.5", TypeError],
+ [{valueOf() { return 7; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ monthsInYear() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar);
+ assert.throws(error, () => instance.monthsInYear, `${typeof result} ${String(result)} not converted to positive integer`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/branding.js
new file mode 100644
index 0000000000..bfc35fc9d5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/branding.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-get-temporal.zoneddatetime.prototype.nanosecond
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const nanosecond = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "nanosecond").get;
+
+assert.sameValue(typeof nanosecond, "function");
+
+assert.throws(TypeError, () => nanosecond.call(undefined), "undefined");
+assert.throws(TypeError, () => nanosecond.call(null), "null");
+assert.throws(TypeError, () => nanosecond.call(true), "true");
+assert.throws(TypeError, () => nanosecond.call(""), "empty string");
+assert.throws(TypeError, () => nanosecond.call(Symbol()), "symbol");
+assert.throws(TypeError, () => nanosecond.call(1), "1");
+assert.throws(TypeError, () => nanosecond.call({}), "plain object");
+assert.throws(TypeError, () => nanosecond.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime");
+assert.throws(TypeError, () => nanosecond.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/builtin-timezone-no-observable-calls.js
new file mode 100644
index 0000000000..f91ac58107
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/builtin-timezone-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.zoneddatetime.prototype.nanosecond
+description: >
+ Calling the method on an instance constructed with a builtin time zone causes
+ no observable lookups or calls to time zone methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.nanosecond;
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/negative-epochnanoseconds.js
new file mode 100644
index 0000000000..892b93bab0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/negative-epochnanoseconds.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.zoneddatetime.prototype.nanosecond
+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]
+---*/
+
+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.
+
+assert.sameValue(datetime.nanosecond, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/prop-desc.js
new file mode 100644
index 0000000000..fe1edf18b0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/prop-desc.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-get-temporal.zoneddatetime.prototype.nanosecond
+description: The "nanosecond" property of Temporal.ZonedDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "nanosecond");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..976416762b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,18 @@
+// |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-get-temporal.zoneddatetime.prototype.nanosecond
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.nanosecond);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..1d1626342f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/timezone-getoffsetnanosecondsfor-not-callable.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-get-temporal.zoneddatetime.prototype.nanosecond
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => datetime.nanosecond,
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..b481e7082b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,18 @@
+// |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-get-temporal.zoneddatetime.prototype.nanosecond
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.nanosecond);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..c1cfd89e9e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/timezone-getoffsetnanosecondsfor-wrong-type.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-get-temporal.zoneddatetime.prototype.nanosecond
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.nanosecond);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/basic.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/basic.js
new file mode 100644
index 0000000000..32c57e464b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/basic.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-get-temporal.zoneddatetime.prototype.offset
+description: Basic tests for Temporal.ZonedDateTime.prototype.offset
+features: [BigInt, Temporal]
+---*/
+
+function test(timeZoneIdentifier, expectedOffsetString, description) {
+ const timeZone = new Temporal.TimeZone(timeZoneIdentifier);
+ const datetime = new Temporal.ZonedDateTime(0n, timeZone);
+ assert.sameValue(datetime.offset, expectedOffsetString, description);
+}
+
+test("UTC", "+00:00", "offset of UTC is +00:00");
+test("+01:00", "+01:00", "positive offset");
+test("-05:00", "-05:00", "negative offset");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/branding.js
new file mode 100644
index 0000000000..9cacefd093
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/branding.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-get-temporal.zoneddatetime.prototype.offset
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const offset = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "offset").get;
+
+assert.sameValue(typeof offset, "function");
+
+assert.throws(TypeError, () => offset.call(undefined), "undefined");
+assert.throws(TypeError, () => offset.call(null), "null");
+assert.throws(TypeError, () => offset.call(true), "true");
+assert.throws(TypeError, () => offset.call(""), "empty string");
+assert.throws(TypeError, () => offset.call(Symbol()), "symbol");
+assert.throws(TypeError, () => offset.call(1), "1");
+assert.throws(TypeError, () => offset.call({}), "plain object");
+assert.throws(TypeError, () => offset.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime");
+assert.throws(TypeError, () => offset.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/builtin-timezone-no-observable-calls.js
new file mode 100644
index 0000000000..ee3c0c0504
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/builtin-timezone-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.zoneddatetime.prototype.offset
+description: >
+ Calling the method on an instance constructed with a builtin time zone causes
+ no observable lookups or calls to time zone methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.offset;
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/prop-desc.js
new file mode 100644
index 0000000000..1764dd61fa
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/prop-desc.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-get-temporal.zoneddatetime.prototype.offset
+description: The "offset" property of Temporal.ZonedDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "offset");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..0a46276d11
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,18 @@
+// |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-get-temporal.zoneddatetime.prototype.offset
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.offset);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..1f60e1b765
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/timezone-getoffsetnanosecondsfor-not-callable.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-get-temporal.zoneddatetime.prototype.offset
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => datetime.offset,
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..69db8612bd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,18 @@
+// |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-get-temporal.zoneddatetime.prototype.offset
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.offset);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..0d0ea6a623
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/timezone-getoffsetnanosecondsfor-wrong-type.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-get-temporal.zoneddatetime.prototype.offset
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.offset);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/branding.js
new file mode 100644
index 0000000000..f0c591f896
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/branding.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-get-temporal.zoneddatetime.prototype.offsetnanoseconds
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const offsetNanoseconds = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "offsetNanoseconds").get;
+
+assert.sameValue(typeof offsetNanoseconds, "function");
+
+assert.throws(TypeError, () => offsetNanoseconds.call(undefined), "undefined");
+assert.throws(TypeError, () => offsetNanoseconds.call(null), "null");
+assert.throws(TypeError, () => offsetNanoseconds.call(true), "true");
+assert.throws(TypeError, () => offsetNanoseconds.call(""), "empty string");
+assert.throws(TypeError, () => offsetNanoseconds.call(Symbol()), "symbol");
+assert.throws(TypeError, () => offsetNanoseconds.call(1), "1");
+assert.throws(TypeError, () => offsetNanoseconds.call({}), "plain object");
+assert.throws(TypeError, () => offsetNanoseconds.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime");
+assert.throws(TypeError, () => offsetNanoseconds.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/builtin-timezone-no-observable-calls.js
new file mode 100644
index 0000000000..4e67a66806
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/builtin-timezone-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.zoneddatetime.prototype.offsetnanoseconds
+description: >
+ Calling the method on an instance constructed with a builtin time zone causes
+ no observable lookups or calls to time zone methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.offsetNanoseconds;
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/prop-desc.js
new file mode 100644
index 0000000000..d1aa0c79c9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/prop-desc.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-get-temporal.zoneddatetime.prototype.offsetnanoseconds
+description: The "offsetNanoseconds" property of Temporal.ZonedDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "offsetNanoseconds");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..e6da8ef60c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,18 @@
+// |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-get-temporal.zoneddatetime.prototype.offsetnanoseconds
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.offsetNanoseconds);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..1970edc4ff
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/timezone-getoffsetnanosecondsfor-not-callable.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-get-temporal.zoneddatetime.prototype.offsetnanoseconds
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => datetime.offsetNanoseconds,
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..05a2465c80
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,18 @@
+// |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-get-temporal.zoneddatetime.prototype.offsetnanoseconds
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.offsetNanoseconds);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..70812b8800
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/timezone-getoffsetnanosecondsfor-wrong-type.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-get-temporal.zoneddatetime.prototype.offsetnanoseconds
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.offsetNanoseconds);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/prop-desc.js
new file mode 100644
index 0000000000..5af4afe5cf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/prop-desc.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-zoneddatetime-prototype
+description: The "prototype" property of Temporal.ZonedDateTime
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(typeof Temporal.ZonedDateTime.prototype, "object");
+assert.notSameValue(Temporal.ZonedDateTime.prototype, null);
+
+verifyProperty(Temporal.ZonedDateTime, "prototype", {
+ writable: false,
+ enumerable: false,
+ configurable: false,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/branding.js
new file mode 100644
index 0000000000..ccb7839122
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/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.zoneddatetime.prototype.round
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const round = Temporal.ZonedDateTime.prototype.round;
+
+assert.sameValue(typeof round, "function");
+
+const args = ["hour"];
+
+assert.throws(TypeError, () => round.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => round.apply(null, args), "null");
+assert.throws(TypeError, () => round.apply(true, args), "true");
+assert.throws(TypeError, () => round.apply("", args), "empty string");
+assert.throws(TypeError, () => round.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => round.apply(1, args), "1");
+assert.throws(TypeError, () => round.apply({}, args), "plain object");
+assert.throws(TypeError, () => round.apply(Temporal.ZonedDateTime, args), "Temporal.ZonedDateTime");
+assert.throws(TypeError, () => round.apply(Temporal.ZonedDateTime.prototype, args), "Temporal.ZonedDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..fd909ce3de
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.zoneddatetime.prototype.round
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dateAddOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dateAdd");
+Object.defineProperty(Temporal.Calendar.prototype, "dateAdd", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("dateAdd should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.round("day");
+
+Object.defineProperty(Temporal.Calendar.prototype, "dateAdd", dateAddOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/builtin-timezone-no-observable-calls.js
new file mode 100644
index 0000000000..74e7a5376e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/builtin-timezone-no-observable-calls.js
@@ -0,0 +1,37 @@
+// |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.zoneddatetime.prototype.round
+description: >
+ Calling the method on an instance constructed with a builtin time zone causes
+ no observable lookups or calls to time zone methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+const getPossibleInstantsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getPossibleInstantsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.round("day");
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", getPossibleInstantsForOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/builtin.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/builtin.js
new file mode 100644
index 0000000000..bca23b8eac
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/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.zoneddatetime.prototype.round
+description: >
+ Tests that Temporal.ZonedDateTime.prototype.round
+ 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.ZonedDateTime.prototype.round),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.round),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.round),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.ZonedDateTime.prototype.round.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/div-zero.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/div-zero.js
new file mode 100644
index 0000000000..776cb7454f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/div-zero.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.zoneddatetime.prototype.round
+description: RangeError thrown if the calculated day length is zero
+features: [Temporal]
+---*/
+
+class TimeZone extends Temporal.TimeZone {
+ #calls = 0;
+ getPossibleInstantsFor(dateTime) {
+ if (++this.#calls === 2) {
+ return super.getPossibleInstantsFor(dateTime.withCalendar("iso8601").subtract({ days: 1 }));
+ }
+ return super.getPossibleInstantsFor(dateTime);
+ }
+}
+
+const units = ["hour", "minute", "second", "millisecond", "microsecond", "nanosecond"];
+for (const smallestUnit of units) {
+ const zdt = new Temporal.ZonedDateTime(0n, new TimeZone("UTC"));
+ assert.throws(RangeError, () => zdt.round({ smallestUnit, roundingIncrement: 2 }), `zero day-length with smallestUnit ${smallestUnit}`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/getpossibleinstantsfor-called-with-iso8601-calendar.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/getpossibleinstantsfor-called-with-iso8601-calendar.js
new file mode 100644
index 0000000000..0c622df167
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/getpossibleinstantsfor-called-with-iso8601-calendar.js
@@ -0,0 +1,58 @@
+// |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.zoneddatetime.prototype.round
+description: >
+ Time zone's getPossibleInstantsFor is called with a PlainDateTime with the
+ built-in ISO 8601 calendar
+features: [Temporal]
+info: |
+ DisambiguatePossibleInstants:
+ 2. Let _n_ be _possibleInstants_'s length.
+ ...
+ 5. Assert: _n_ = 0.
+ ...
+ 19. If _disambiguation_ is *"earlier"*, then
+ ...
+ c. Let _earlierDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*).
+ d. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _earlierDateTime_).
+ ...
+ 20. Assert: _disambiguation_ is *"compatible"* or *"later"*.
+ ...
+ 23. Let _laterDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*).
+ 24. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _laterDateTime_).
+---*/
+
+class SkippedDateTime extends Temporal.TimeZone {
+ constructor() {
+ super("UTC");
+ this.calls = 0;
+ }
+
+ getPossibleInstantsFor(dateTime) {
+ // Calls occur in pairs. For the first one return no possible instants so
+ // that DisambiguatePossibleInstants will call it again
+ if (this.calls++ % 2 == 0) {
+ return [];
+ }
+
+ assert.sameValue(
+ dateTime.getISOFields().calendar,
+ "iso8601",
+ "getPossibleInstantsFor called with dateTime with built-in ISO 8601 calendar"
+ );
+ return super.getPossibleInstantsFor(dateTime);
+ }
+}
+
+const nonBuiltinISOCalendar = new Temporal.Calendar("iso8601");
+const timeZone = new SkippedDateTime();
+
+const instance = new Temporal.ZonedDateTime(0n, timeZone, nonBuiltinISOCalendar);
+instance.round({ smallestUnit: "hours" });
+
+assert.sameValue(timeZone.calls, 6, "getPossibleInstantsFor should have been called 6 times");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/length.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/length.js
new file mode 100644
index 0000000000..7e48739e9d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/length.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.zoneddatetime.prototype.round
+description: Temporal.ZonedDateTime.prototype.round.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.ZonedDateTime.prototype.round, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/name.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/name.js
new file mode 100644
index 0000000000..a91ac4b450
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/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.zoneddatetime.prototype.round
+description: Temporal.ZonedDateTime.prototype.round.name is "round".
+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.ZonedDateTime.prototype.round, "name", {
+ value: "round",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/negative-epochnanoseconds.js
new file mode 100644
index 0000000000..827a5b440a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/negative-epochnanoseconds.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.zoneddatetime.prototype.round
+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]
+---*/
+
+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 result = datetime.round({ smallestUnit: "millisecond" });
+assert.sameValue(result.epochNanoseconds, -13849765_000_000_000n);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/not-a-constructor.js
new file mode 100644
index 0000000000..75d14fe479
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/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.zoneddatetime.prototype.round
+description: >
+ Temporal.ZonedDateTime.prototype.round 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.ZonedDateTime.prototype.round();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.round), false,
+ "isConstructor(Temporal.ZonedDateTime.prototype.round)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/options-wrong-type.js
new file mode 100644
index 0000000000..c2bf08c0f0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/options-wrong-type.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.zoneddatetime.prototype.round
+description: TypeError thrown when options argument is missing or a non-string primitive
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const badOptions = [
+ undefined,
+ null,
+ true,
+ Symbol(),
+ 1,
+ 2n,
+];
+
+const instance = new Temporal.ZonedDateTime(0n, "UTC");
+assert.throws(TypeError, () => instance.round(), "TypeError on missing options argument");
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.round(value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/order-of-operations.js
new file mode 100644
index 0000000000..48ca638b39
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/order-of-operations.js
@@ -0,0 +1,158 @@
+// |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.zoneddatetime.prototype.round
+description: Properties on objects passed to round() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "get options.roundingIncrement",
+ "get options.roundingIncrement.valueOf",
+ "call options.roundingIncrement.valueOf",
+ "get options.roundingMode",
+ "get options.roundingMode.toString",
+ "call options.roundingMode.toString",
+ "get options.smallestUnit",
+ "get options.smallestUnit.toString",
+ "call options.smallestUnit.toString",
+ // lookup
+ "get this.timeZone.getOffsetNanosecondsFor",
+ "get this.timeZone.getPossibleInstantsFor",
+ // GetPlainDateTimeFor on receiver's instant
+ "call this.timeZone.getOffsetNanosecondsFor",
+ // GetInstantFor on preceding midnight
+ "call this.timeZone.getPossibleInstantsFor",
+ // AddDaysToZonedDateTime
+ "call this.timeZone.getPossibleInstantsFor",
+ // InterpretISODateTimeOffset
+ "call this.timeZone.getPossibleInstantsFor",
+ "call this.timeZone.getOffsetNanosecondsFor",
+];
+const actual = [];
+
+const options = TemporalHelpers.propertyBagObserver(actual, {
+ smallestUnit: "nanoseconds",
+ roundingMode: "halfExpand",
+ roundingIncrement: 2,
+}, "options");
+
+const nextHourOptions = TemporalHelpers.propertyBagObserver(actual, {
+ smallestUnit: "hour",
+ roundingMode: "ceil",
+ roundingIncrement: 1,
+}, "options");
+
+const calendar = TemporalHelpers.calendarObserver(actual, "this.calendar");
+const instance = new Temporal.ZonedDateTime(
+ 988786472_987_654_321n, /* 2001-05-02T06:54:32.987654321Z */
+ TemporalHelpers.timeZoneObserver(actual, "this.timeZone"),
+ calendar,
+);
+
+const fallBackTimeZone = TemporalHelpers.oneShiftTimeZone(Temporal.Instant.fromEpochSeconds(1800), -3600_000_000_000);
+const fallBackTimeZoneObserver = TemporalHelpers.timeZoneObserver(actual, "this.timeZone", {
+ getOffsetNanosecondsFor: fallBackTimeZone.getOffsetNanosecondsFor.bind(fallBackTimeZone),
+ getPossibleInstantsFor: fallBackTimeZone.getPossibleInstantsFor.bind(fallBackTimeZone),
+});
+const fallBackInstance = new Temporal.ZonedDateTime(0n, fallBackTimeZoneObserver, calendar);
+const beforeFallBackInstance = new Temporal.ZonedDateTime(-3599_000_000_000n, fallBackTimeZoneObserver, calendar);
+
+const springForwardTimeZone = TemporalHelpers.oneShiftTimeZone(Temporal.Instant.fromEpochSeconds(-1800), 3600_000_000_000);
+const springForwardTimeZoneObserver = TemporalHelpers.timeZoneObserver(actual, "this.timeZone", {
+ getOffsetNanosecondsFor: springForwardTimeZone.getOffsetNanosecondsFor.bind(springForwardTimeZone),
+ getPossibleInstantsFor: springForwardTimeZone.getPossibleInstantsFor.bind(springForwardTimeZone),
+});
+const springForwardInstance = new Temporal.ZonedDateTime(0n, springForwardTimeZoneObserver, calendar);
+const beforeSpringForwardInstance = new Temporal.ZonedDateTime(-3599_000_000_000n, springForwardTimeZoneObserver, calendar);
+// clear any observable operations that happen due to time zone or calendar
+// calls in the constructors
+actual.splice(0);
+
+instance.round(options);
+assert.compareArray(actual, expected, "order of operations");
+actual.splice(0); // clear
+
+fallBackInstance.round(options);
+assert.compareArray(actual, expected, "order of operations with preceding midnight at repeated wall-clock time");
+actual.splice(0); // clear
+
+beforeFallBackInstance.round(nextHourOptions);
+assert.compareArray(actual, expected, "order of operations with rounding result at repeated wall-clock time");
+actual.splice(0); // clear
+
+const expectedSkippedDateTime = [
+ "get options.roundingIncrement",
+ "get options.roundingIncrement.valueOf",
+ "call options.roundingIncrement.valueOf",
+ "get options.roundingMode",
+ "get options.roundingMode.toString",
+ "call options.roundingMode.toString",
+ "get options.smallestUnit",
+ "get options.smallestUnit.toString",
+ "call options.smallestUnit.toString",
+ // lookup
+ "get this.timeZone.getOffsetNanosecondsFor",
+ "get this.timeZone.getPossibleInstantsFor",
+ // GetPlainDateTimeFor on receiver's instant
+ "call this.timeZone.getOffsetNanosecondsFor",
+ // GetInstantFor on preceding midnight
+ "call this.timeZone.getPossibleInstantsFor",
+ // DisambiguatePossibleInstants
+ "call this.timeZone.getOffsetNanosecondsFor",
+ "call this.timeZone.getOffsetNanosecondsFor",
+ "call this.timeZone.getPossibleInstantsFor",
+ // AddZonedDateTime
+ "call this.timeZone.getPossibleInstantsFor",
+ // InterpretISODateTimeOffset
+ "call this.timeZone.getPossibleInstantsFor",
+ "call this.timeZone.getOffsetNanosecondsFor",
+];
+
+springForwardInstance.round(options);
+assert.compareArray(actual, expectedSkippedDateTime, "order of operations with preceding midnight at skipped wall-clock time");
+actual.splice(0); // clear
+
+const expectedSkippedResult = [
+ "get options.roundingIncrement",
+ "get options.roundingIncrement.valueOf",
+ "call options.roundingIncrement.valueOf",
+ "get options.roundingMode",
+ "get options.roundingMode.toString",
+ "call options.roundingMode.toString",
+ "get options.smallestUnit",
+ "get options.smallestUnit.toString",
+ "call options.smallestUnit.toString",
+ // lookup
+ "get this.timeZone.getOffsetNanosecondsFor",
+ "get this.timeZone.getPossibleInstantsFor",
+ // GetPlainDateTimeFor on receiver's instant
+ "call this.timeZone.getOffsetNanosecondsFor",
+ // GetInstantFor on preceding midnight
+ "call this.timeZone.getPossibleInstantsFor",
+ // AddDaysToZonedDateTime
+ "call this.timeZone.getPossibleInstantsFor",
+ // DisambiguatePossibleInstants
+ "call this.timeZone.getOffsetNanosecondsFor",
+ "call this.timeZone.getOffsetNanosecondsFor",
+ "call this.timeZone.getPossibleInstantsFor",
+ // InterpretISODateTimeOffset
+ "call this.timeZone.getPossibleInstantsFor",
+ // DisambiguatePossibleInstants
+ "call this.timeZone.getOffsetNanosecondsFor",
+ "call this.timeZone.getOffsetNanosecondsFor",
+ "call this.timeZone.getPossibleInstantsFor",
+];
+
+beforeSpringForwardInstance.round(nextHourOptions);
+assert.compareArray(
+ actual,
+ expectedSkippedResult,
+ "order of operations with following midnight and rounding result at skipped wall-clock time"
+);
+actual.splice(0); // clear
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/prop-desc.js
new file mode 100644
index 0000000000..aa8fbfef3d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/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.zoneddatetime.prototype.round
+description: The "round" property of Temporal.ZonedDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.ZonedDateTime.prototype.round,
+ "function",
+ "`typeof ZonedDateTime.prototype.round` is `function`"
+);
+
+verifyProperty(Temporal.ZonedDateTime.prototype, "round", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/rounding-direction.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/rounding-direction.js
new file mode 100644
index 0000000000..095bb6640f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/rounding-direction.js
@@ -0,0 +1,33 @@
+// |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.zoneddatetime.prototype.round
+description: Rounding down is towards the Big Bang, not the epoch or 1 BCE
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(-65_261_246_399_500_000_000n, "UTC"); // -000099-12-15T12:00:00.5Z
+assert.sameValue(
+ instance.round({ smallestUnit: "second", roundingMode: "floor" }).epochNanoseconds,
+ -65_261_246_400_000_000_000n, // -000099-12-15T12:00:00Z
+ "Rounding down is towards the Big Bang, not the epoch or 1 BCE (roundingMode floor)"
+);
+assert.sameValue(
+ instance.round({ smallestUnit: "second", roundingMode: "trunc" }).epochNanoseconds,
+ -65_261_246_400_000_000_000n, // -000099-12-15T12:00:00Z
+ "Rounding down is towards the Big Bang, not the epoch or 1 BCE (roundingMode trunc)"
+);
+assert.sameValue(
+ instance.round({ smallestUnit: "second", roundingMode: "ceil" }).epochNanoseconds,
+ -65_261_246_399_000_000_000n, // -000099-12-15T12:00:01Z
+ "Rounding up is away from the Big Bang, not the epoch or 1 BCE (roundingMode ceil)"
+);
+assert.sameValue(
+ instance.round({ smallestUnit: "second", roundingMode: "halfExpand" }).epochNanoseconds,
+ -65_261_246_399_000_000_000n, // -000099-12-15T12:00:01Z
+ "Rounding up is away from the Big Bang, not the epoch or 1 BCE (roundingMode halfExpand)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/rounding-is-noop.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/rounding-is-noop.js
new file mode 100644
index 0000000000..a52601fe31
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/rounding-is-noop.js
@@ -0,0 +1,38 @@
+// |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.zoneddatetime.prototype.round
+description: >
+ No calendar or time zone methods are called under circumstances where rounding
+ is a no-op
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarThrowEverything();
+const timeZone = TemporalHelpers.timeZoneThrowEverything();
+const instance = new Temporal.ZonedDateTime(0n, timeZone, calendar);
+
+const noopRoundingOperations = [
+ [{ smallestUnit: "nanoseconds" }, "smallestUnit ns"],
+ [{ smallestUnit: "nanoseconds", roundingIncrement: 1 }, "round to 1 ns"],
+];
+for (const [options, descr] of noopRoundingOperations) {
+ const result = instance.round(options);
+ assert.notSameValue(result, instance, "rounding result should be a new object");
+ assert.sameValue(result.epochNanoseconds, instance.epochNanoseconds, "instant should be unchanged");
+ assert.sameValue(result.getCalendar(), instance.getCalendar(), "calendar should be preserved");
+ assert.sameValue(result.getTimeZone(), instance.getTimeZone(), "time zone should be preserved");
+}
+
+const notNoopRoundingOperations = [
+ [{ smallestUnit: "microseconds" }, "round to 1 µs"],
+ [{ smallestUnit: "nanoseconds", roundingIncrement: 2 }, "round to 2 ns"],
+];
+for (const [options, descr] of notNoopRoundingOperations) {
+ assert.throws(Test262Error, () => instance.round(options), `rounding should not be a no-op with ${descr}`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingincrement-nan.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingincrement-nan.js
new file mode 100644
index 0000000000..89bc7f34f8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingincrement-nan.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.zoneddatetime.prototype.round
+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-totemporaldatetimeroundingincrement step 5:
+ 5. Return ? ToTemporalRoundingIncrement(_normalizedOptions_, _maximum_, *false*).
+ sec-temporal.zoneddatetime.prototype.round step 8:
+ 8. Let _roundingIncrement_ be ? ToTemporalDateTimeRoundingIncrement(_options_, _smallestUnit_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+assert.throws(RangeError, () => datetime.round({ smallestUnit: 'second', roundingIncrement: NaN }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingincrement-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingincrement-non-integer.js
new file mode 100644
index 0000000000..bca19257d7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingincrement-non-integer.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.zoneddatetime.prototype.round
+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_.
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_005n, "UTC");
+const result = datetime.round({ smallestUnit: "nanosecond", roundingIncrement: 2.5, roundingMode: "expand" });
+assert.sameValue(result.epochNanoseconds, 1_000_000_000_000_000_006n, "roundingIncrement 2.5 truncates to 2");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingincrement-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingincrement-out-of-range.js
new file mode 100644
index 0000000000..c7b5daf9b2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingincrement-out-of-range.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.zoneddatetime.prototype.round
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_005n, "UTC");
+assert.throws(RangeError, () => datetime.round({ smallestUnit: "nanoseconds", roundingIncrement: -Infinity }));
+assert.throws(RangeError, () => datetime.round({ smallestUnit: "nanoseconds", roundingIncrement: -1 }));
+assert.throws(RangeError, () => datetime.round({ smallestUnit: "nanoseconds", roundingIncrement: 0 }));
+assert.throws(RangeError, () => datetime.round({ smallestUnit: "nanoseconds", roundingIncrement: 0.9 }));
+assert.throws(RangeError, () => datetime.round({ smallestUnit: "nanoseconds", roundingIncrement: 1e9 + 1 }));
+assert.throws(RangeError, () => datetime.round({ smallestUnit: "nanoseconds", roundingIncrement: Infinity }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingincrement-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingincrement-undefined.js
new file mode 100644
index 0000000000..95e3b77b12
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/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.zoneddatetime.prototype.round
+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-totemporaldatetimeroundingincrement step 5:
+ 5. Return ? ToTemporalRoundingIncrement(_normalizedOptions_, _maximum_, *false*).
+ sec-temporal.zoneddatetime.prototype.round step 8:
+ 8. Let _roundingIncrement_ be ? ToTemporalDateTimeRoundingIncrement(_options_, _smallestUnit_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+
+const explicit = datetime.round({ smallestUnit: 'second', roundingIncrement: undefined });
+assert.sameValue(explicit.epochNanoseconds, 1_000_000_001_000_000_000n, "default roundingIncrement is 1");
+
+const implicit = datetime.round({ smallestUnit: 'second' });
+assert.sameValue(implicit.epochNanoseconds, 1_000_000_001_000_000_000n, "default roundingIncrement is 1");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingincrement-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingincrement-wrong-type.js
new file mode 100644
index 0000000000..4b21c1e712
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingincrement-wrong-type.js
@@ -0,0 +1,29 @@
+// |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.zoneddatetime.prototype.round
+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-totemporaldatetimeroundingincrement step 5:
+ 5. Return ? ToTemporalRoundingIncrement(_normalizedOptions_, _maximum_, *false*).
+ sec-temporal.zoneddatetime.prototype.round step 8:
+ 8. Let _roundingIncrement_ be ? ToTemporalDateTimeRoundingIncrement(_options_, _smallestUnit_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+
+TemporalHelpers.checkRoundingIncrementOptionWrongType(
+ (roundingIncrement) => datetime.round({ smallestUnit: 'second', roundingIncrement }),
+ (result, descr) => assert.sameValue(result.epochNanoseconds, 1_000_000_001_000_000_000n, descr),
+ (result, descr) => assert.sameValue(result.epochNanoseconds, 1_000_000_000_000_000_000n, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-ceil.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-ceil.js
new file mode 100644
index 0000000000..6ca2d5363a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-ceil.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.zoneddatetime.prototype.round
+description: Tests calculations with roundingMode "ceil".
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(217175010_123_987_500n /* 1976-11-18T15:23:30.1239875+01:00 */, "+01:00");
+
+const expected = [
+ ["day", 217206000_000_000_000n /* 1976-11-19T00:00:00+01:00 */],
+ ["minute", 217175040_000_000_000n /* 1976-11-18T15:24:00+01:00 */],
+ ["second", 217175011_000_000_000n /* 1976-11-18T15:23:31+01:00 */],
+ ["millisecond", 217175010_124_000_000n /* 1976-11-18T15:23:30.124+01:00 */],
+ ["microsecond", 217175010_123_988_000n /* 1976-11-18T15:23:30.123988+01:00 */],
+ ["nanosecond", 217175010_123_987_500n /* 1976-11-18T15:23:30.1239875+01:00 */],
+];
+
+const roundingMode = "ceil";
+
+expected.forEach(([smallestUnit, expected]) => {
+ assert.sameValue(
+ instance.round({ smallestUnit, roundingMode }).epochNanoseconds,
+ expected,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-expand.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-expand.js
new file mode 100644
index 0000000000..8ba1f7a9a3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-expand.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.zoneddatetime.prototype.round
+description: Tests calculations with roundingMode "expand".
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(217175010_123_987_500n /* 1976-11-18T15:23:30.1239875+01:00 */, "+01:00");
+
+const expected = [
+ ["day", 217206000_000_000_000n /* 1976-11-19T00:00:00+01:00 */],
+ ["minute", 217175040_000_000_000n /* 1976-11-18T15:24:00+01:00 */],
+ ["second", 217175011_000_000_000n /* 1976-11-18T15:23:31+01:00 */],
+ ["millisecond", 217175010_124_000_000n /* 1976-11-18T15:23:30.124+01:00 */],
+ ["microsecond", 217175010_123_988_000n /* 1976-11-18T15:23:30.123988+01:00 */],
+ ["nanosecond", 217175010_123_987_500n /* 1976-11-18T15:23:30.1239875+01:00 */],
+];
+
+const roundingMode = "expand";
+
+expected.forEach(([smallestUnit, expected]) => {
+ assert.sameValue(
+ instance.round({ smallestUnit, roundingMode }).epochNanoseconds,
+ expected,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-floor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-floor.js
new file mode 100644
index 0000000000..5ce8c322ac
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-floor.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.zoneddatetime.prototype.round
+description: Tests calculations with roundingMode "floor".
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(217175010_123_987_500n /* 1976-11-18T15:23:30.1239875+01:00 */, "+01:00");
+
+const expected = [
+ ["day", 217119600_000_000_000n /* 1976-11-18T00:00:00+01:00 */],
+ ["minute", 217174980_000_000_000n /* 1976-11-18T15:23:00+01:00 */],
+ ["second", 217175010_000_000_000n /* 1976-11-18T15:23:30+01:00 */],
+ ["millisecond", 217175010_123_000_000n /* 1976-11-18T15:23:30.123+01:00 */],
+ ["microsecond", 217175010_123_987_000n /* 1976-11-18T15:23:30.123987+01:00 */],
+ ["nanosecond", 217175010_123_987_500n /* 1976-11-18T15:23:30.1239875+01:00 */],
+];
+
+const roundingMode = "floor";
+
+expected.forEach(([smallestUnit, expected]) => {
+ assert.sameValue(
+ instance.round({ smallestUnit, roundingMode }).epochNanoseconds,
+ expected,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-halfCeil.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-halfCeil.js
new file mode 100644
index 0000000000..3c76d77adb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-halfCeil.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.zoneddatetime.prototype.round
+description: Tests calculations with roundingMode "halfCeil".
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(217175010_123_987_500n /* 1976-11-18T15:23:30.1239875+01:00 */, "+01:00");
+
+const expected = [
+ ["day", 217206000_000_000_000n /* 1976-11-19T00:00:00+01:00 */],
+ ["minute", 217175040_000_000_000n /* 1976-11-18T15:24:00+01:00 */],
+ ["second", 217175010_000_000_000n /* 1976-11-18T15:23:30+01:00 */],
+ ["millisecond", 217175010_124_000_000n /* 1976-11-18T15:23:30.124+01:00 */],
+ ["microsecond", 217175010_123_988_000n /* 1976-11-18T15:23:30.123988+01:00 */],
+ ["nanosecond", 217175010_123_987_500n /* 1976-11-18T15:23:30.1239875+01:00 */],
+];
+
+const roundingMode = "halfCeil";
+
+expected.forEach(([smallestUnit, expected]) => {
+ assert.sameValue(
+ instance.round({ smallestUnit, roundingMode }).epochNanoseconds,
+ expected,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-halfEven.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-halfEven.js
new file mode 100644
index 0000000000..910937fcf6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-halfEven.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.zoneddatetime.prototype.round
+description: Tests calculations with roundingMode "halfEven".
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(217175010_123_987_500n /* 1976-11-18T15:23:30.1239875+01:00 */, "+01:00");
+
+const expected = [
+ ["day", 217206000_000_000_000n /* 1976-11-19T00:00:00+01:00 */],
+ ["minute", 217175040_000_000_000n /* 1976-11-18T15:24:00+01:00 */],
+ ["second", 217175010_000_000_000n /* 1976-11-18T15:23:30+01:00 */],
+ ["millisecond", 217175010_124_000_000n /* 1976-11-18T15:23:30.124+01:00 */],
+ ["microsecond", 217175010_123_988_000n /* 1976-11-18T15:23:30.123988+01:00 */],
+ ["nanosecond", 217175010_123_987_500n /* 1976-11-18T15:23:30.1239875+01:00 */],
+];
+
+const roundingMode = "halfEven";
+
+expected.forEach(([smallestUnit, expected]) => {
+ assert.sameValue(
+ instance.round({ smallestUnit, roundingMode }).epochNanoseconds,
+ expected,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-halfExpand.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-halfExpand.js
new file mode 100644
index 0000000000..aa42588b57
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-halfExpand.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.zoneddatetime.prototype.round
+description: Tests calculations with roundingMode "halfExpand".
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(217175010_123_987_500n /* 1976-11-18T15:23:30.1239875+01:00 */, "+01:00");
+
+const expected = [
+ ["day", 217206000_000_000_000n /* 1976-11-19T00:00:00+01:00 */],
+ ["minute", 217175040_000_000_000n /* 1976-11-18T15:24:00+01:00 */],
+ ["second", 217175010_000_000_000n /* 1976-11-18T15:23:30+01:00 */],
+ ["millisecond", 217175010_124_000_000n /* 1976-11-18T15:23:30.124+01:00 */],
+ ["microsecond", 217175010_123_988_000n /* 1976-11-18T15:23:30.123988+01:00 */],
+ ["nanosecond", 217175010_123_987_500n /* 1976-11-18T15:23:30.1239875+01:00 */],
+];
+
+const roundingMode = "halfExpand";
+
+expected.forEach(([smallestUnit, expected]) => {
+ assert.sameValue(
+ instance.round({ smallestUnit, roundingMode }).epochNanoseconds,
+ expected,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-halfFloor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-halfFloor.js
new file mode 100644
index 0000000000..f41ae34d5d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-halfFloor.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.zoneddatetime.prototype.round
+description: Tests calculations with roundingMode "halfFloor".
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(217175010_123_987_500n /* 1976-11-18T15:23:30.1239875+01:00 */, "+01:00");
+
+const expected = [
+ ["day", 217206000_000_000_000n /* 1976-11-19T00:00:00+01:00 */],
+ ["minute", 217175040_000_000_000n /* 1976-11-18T15:24:00+01:00 */],
+ ["second", 217175010_000_000_000n /* 1976-11-18T15:23:30+01:00 */],
+ ["millisecond", 217175010_124_000_000n /* 1976-11-18T15:23:30.124+01:00 */],
+ ["microsecond", 217175010_123_987_000n /* 1976-11-18T15:23:30.123987+01:00 */],
+ ["nanosecond", 217175010_123_987_500n /* 1976-11-18T15:23:30.1239875+01:00 */],
+];
+
+const roundingMode = "halfFloor";
+
+expected.forEach(([smallestUnit, expected]) => {
+ assert.sameValue(
+ instance.round({ smallestUnit, roundingMode }).epochNanoseconds,
+ expected,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-halfTrunc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-halfTrunc.js
new file mode 100644
index 0000000000..68c26b68b8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-halfTrunc.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.zoneddatetime.prototype.round
+description: Tests calculations with roundingMode "halfTrunc".
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(217175010_123_987_500n /* 1976-11-18T15:23:30.1239875+01:00 */, "+01:00");
+
+const expected = [
+ ["day", 217206000_000_000_000n /* 1976-11-19T00:00:00+01:00 */],
+ ["minute", 217175040_000_000_000n /* 1976-11-18T15:24:00+01:00 */],
+ ["second", 217175010_000_000_000n /* 1976-11-18T15:23:30+01:00 */],
+ ["millisecond", 217175010_124_000_000n /* 1976-11-18T15:23:30.124+01:00 */],
+ ["microsecond", 217175010_123_987_000n /* 1976-11-18T15:23:30.123987+01:00 */],
+ ["nanosecond", 217175010_123_987_500n /* 1976-11-18T15:23:30.1239875+01:00 */],
+];
+
+const roundingMode = "halfTrunc";
+
+expected.forEach(([smallestUnit, expected]) => {
+ assert.sameValue(
+ instance.round({ smallestUnit, roundingMode }).epochNanoseconds,
+ expected,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-invalid-string.js
new file mode 100644
index 0000000000..82051763ed
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-invalid-string.js
@@ -0,0 +1,16 @@
+// |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.zoneddatetime.prototype.round
+description: RangeError thrown when roundingMode option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_123_987_500n, "UTC");
+for (const roundingMode of ["other string", "cile", "CEIL", "ce\u0131l", "auto", "halfexpand", "floor\0"]) {
+ assert.throws(RangeError, () => datetime.round({ smallestUnit: "microsecond", roundingMode }));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-trunc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-trunc.js
new file mode 100644
index 0000000000..05cd46c481
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-trunc.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.zoneddatetime.prototype.round
+description: Tests calculations with roundingMode "trunc".
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(217175010_123_987_500n /* 1976-11-18T15:23:30.1239875+01:00 */, "+01:00");
+
+const expected = [
+ ["day", 217119600_000_000_000n /* 1976-11-18T00:00:00+01:00 */],
+ ["minute", 217174980_000_000_000n /* 1976-11-18T15:23:00+01:00 */],
+ ["second", 217175010_000_000_000n /* 1976-11-18T15:23:30+01:00 */],
+ ["millisecond", 217175010_123_000_000n /* 1976-11-18T15:23:30.123+01:00 */],
+ ["microsecond", 217175010_123_987_000n /* 1976-11-18T15:23:30.123987+01:00 */],
+ ["nanosecond", 217175010_123_987_500n /* 1976-11-18T15:23:30.1239875+01:00 */],
+];
+
+const roundingMode = "trunc";
+
+expected.forEach(([smallestUnit, expected]) => {
+ assert.sameValue(
+ instance.round({ smallestUnit, roundingMode }).epochNanoseconds,
+ expected,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-undefined.js
new file mode 100644
index 0000000000..0add782f70
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-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.zoneddatetime.prototype.round
+description: Fallback value for roundingMode option
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_123_987_500n, "UTC");
+
+const explicit1 = datetime.round({ smallestUnit: "microsecond", roundingMode: undefined });
+assert.sameValue(explicit1.epochNanoseconds, 1_000_000_000_123_988_000n, "default roundingMode is halfExpand");
+const implicit1 = datetime.round({ smallestUnit: "microsecond" });
+assert.sameValue(implicit1.epochNanoseconds, 1_000_000_000_123_988_000n, "default roundingMode is halfExpand");
+
+const explicit2 = datetime.round({ smallestUnit: "millisecond", roundingMode: undefined });
+assert.sameValue(explicit2.epochNanoseconds, 1_000_000_000_124_000_000n, "default roundingMode is halfExpand");
+const implicit2 = datetime.round({ smallestUnit: "millisecond" });
+assert.sameValue(implicit2.epochNanoseconds, 1_000_000_000_124_000_000n, "default roundingMode is halfExpand");
+
+const explicit3 = datetime.round({ smallestUnit: "second", roundingMode: undefined });
+assert.sameValue(explicit3.epochNanoseconds, 1_000_000_000_000_000_000n, "default roundingMode is halfExpand");
+const implicit3 = datetime.round({ smallestUnit: "second" });
+assert.sameValue(implicit3.epochNanoseconds, 1_000_000_000_000_000_000n, "default roundingMode is halfExpand");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-wrong-type.js
new file mode 100644
index 0000000000..921a1ef5c9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-wrong-type.js
@@ -0,0 +1,18 @@
+// |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.zoneddatetime.prototype.round
+description: Type conversions for roundingMode option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_123_987_500n, "UTC");
+TemporalHelpers.checkStringOptionWrongType("roundingMode", "halfExpand",
+ (roundingMode) => datetime.round({ smallestUnit: "microsecond", roundingMode }),
+ (result, descr) => assert.sameValue(result.epochNanoseconds, 1_000_000_000_123_988_000n, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundto-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundto-invalid-string.js
new file mode 100644
index 0000000000..47fba41c19
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundto-invalid-string.js
@@ -0,0 +1,36 @@
+// |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.zoneddatetime.prototype.round
+description: RangeError thrown when smallestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_123_987_500n, "UTC");
+const badValues = [
+ "era",
+ "eraYear",
+ "year",
+ "month",
+ "week",
+ "millisecond\0",
+ "mill\u0131second",
+ "SECOND",
+ "eras",
+ "eraYears",
+ "years",
+ "months",
+ "weeks",
+ "milliseconds\0",
+ "mill\u0131seconds",
+ "SECONDS",
+ "other string",
+];
+for (const smallestUnit of badValues) {
+ assert.throws(RangeError, () => datetime.round(smallestUnit),
+ `"${smallestUnit}" is not a valid value for smallest unit`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/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/ZonedDateTime/prototype/round/smallest-unit-day-daylength-too-large.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/smallest-unit-day-daylength-too-large.js
new file mode 100644
index 0000000000..0a1e6a1d4d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/smallest-unit-day-daylength-too-large.js
@@ -0,0 +1,87 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.round
+description: >
+ Round smallestUnit "day" with very large or very small divisor (dayLengthNs).
+info: |
+ Temporal.ZonedDateTime.prototype.round ( roundTo )
+ ...
+ 18. Let dayLengthNs be ℝ(endNs - startNs).
+ 19. If dayLengthNs ≤ 0, then
+ a. Throw a RangeError exception.
+ 20. Let roundResult be ! RoundISODateTime(temporalDateTime.[[ISOYear]],
+ temporalDateTime.[[ISOMonth]], temporalDateTime.[[ISODay]], temporalDateTime.[[ISOHour]],
+ temporalDateTime.[[ISOMinute]], temporalDateTime.[[ISOSecond]],
+ temporalDateTime.[[ISOMillisecond]], temporalDateTime.[[ISOMicrosecond]],
+ temporalDateTime.[[ISONanosecond]], roundingIncrement, smallestUnit, roundingMode,
+ dayLengthNs).
+ ...
+
+ RoundISODateTime ( year, month, day, hour, minute, second, millisecond, microsecond, nanosecond,
+ increment, unit, roundingMode [ , dayLength ] )
+ ...
+ 4. Let roundedTime be ! RoundTime(hour, minute, second, millisecond, microsecond, nanosecond,
+ increment, unit, roundingMode, dayLength).
+ ...
+
+ RoundTime ( hour, minute, second, millisecond, microsecond, nanosecond, increment, unit,
+ roundingMode [ , dayLengthNs ] )
+ ...
+ 4. If unit is "day", then
+ ...
+ b. Let quantity be (((((hour × 60 + minute) × 60 + second) × 1000 + millisecond) × 1000 +
+ microsecond) × 1000 + nanosecond) / dayLengthNs.
+ ...
+features: [Temporal]
+---*/
+
+class TimeZone extends Temporal.TimeZone {
+ #count = 0;
+ #nanoseconds;
+
+ constructor(nanoseconds) {
+ super("UTC");
+ this.#nanoseconds = nanoseconds;
+ }
+ getPossibleInstantsFor(dateTime) {
+ if (++this.#count === 2) {
+ return [new Temporal.Instant(this.#nanoseconds)];
+ }
+ return super.getPossibleInstantsFor(dateTime);
+ }
+}
+
+const maxInstant = 86_40000_00000_00000_00000n;
+const minInstant = -86_40000_00000_00000_00000n;
+const oneDay = 24n * 60n * 60n * 1000n * 1000n * 1000n
+
+// Divisor too large.
+{
+ let tz = new TimeZone(maxInstant);
+ let zoned = new Temporal.ZonedDateTime(0n, tz);
+ let result = zoned.round({ smallestUnit: "days" });
+ assert(zoned.equals(result));
+}
+{
+ let tz = new TimeZone(maxInstant);
+ let zoned = new Temporal.ZonedDateTime(minInstant, tz);
+ let result = zoned.round({ smallestUnit: "days" });
+ assert(zoned.equals(result));
+}
+
+// Divisor too small.
+{
+ let tz = new TimeZone(minInstant);
+ let zoned = new Temporal.ZonedDateTime(0n, tz);
+ assert.throws(RangeError, () => zoned.round({ smallestUnit: "days" }));
+}
+{
+ let tz = new TimeZone(minInstant);
+ let zoned = new Temporal.ZonedDateTime(maxInstant - oneDay, tz);
+ assert.throws(RangeError, () => zoned.round({ smallestUnit: "days" }));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/smallest-unit-day-daylength-zero-or-negative.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/smallest-unit-day-daylength-zero-or-negative.js
new file mode 100644
index 0000000000..c7904c2e93
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/smallest-unit-day-daylength-zero-or-negative.js
@@ -0,0 +1,69 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.round
+description: >
+ Round smallestUnit "day" with zero or negative day length.
+info: |
+ Temporal.ZonedDateTime.prototype.round ( roundTo )
+ ...
+ 18. Let dayLengthNs be ℝ(endNs - startNs).
+ 19. If dayLengthNs ≤ 0, then
+ a. Throw a RangeError exception.
+ 20. Let roundResult be ! RoundISODateTime(temporalDateTime.[[ISOYear]],
+ temporalDateTime.[[ISOMonth]], temporalDateTime.[[ISODay]], temporalDateTime.[[ISOHour]],
+ temporalDateTime.[[ISOMinute]], temporalDateTime.[[ISOSecond]],
+ temporalDateTime.[[ISOMillisecond]], temporalDateTime.[[ISOMicrosecond]],
+ temporalDateTime.[[ISONanosecond]], roundingIncrement, smallestUnit, roundingMode,
+ dayLengthNs).
+ ...
+
+ RoundISODateTime ( year, month, day, hour, minute, second, millisecond, microsecond, nanosecond,
+ increment, unit, roundingMode [ , dayLength ] )
+ ...
+ 4. Let roundedTime be ! RoundTime(hour, minute, second, millisecond, microsecond, nanosecond,
+ increment, unit, roundingMode, dayLength).
+ ...
+
+ RoundTime ( hour, minute, second, millisecond, microsecond, nanosecond, increment, unit,
+ roundingMode [ , dayLengthNs ] )
+ ...
+ 4. If unit is "day", then
+ ...
+ b. Let quantity be (((((hour × 60 + minute) × 60 + second) × 1000 + millisecond) × 1000 +
+ microsecond) × 1000 + nanosecond) / dayLengthNs.
+ ...
+features: [Temporal]
+---*/
+
+class TimeZone extends Temporal.TimeZone {
+ #count = 0;
+ #nanoseconds;
+
+ constructor(nanoseconds) {
+ super("UTC");
+ this.#nanoseconds = nanoseconds;
+ }
+ getPossibleInstantsFor(dateTime) {
+ if (++this.#count === 2) {
+ return [new Temporal.Instant(this.#nanoseconds)];
+ }
+ return super.getPossibleInstantsFor(dateTime);
+ }
+}
+
+{
+ let tz = new TimeZone(0n);
+ let zoned = new Temporal.ZonedDateTime(0n, tz);
+ assert.throws(RangeError, () => zoned.round({ smallestUnit: "days" }));
+}
+
+{
+ let tz = new TimeZone(-1n);
+ let zoned = new Temporal.ZonedDateTime(0n, tz);
+ assert.throws(RangeError, () => zoned.round({ smallestUnit: "days" }));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/smallest-unit-day-rounding-modes.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/smallest-unit-day-rounding-modes.js
new file mode 100644
index 0000000000..f694712457
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/smallest-unit-day-rounding-modes.js
@@ -0,0 +1,97 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.round
+description: >
+ Round smallestUnit "day" with various rounding modes.
+info: |
+ Temporal.ZonedDateTime.prototype.round ( roundTo )
+ ...
+ 18. Let dayLengthNs be ℝ(endNs - startNs).
+ ...
+ 20. Let roundResult be ! RoundISODateTime(temporalDateTime.[[ISOYear]],
+ temporalDateTime.[[ISOMonth]], temporalDateTime.[[ISODay]], temporalDateTime.[[ISOHour]],
+ temporalDateTime.[[ISOMinute]], temporalDateTime.[[ISOSecond]],
+ temporalDateTime.[[ISOMillisecond]], temporalDateTime.[[ISOMicrosecond]],
+ temporalDateTime.[[ISONanosecond]], roundingIncrement, smallestUnit, roundingMode,
+ dayLengthNs).
+ ...
+
+ RoundISODateTime ( year, month, day, hour, minute, second, millisecond, microsecond, nanosecond,
+ increment, unit, roundingMode [ , dayLength ] )
+ ...
+ 4. Let roundedTime be ! RoundTime(hour, minute, second, millisecond, microsecond, nanosecond,
+ increment, unit, roundingMode, dayLength).
+ ...
+
+ RoundTime ( hour, minute, second, millisecond, microsecond, nanosecond, increment, unit,
+ roundingMode [ , dayLengthNs ] )
+ ...
+ 4. If unit is "day", then
+ ...
+ b. Let quantity be (((((hour × 60 + minute) × 60 + second) × 1000 + millisecond) × 1000 +
+ microsecond) × 1000 + nanosecond) / dayLengthNs.
+ ...
+features: [Temporal]
+---*/
+
+class TimeZone extends Temporal.TimeZone {
+ #count = 0;
+ #nanoseconds;
+
+ constructor(nanoseconds) {
+ super("UTC");
+ this.#nanoseconds = nanoseconds;
+ }
+ getPossibleInstantsFor(dateTime) {
+ if (++this.#count === 2) {
+ return [new Temporal.Instant(this.#nanoseconds)];
+ }
+ return super.getPossibleInstantsFor(dateTime);
+ }
+}
+
+function test(epochNanoseconds, tomorrowEpochNanoseconds, testCases) {
+ for (let [roundingMode, expected] of Object.entries(testCases)) {
+ let timeZone = new TimeZone(tomorrowEpochNanoseconds);
+ let zoned = new Temporal.ZonedDateTime(epochNanoseconds, timeZone);
+ let result = zoned.round({ smallestUnit: "days", roundingMode });
+ assert.sameValue(result.epochNanoseconds, expected);
+ }
+}
+
+const oneDay = 24n * 60n * 60n * 1000n * 1000n * 1000n;
+
+// Test positive divisor (dayLengthNs).
+test(3n, 10n, {
+ ceil: oneDay,
+ floor: 0n,
+ trunc: 0n,
+ halfExpand: 0n,
+});
+
+test(-3n, 10n, {
+ ceil: 0n,
+ floor: -oneDay,
+ trunc: -oneDay,
+ halfExpand: 0n,
+});
+
+test(-3n, -10n, {
+ ceil: oneDay,
+ floor: 0n,
+ trunc: 0n,
+ halfExpand: 0n,
+});
+
+// Test values at int64 boundaries.
+test(3n, /*INT64_MAX=*/ 9223372036854775807n, {
+ ceil: oneDay,
+ floor: 0n,
+ trunc: 0n,
+ halfExpand: 0n,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/smallestunit-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/smallestunit-invalid-string.js
new file mode 100644
index 0000000000..40c987eb81
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/smallestunit-invalid-string.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.zoneddatetime.prototype.round
+description: RangeError thrown when smallestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_123_987_500n, "UTC");
+const badValues = [
+ "era",
+ "eraYear",
+ "year",
+ "month",
+ "week",
+ "millisecond\0",
+ "mill\u0131second",
+ "SECOND",
+ "eras",
+ "eraYears",
+ "years",
+ "months",
+ "weeks",
+ "milliseconds\0",
+ "mill\u0131seconds",
+ "SECONDS",
+ "other string",
+];
+for (const smallestUnit of badValues) {
+ assert.throws(RangeError, () => datetime.round({ smallestUnit }),
+ `"${smallestUnit}" is not a valid value for smallest unit`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/smallestunit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/smallestunit-plurals-accepted.js
new file mode 100644
index 0000000000..d131690241
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/smallestunit-plurals-accepted.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.zoneddatetime.prototype.round
+description: Plural units are accepted as well for the smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal, arrow-function]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+const validUnits = [
+ "day",
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+];
+TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => datetime.round({ smallestUnit }), validUnits);
+TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => datetime.round(smallestUnit), validUnits);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/smallestunit-string-shorthand.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/smallestunit-string-shorthand.js
new file mode 100644
index 0000000000..0a1f554087
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/smallestunit-string-shorthand.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.zoneddatetime.prototype.round
+description: String as first argument is equivalent to options bag with smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal, arrow-function]
+---*/
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+const validUnits = [
+ "day",
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+];
+validUnits.forEach((smallestUnit) => {
+ const full = instance.round({ smallestUnit });
+ const shorthand = instance.round(smallestUnit);
+ TemporalHelpers.assertZonedDateTimesEqual(shorthand, full, `"${smallestUnit}" as first argument to round is equivalent to options bag`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/smallestunit-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/smallestunit-wrong-type.js
new file mode 100644
index 0000000000..f1f43da8d7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/smallestunit-wrong-type.js
@@ -0,0 +1,18 @@
+// |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.zoneddatetime.prototype.round
+description: Type conversions for smallestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_123_987_500n, "UTC");
+TemporalHelpers.checkStringOptionWrongType("smallestUnit", "microsecond",
+ (smallestUnit) => datetime.round({ smallestUnit }),
+ (result, descr) => assert.sameValue(result.epochNanoseconds, 1_000_000_000_123_988_000n, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/subclassing-ignored.js
new file mode 100644
index 0000000000..f329bf25ce
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/subclassing-ignored.js
@@ -0,0 +1,31 @@
+// |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.zoneddatime.prototype.round
+description: Objects of a subclass are never created as return values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.ZonedDateTime,
+ [10n, "UTC"],
+ "round",
+ [{ smallestUnit: 'second', roundingMode: 'ceil' }],
+ (result) => {
+ assert.sameValue(result.epochNanoseconds, 1_000_000_000n, "epochNanoseconds result");
+ assert.sameValue(result.year, 1970, "year result");
+ assert.sameValue(result.month, 1, "month result");
+ assert.sameValue(result.day, 1, "day result");
+ assert.sameValue(result.hour, 0, "hour result");
+ assert.sameValue(result.minute, 0, "minute result");
+ assert.sameValue(result.second, 1, "second result");
+ assert.sameValue(result.millisecond, 0, "millisecond result");
+ assert.sameValue(result.microsecond, 0, "microsecond result");
+ assert.sameValue(result.nanosecond, 0, "nanosecond result");
+ },
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..e2f42ae0c2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,18 @@
+// |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.zoneddatetime.prototype.round
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.round({ smallestUnit: "second" }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..7f56dc553d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/timezone-getoffsetnanosecondsfor-not-callable.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.zoneddatetime.prototype.round
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => datetime.round({ smallestUnit: "second" }),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..feabc87f2c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,18 @@
+// |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.zoneddatetime.prototype.round
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.round({ smallestUnit: "second" }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..f3f3a626e7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/timezone-getoffsetnanosecondsfor-wrong-type.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.zoneddatetime.prototype.round
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.round({ smallestUnit: "second" }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/timezone-getpossibleinstantsfor-iterable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/timezone-getpossibleinstantsfor-iterable.js
new file mode 100644
index 0000000000..922b114e0b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/timezone-getpossibleinstantsfor-iterable.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.zoneddatetime.prototype.round
+description: An iterable returned from timeZone.getPossibleInstantsFor is consumed after each call
+info: |
+ sec-temporal.zoneddatetime.prototype.round steps 14, 16, and 20:
+ 14. Let _instantStart_ be ? BuiltinTimeZoneGetInstantFor(_timeZone_, _dtStart_, *"compatible"*).
+ 16. Let _endNs_ be ? AddZonedDateTime(_startNs_, _timeZone_, _zonedDateTime_.[[Calendar]], 0, 0, 0, 1, 0, 0, 0, 0, 0, 0).
+ 20. Let _epochNanoseconds_ be ? InterpretISODateTimeOffset(_roundResult_.[[Year]], [...], _roundResult_.[[Nanosecond]], _offsetNanoseconds_, _timeZone_, *"compatible"*, *"prefer"*).
+ sec-temporal-addzoneddatetime step 8:
+ 8. Let _intermediateInstant_ be ? BuiltinTimeZoneGetInstantFor(_timeZone_, _intermediateDateTime_, *"compatible"*).
+ sec-temporal-builtintimezonegetinstantfor step 1:
+ 1. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_).
+ sec-temporal-interpretisodatetimeoffset step 7:
+ 7. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_).
+ sec-temporal-getpossibleinstantsfor step 2:
+ 2. Let _list_ be ? IterableToList(_possibleInstants_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "2001-09-09T00:00:00", // called once on midnight of the input datetime
+ "2001-09-10T00:00:00", // called once on the previous value plus one calendar day
+ "2001-09-09T02:00:00", // called once on the rounding result
+];
+
+TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => {
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, timeZone);
+ datetime.round({ smallestUnit: 'hour' });
+}, expected);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/balance-negative-time-units.js
new file mode 100644
index 0000000000..f18e5737db
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/balance-negative-time-units.js
@@ -0,0 +1,37 @@
+// |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.zoneddatetime.prototype.second
+description: Negative time fields are balanced upwards
+info: |
+ sec-temporal-balancetime steps 3–8:
+ 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.
+ 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-get-temporal.zoneddatetime.prototype.second step 6:
+ 6. Let _temporalDateTime_ be ? BuiltinTimeZoneGetPlainDateTimeFor(_timeZone_, _instant_, _calendar_).
+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(1_000_000_001n, tz);
+
+assert.sameValue(datetime.second, 0);
+assert.sameValue(datetime.millisecond, 999);
+assert.sameValue(datetime.microsecond, 999);
+assert.sameValue(datetime.nanosecond, 999);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/branding.js
new file mode 100644
index 0000000000..3abae4d6b9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/branding.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-get-temporal.zoneddatetime.prototype.second
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const second = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "second").get;
+
+assert.sameValue(typeof second, "function");
+
+assert.throws(TypeError, () => second.call(undefined), "undefined");
+assert.throws(TypeError, () => second.call(null), "null");
+assert.throws(TypeError, () => second.call(true), "true");
+assert.throws(TypeError, () => second.call(""), "empty string");
+assert.throws(TypeError, () => second.call(Symbol()), "symbol");
+assert.throws(TypeError, () => second.call(1), "1");
+assert.throws(TypeError, () => second.call({}), "plain object");
+assert.throws(TypeError, () => second.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime");
+assert.throws(TypeError, () => second.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/builtin-timezone-no-observable-calls.js
new file mode 100644
index 0000000000..7a4d35aaad
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/builtin-timezone-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.zoneddatetime.prototype.second
+description: >
+ Calling the method on an instance constructed with a builtin time zone causes
+ no observable lookups or calls to time zone methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.second;
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/prop-desc.js
new file mode 100644
index 0000000000..c27117caf4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/prop-desc.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-get-temporal.zoneddatetime.prototype.second
+description: The "second" property of Temporal.ZonedDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "second");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..360c2f8036
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,18 @@
+// |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-get-temporal.zoneddatetime.prototype.second
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.second);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..39c961233c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/timezone-getoffsetnanosecondsfor-not-callable.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-get-temporal.zoneddatetime.prototype.second
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => datetime.second,
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..b75712751a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,18 @@
+// |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-get-temporal.zoneddatetime.prototype.second
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.second);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..04885bc85d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/timezone-getoffsetnanosecondsfor-wrong-type.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-get-temporal.zoneddatetime.prototype.second
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.second);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..7c211ed7b0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-builtin-calendar-no-array-iteration.js
@@ -0,0 +1,25 @@
+// |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.zoneddatetime.prototype.since
+description: >
+ Calling the method with a property bag argument with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const timeZone = "UTC";
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+const arg = { year: 2000, month: 5, day: 2, hour: 21, minute: 43, second: 5, timeZone, calendar: "iso8601" };
+instance.since(arg);
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-ambiguous-wall-clock-time.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-ambiguous-wall-clock-time.js
new file mode 100644
index 0000000000..37a807a693
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-ambiguous-wall-clock-time.js
@@ -0,0 +1,93 @@
+// |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.zoneddatetime.prototype.since
+description: >
+ Correct time zone calls are made when converting a ZonedDateTime-like property
+ bag denoting an ambiguous wall-clock time
+includes: [temporalHelpers.js, compareArray.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+
+const dstTimeZone = TemporalHelpers.springForwardFallBackTimeZone();
+const dstTimeZoneObserver = TemporalHelpers.timeZoneObserver(actual, "timeZone", {
+ getOffsetNanosecondsFor: dstTimeZone.getOffsetNanosecondsFor.bind(dstTimeZone),
+ getPossibleInstantsFor: dstTimeZone.getPossibleInstantsFor.bind(dstTimeZone),
+});
+const calendar = TemporalHelpers.calendarObserver(actual, "calendar");
+
+const timeZone = "UTC";
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+
+let arg = { year: 2000, month: 4, day: 2, hour: 2, minute: 30, timeZone: dstTimeZoneObserver, calendar };
+instance.since(arg);
+
+const expected = [
+ // GetTemporalCalendarSlotValueWithISODefault
+ "has calendar.dateAdd",
+ "has calendar.dateFromFields",
+ "has calendar.dateUntil",
+ "has calendar.day",
+ "has calendar.dayOfWeek",
+ "has calendar.dayOfYear",
+ "has calendar.daysInMonth",
+ "has calendar.daysInWeek",
+ "has calendar.daysInYear",
+ "has calendar.fields",
+ "has calendar.id",
+ "has calendar.inLeapYear",
+ "has calendar.mergeFields",
+ "has calendar.month",
+ "has calendar.monthCode",
+ "has calendar.monthDayFromFields",
+ "has calendar.monthsInYear",
+ "has calendar.weekOfYear",
+ "has calendar.year",
+ "has calendar.yearMonthFromFields",
+ "has calendar.yearOfWeek",
+ // lookup in ToTemporalZonedDateTime
+ "get calendar.dateFromFields",
+ "get calendar.fields",
+ // CalendarFields
+ "call calendar.fields",
+ // ToTemporalTimeZoneSlotValue
+ "has timeZone.getOffsetNanosecondsFor",
+ "has timeZone.getPossibleInstantsFor",
+ "has timeZone.id",
+ // InterpretTemporalDateTimeFields
+ "call calendar.dateFromFields",
+ // lookup in ToTemporalZonedDateTime
+ "get timeZone.getOffsetNanosecondsFor",
+ "get timeZone.getPossibleInstantsFor",
+ // InterpretISODateTimeOffset
+ "call timeZone.getPossibleInstantsFor",
+];
+
+const expectedSpringForward = expected.concat([
+ // DisambiguatePossibleInstants
+ "call timeZone.getOffsetNanosecondsFor",
+ "call timeZone.getOffsetNanosecondsFor",
+ "call timeZone.getPossibleInstantsFor",
+]);
+assert.compareArray(
+ actual.slice(0, expectedSpringForward.length), // ignore operations after ToTemporalZonedDateTime
+ expectedSpringForward,
+ "order of operations converting property bag at skipped wall-clock time"
+);
+actual.splice(0); // clear
+
+arg = { year: 2000, month: 10, day: 29, hour: 1, minute: 30, timeZone: dstTimeZoneObserver, calendar };
+instance.since(arg);
+
+assert.compareArray(
+ actual.slice(0, expected.length), // ignore operations after ToTemporalZonedDateTime
+ expected,
+ "order of operations converting property bag at repeated wall-clock time"
+);
+actual.splice(0); // clear
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-calendar-case-insensitive.js
new file mode 100644
index 0000000000..53d0d01258
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-calendar-case-insensitive.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.zoneddatetime.prototype.since
+description: The calendar name is case-insensitive
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+
+const calendar = "IsO8601";
+
+const arg = { year: 1970, monthCode: "M01", day: 1, timeZone, calendar };
+const result = instance.since(arg);
+TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-calendar-leap-second.js
new file mode 100644
index 0000000000..4722e5630e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-calendar-leap-second.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.zoneddatetime.prototype.since
+description: Leap second is a valid ISO string for a calendar in a property bag
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+
+const calendar = "2016-12-31T23:59:60+00:00[UTC]";
+
+const arg = { year: 1970, monthCode: "M01", day: 1, timeZone, calendar };
+const result = instance.since(arg);
+TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "leap second is a valid ISO string for calendar"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-calendar-number.js
new file mode 100644
index 0000000000..c4f7c29c36
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-calendar-number.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.zoneddatetime.prototype.since
+description: A number as calendar in a property bag is not accepted
+features: [Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+
+const numbers = [
+ 1,
+ 19970327,
+ -19970327,
+ 1234567890,
+];
+
+for (const calendar of numbers) {
+ const arg = { year: 1970, monthCode: "M01", day: 1, timeZone, calendar };
+ assert.throws(
+ TypeError,
+ () => instance.since(arg),
+ "Numbers cannot be used as a calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-calendar-string.js
new file mode 100644
index 0000000000..7fec62dde5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-calendar-string.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.zoneddatetime.prototype.since
+description: A calendar ID is valid input for Calendar
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+
+const calendar = "iso8601";
+
+const arg = { year: 1970, monthCode: "M01", day: 1, timeZone, calendar };
+const result = instance.since(arg);
+TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, `Calendar created from string "${calendar}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-calendar-wrong-type.js
new file mode 100644
index 0000000000..76eabbc22c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-calendar-wrong-type.js
@@ -0,0 +1,46 @@
+// |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.zoneddatetime.prototype.since
+description: >
+ Appropriate error thrown when a calendar property from a property bag cannot
+ be converted to a calendar object or string
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [calendar, description] of primitiveTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ typeof calendar === 'string' ? RangeError : TypeError,
+ () => instance.since(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+ [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields()
+];
+
+for (const [calendar, description] of typeErrorTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ 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/ZonedDateTime/prototype/since/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-calendar-year-zero.js
new file mode 100644
index 0000000000..52a3aee0f5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-calendar-year-zero.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.zoneddatetime.prototype.since
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T17:45",
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+01:00",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.since(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-getpossibleinstantsfor-called-with-iso8601-calendar.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-getpossibleinstantsfor-called-with-iso8601-calendar.js
new file mode 100644
index 0000000000..5aea2202fd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-getpossibleinstantsfor-called-with-iso8601-calendar.js
@@ -0,0 +1,59 @@
+// |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.zoneddatetime.prototype.since
+description: >
+ Time zone's getPossibleInstantsFor is called with a PlainDateTime with the
+ built-in ISO 8601 calendar
+features: [Temporal]
+info: |
+ DisambiguatePossibleInstants:
+ 2. Let _n_ be _possibleInstants_'s length.
+ ...
+ 5. Assert: _n_ = 0.
+ ...
+ 19. If _disambiguation_ is *"earlier"*, then
+ ...
+ c. Let _earlierDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*).
+ d. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _earlierDateTime_).
+ ...
+ 20. Assert: _disambiguation_ is *"compatible"* or *"later"*.
+ ...
+ 23. Let _laterDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*).
+ 24. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _laterDateTime_).
+---*/
+
+class SkippedDateTime extends Temporal.TimeZone {
+ constructor() {
+ super("UTC");
+ this.calls = 0;
+ }
+
+ getPossibleInstantsFor(dateTime) {
+ // Calls occur in pairs. For the first one return no possible instants so
+ // that DisambiguatePossibleInstants will call it again
+ if (this.calls++ % 2 == 0) {
+ return [];
+ }
+
+ assert.sameValue(
+ dateTime.getISOFields().calendar,
+ "iso8601",
+ "getPossibleInstantsFor called with dateTime with built-in ISO 8601 calendar"
+ );
+ return super.getPossibleInstantsFor(dateTime);
+ }
+}
+
+const nonBuiltinISOCalendar = new Temporal.Calendar("iso8601");
+const timeZone = new SkippedDateTime();
+const arg = { year: 2000, month: 5, day: 2, timeZone, calendar: nonBuiltinISOCalendar };
+
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+instance.since(arg);
+
+assert.sameValue(timeZone.calls, 2, "getPossibleInstantsFor should have been called 2 times");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-invalid-offset-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-invalid-offset-string.js
new file mode 100644
index 0000000000..34f3ee71fc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-invalid-offset-string.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.zoneddatetime.prototype.since
+description: Property bag with offset property is rejected if offset is in the wrong format
+features: [Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+
+const badOffsets = [
+ "00:00", // missing sign
+ "+0", // too short
+ "-000:00", // too long
+ 0, // must be a string
+ null, // must be a string
+ true, // must be a string
+ 1000n, // must be a string
+];
+badOffsets.forEach((offset) => {
+ const arg = { year: 2021, month: 10, day: 28, offset, timeZone };
+ assert.throws(
+ typeof(offset) === 'string' ? RangeError : TypeError,
+ () => instance.since(arg),
+ `"${offset} is not a valid offset string`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-offset-not-agreeing-with-timezone.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-offset-not-agreeing-with-timezone.js
new file mode 100644
index 0000000000..3a66dec910
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-offset-not-agreeing-with-timezone.js
@@ -0,0 +1,17 @@
+// |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.zoneddatetime.prototype.since
+description: Property bag with offset property is rejected if offset does not agree with time zone
+features: [Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("+01:00");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+
+const properties = { year: 2021, month: 10, day: 28, offset: "-07:00", timeZone };
+assert.throws(RangeError, () => instance.since(properties), "offset property not matching time zone is rejected");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..b212528041
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-getoffsetnanosecondsfor-non-integer.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.zoneddatetime.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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+ const properties = { year: 2004, month: 11, day: 9, hour: 11, minute: 33, second: 20, timeZone };
+ timeZone.getPossibleInstantsFor = function () {
+ return [];
+ };
+ assert.throws(RangeError, () => datetime.since(properties, { largestUnit: "days" }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..d44a948d58
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-getoffsetnanosecondsfor-not-callable.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.zoneddatetime.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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+ const properties = { year: 2004, month: 11, day: 9, hour: 11, minute: 33, second: 20, timeZone };
+ timeZone.getPossibleInstantsFor = function () {
+ return [];
+ };
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => datetime.since(properties, { largestUnit: "days" }),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..7404bcb2cf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.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.zoneddatetime.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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+ const properties = { year: 2004, month: 11, day: 9, hour: 11, minute: 33, second: 20, timeZone };
+ timeZone.getPossibleInstantsFor = function () {
+ return [];
+ };
+ assert.throws(RangeError, () => datetime.since(properties, { largestUnit: "days" }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..e9c6d6a1b1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,31 @@
+// |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.zoneddatetime.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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+ const properties = { year: 2004, month: 11, day: 9, hour: 11, minute: 33, second: 20, timeZone };
+ timeZone.getPossibleInstantsFor = function () {
+ return [];
+ };
+ assert.throws(TypeError, () => datetime.since(properties, { largestUnit: "days" }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-id-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-id-wrong-type.js
new file mode 100644
index 0000000000..2aca2d9cd6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-id-wrong-type.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Justin Grant. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.since
+description: TypeError thrown if time zone reports an id that is not a String
+features: [Temporal]
+---*/
+
+class CustomTimeZone extends Temporal.TimeZone {
+ constructor(id) {
+ super("UTC");
+ this._id = id;
+ }
+ get id() {
+ return this._id;
+ }
+}
+
+[
+ undefined,
+ null,
+ true,
+ -1000,
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ {
+ valueOf() {
+ return 3600_000_000_000;
+ }
+ }
+].forEach((wrongId) => {
+ const timeZone = new CustomTimeZone(wrongId);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+ const properties = { year: 2004, month: 11, day: 9, hour: 11, minute: 33, second: 20, timeZone };
+ assert.throws(TypeError, () => datetime.since(properties, { largestUnit: "days" }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-string-datetime.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-string-datetime.js
new file mode 100644
index 0000000000..bfa51c52a5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-string-datetime.js
@@ -0,0 +1,69 @@
+// |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.zoneddatetime.prototype.since
+description: Conversion of ISO date-time strings to Temporal.TimeZone instances
+features: [Temporal]
+---*/
+
+let expectedTimeZone = "UTC";
+const instance1 = new Temporal.ZonedDateTime(0n, expectedTimeZone);
+
+let timeZone = "2021-08-19T17:30";
+assert.throws(RangeError, () => instance1.since({ year: 2020, month: 5, day: 2, timeZone }), "bare date-time string is not a time zone");
+
+[
+ "2021-08-19T17:30-07:00:01",
+ "2021-08-19T17:30-07:00:00",
+ "2021-08-19T17:30-07:00:00.1",
+ "2021-08-19T17:30-07:00:00.0",
+ "2021-08-19T17:30-07:00:00.01",
+ "2021-08-19T17:30-07:00:00.00",
+ "2021-08-19T17:30-07:00:00.001",
+ "2021-08-19T17:30-07:00:00.000",
+ "2021-08-19T17:30-07:00:00.0001",
+ "2021-08-19T17:30-07:00:00.0000",
+ "2021-08-19T17:30-07:00:00.00001",
+ "2021-08-19T17:30-07:00:00.00000",
+ "2021-08-19T17:30-07:00:00.000001",
+ "2021-08-19T17:30-07:00:00.000000",
+ "2021-08-19T17:30-07:00:00.0000001",
+ "2021-08-19T17:30-07:00:00.0000000",
+ "2021-08-19T17:30-07:00:00.00000001",
+ "2021-08-19T17:30-07:00:00.00000000",
+ "2021-08-19T17:30-07:00:00.000000001",
+ "2021-08-19T17:30-07:00:00.000000000",
+].forEach((timeZone) => {
+ assert.throws(
+ RangeError,
+ () => instance1.since({ year: 2020, month: 5, day: 2, timeZone }),
+ `ISO string ${timeZone} with a sub-minute offset is not a valid time zone`
+ );
+});
+
+// The following are all valid strings so should not throw. They should produce
+// expectedTimeZone, so additionally the operation will not throw due to the
+// time zones being different on the receiver and the argument.
+
+timeZone = "2021-08-19T17:30Z";
+instance1.since({ year: 2020, month: 5, day: 2, timeZone });
+
+expectedTimeZone = "-07:00";
+const instance2 = new Temporal.ZonedDateTime(0n, expectedTimeZone);
+timeZone = "2021-08-19T17:30-07:00";
+instance2.since({ year: 2020, month: 5, day: 2, timeZone });
+
+expectedTimeZone = "UTC";
+const instance3 = new Temporal.ZonedDateTime(0n, expectedTimeZone);
+timeZone = "2021-08-19T17:30[UTC]";
+instance3.since({ year: 2020, month: 5, day: 2, timeZone });
+
+timeZone = "2021-08-19T17:30Z[UTC]";
+instance3.since({ year: 2020, month: 5, day: 2, timeZone });
+
+timeZone = "2021-08-19T17:30-07:00[UTC]";
+instance3.since({ year: 2020, month: 5, day: 2, timeZone });
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-string-leap-second.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-string-leap-second.js
new file mode 100644
index 0000000000..fe5907ef88
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-string-leap-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.zoneddatetime.prototype.since
+description: Leap second is a valid ISO string for TimeZone
+features: [Temporal]
+---*/
+
+const expectedTimeZone = "UTC";
+const instance = new Temporal.ZonedDateTime(0n, expectedTimeZone);
+let timeZone = "2016-12-31T23:59:60+00:00[UTC]";
+
+// This operation should produce expectedTimeZone, so the following operation
+// should not throw due to the time zones being different on the receiver and
+// the argument.
+
+instance.since({ year: 2020, month: 5, day: 2, timeZone });
+
+timeZone = "2021-08-19T17:30:45.123456789+23:59[+23:59:60]";
+assert.throws(RangeError, () => instance.since({ year: 2020, month: 5, day: 2, timeZone }), "leap second in time zone name not valid");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-string-multiple-offsets.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-string-multiple-offsets.js
new file mode 100644
index 0000000000..048d907230
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-string-multiple-offsets.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.zoneddatetime.prototype.since
+description: Time zone parsing from ISO strings uses the bracketed offset, not the ISO string offset
+features: [Temporal]
+---*/
+
+const expectedTimeZone = "+01:46";
+const instance = new Temporal.ZonedDateTime(0n, expectedTimeZone);
+const timeZone = "2021-08-19T17:30:45.123456789-12:12[+01:46]";
+
+// This operation should produce expectedTimeZone, so the following operation
+// should not throw due to the time zones being different on the receiver and
+// the argument.
+
+instance.since({ year: 2020, month: 5, day: 2, timeZone });
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-string-year-zero.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-string-year-zero.js
new file mode 100644
index 0000000000..a6ab714edb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-string-year-zero.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.zoneddatetime.prototype.since
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+const instance = new Temporal.ZonedDateTime(0n, new Temporal.TimeZone("UTC"));
+invalidStrings.forEach((timeZone) => {
+ assert.throws(
+ RangeError,
+ () => instance.since({ year: 2020, month: 5, day: 2, timeZone }),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-string.js
new file mode 100644
index 0000000000..6e2b02f543
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-string.js
@@ -0,0 +1,38 @@
+// |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.zoneddatetime.prototype.since
+description: Time zone IDs are valid input for a time zone
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getPossibleInstantsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getPossibleInstantsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be looked up");
+ },
+});
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+
+const instance1 = new Temporal.ZonedDateTime(0n, new Temporal.TimeZone("UTC"));
+assert(instance1.since({ year: 1970, month: 1, day: 1, timeZone: "UTC" }).blank, "Time zone created from string 'UTC'");
+
+const instance2 = new Temporal.ZonedDateTime(0n, new Temporal.TimeZone("-01:30"));
+assert(instance2.since({ year: 1969, month: 12, day: 31, hour: 22, minute: 30, timeZone: "-01:30" }).blank, "Time zone created from string '-01:30'");
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", getPossibleInstantsForOriginal);
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-wrong-type.js
new file mode 100644
index 0000000000..e7ee5b19db
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-wrong-type.js
@@ -0,0 +1,42 @@
+// |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.zoneddatetime.prototype.since
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or object for TimeZone
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(0n, new Temporal.TimeZone("UTC"));
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [19761118, "number that would convert to a valid ISO string in other contexts"],
+ [1n, "bigint"],
+];
+
+for (const [timeZone, description] of primitiveTests) {
+ assert.throws(
+ typeof timeZone === 'string' ? RangeError : TypeError,
+ () => instance.since({ year: 2020, month: 5, day: 2, timeZone }),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "object not implementing time zone protocol"],
+ [new Temporal.Calendar("iso8601"), "calendar instance"],
+];
+
+for (const [timeZone, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.since({ year: 2020, month: 5, day: 2, timeZone }), `${description} is not a valid object and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..49ef648896
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-string-calendar-annotation.js
@@ -0,0 +1,31 @@
+// |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.zoneddatetime.prototype.since
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["1970-01-01T00:00[UTC][u-ca=iso8601]", "without !"],
+ ["1970-01-01T00:00[UTC][!u-ca=iso8601]", "with !"],
+ ["1970-01-01T00:00[UTC][u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+
+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/ZonedDateTime/prototype/since/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..f18c475a84
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-string-critical-unknown-annotation.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.zoneddatetime.prototype.since
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01T00:00[UTC][!foo=bar]",
+ "1970-01-01T00:00[UTC][!foo=bar][u-ca=iso8601]",
+ "1970-01-01T00:00[UTC][u-ca=iso8601][!foo=bar]",
+ "1970-01-01T00:00[foo=bar][!_foo-bar0=Dont-Ignore-This-99999999999]",
+];
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+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/ZonedDateTime/prototype/since/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..7830684d15
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-string-date-with-utc-offset.js
@@ -0,0 +1,47 @@
+// |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.zoneddatetime.prototype.since
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+
+const validStrings = [
+ "1970-01-01T00Z[UTC]",
+ "1970-01-01T00Z[!UTC]",
+ "1970-01-01T00+00:00[UTC]",
+ "1970-01-01T00+00:00[!UTC]",
+];
+
+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 ZonedDateTime`
+ );
+}
+
+const invalidStrings = [
+ "2022-09-15Z[UTC]",
+ "2022-09-15Z[Europe/Vienna]",
+ "2022-09-15+00:00[UTC]",
+ "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 ZonedDateTime`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..915733189c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-string-multiple-calendar.js
@@ -0,0 +1,28 @@
+// |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.zoneddatetime.prototype.since
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01T00:00[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00[UTC][!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01T00:00[UTC][u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+];
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+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/ZonedDateTime/prototype/since/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..06ea7a0ed9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-string-multiple-time-zone.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.zoneddatetime.prototype.since
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "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 timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+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/ZonedDateTime/prototype/since/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-string-time-separators.js
new file mode 100644
index 0000000000..c2d3f6cc44
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-string-time-separators.js
@@ -0,0 +1,31 @@
+// |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.zoneddatetime.prototype.since
+description: Time separator in string argument can vary
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["1970-01-01T00:00+00:00[UTC]", "uppercase T"],
+ ["1970-01-01t00:00+00:00[UTC]", "lowercase T"],
+ ["1970-01-01 00:00+00:00[UTC]", "space between date and time"],
+];
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+
+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/ZonedDateTime/prototype/since/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..8dd7c50c9a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-string-time-zone-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.zoneddatetime.prototype.since
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["1970-01-01T00:00[UTC]", "named, with no offset"],
+ ["1970-01-01T00:00[!UTC]", "named, with ! and no offset"],
+ ["1970-01-01T00:00[+00:00]", "numeric, with no offset"],
+ ["1970-01-01T00:00[!+00:00]", "numeric, with ! and no offset"],
+ ["1970-01-01T00:00Z[UTC]", "named, with Z"],
+ ["1970-01-01T00:00Z[!UTC]", "named, with Z and !"],
+ ["1970-01-01T00:00Z[+00:00]", "numeric, with Z"],
+ ["1970-01-01T00:00Z[!+00:00]", "numeric, with Z and !"],
+ ["1970-01-01T00:00+00:00[UTC]", "named, with offset"],
+ ["1970-01-01T00:00+00:00[!UTC]", "named, with offset and !"],
+ ["1970-01-01T00:00+00:00[+00:00]", "numeric, with offset"],
+ ["1970-01-01T00:00+00:00[!+00:00]", "numeric, with offset and !"],
+];
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+
+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/ZonedDateTime/prototype/since/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..c1efd988fa
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-string-unknown-annotation.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.zoneddatetime.prototype.since
+description: Various forms of unknown annotation
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["1970-01-01T00:00[UTC][foo=bar]", "with time zone"],
+ ["1970-01-01T00:00[UTC][foo=bar][u-ca=iso8601]", "before calendar"],
+ ["1970-01-01T00:00[UTC][u-ca=iso8601][foo=bar]", "after calendar"],
+ ["1970-01-01T00:00[UTC][foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+
+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/ZonedDateTime/prototype/since/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-wrong-type.js
new file mode 100644
index 0000000000..1d3b610010
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-wrong-type.js
@@ -0,0 +1,45 @@
+// |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.zoneddatetime.prototype.since
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for ZonedDateTime
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+
+const primitiveTests = [
+ [undefined, "undefined"],
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [19761118, "number that would convert to a valid ISO string in other contexts"],
+ [1n, "bigint"],
+];
+
+for (const [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.ZonedDateTime, "Temporal.ZonedDateTime, object"],
+ [Temporal.ZonedDateTime.prototype, "Temporal.ZonedDateTime.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/ZonedDateTime/prototype/since/balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/balance-negative-time-units.js
new file mode 100644
index 0000000000..802cdbf92d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/balance-negative-time-units.js
@@ -0,0 +1,57 @@
+// |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.zoneddatetime.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-differenceisodatetime step 2:
+ 2. Let _timeDifference_ be ? DifferenceTime(_h1_, _min1_, _s1_, _ms1_, _mus1_, _ns1_, _h2_, _min2_, _s2_, _ms2_, _mus2_, _ns2_).
+ sec-temporal-differencezoneddatetime step 7:
+ 7. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _calendar_, _largestUnit_, _options_).
+ sec-temporal.zoneddatetime.prototype.since step 16:
+ 16. Let _difference_ be ? DifferenceZonedDateTime(_zonedDateTime_.[[Nanoseconds]], _other_.[[Nanoseconds]], _zonedDateTime_.[[TimeZone]], _zonedDateTime_.[[Calendar]], _largestUnit_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const datetime = new Temporal.ZonedDateTime(830998861_001_001_001n, timeZone);
+const options = { largestUnit: "days" };
+
+const result1 = datetime.since(new Temporal.ZonedDateTime(830995200_000_000_002n, timeZone), options);
+TemporalHelpers.assertDuration(result1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 999, "nanoseconds balance");
+
+const result2 = datetime.since(new Temporal.ZonedDateTime(830995200_000_002_000n, timeZone), options);
+TemporalHelpers.assertDuration(result2, 0, 0, 0, 0, 1, 1, 1, 0, 999, 1, "microseconds balance");
+
+const result3 = datetime.since(new Temporal.ZonedDateTime(830995200_002_000_000n, timeZone), options);
+TemporalHelpers.assertDuration(result3, 0, 0, 0, 0, 1, 1, 0, 999, 1, 1, "milliseconds balance");
+
+const result4 = datetime.since(new Temporal.ZonedDateTime(830995202_000_000_000n, timeZone), options);
+TemporalHelpers.assertDuration(result4, 0, 0, 0, 0, 1, 0, 59, 1, 1, 1, "seconds balance");
+
+const result5 = datetime.since(new Temporal.ZonedDateTime(830995320_000_000_000n, timeZone), options);
+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 = datetime.since(new Temporal.ZonedDateTime(831002400_000_000_000n, timeZone), options);
+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/ZonedDateTime/prototype/since/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/branding.js
new file mode 100644
index 0000000000..129c2be910
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/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.zoneddatetime.prototype.since
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const since = Temporal.ZonedDateTime.prototype.since;
+
+assert.sameValue(typeof since, "function");
+
+const args = [new Temporal.ZonedDateTime(123456n, new Temporal.TimeZone("UTC"))];
+
+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.ZonedDateTime, args), "Temporal.ZonedDateTime");
+assert.throws(TypeError, () => since.apply(Temporal.ZonedDateTime.prototype, args), "Temporal.ZonedDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..60a4505527
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.zoneddatetime.prototype.since
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dateUntilOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dateUntil");
+Object.defineProperty(Temporal.Calendar.prototype, "dateUntil", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("dateUntil should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.since(new Temporal.ZonedDateTime(0n, "UTC"));
+
+Object.defineProperty(Temporal.Calendar.prototype, "dateUntil", dateUntilOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/builtin-timezone-no-observable-calls.js
new file mode 100644
index 0000000000..f7e23e64c0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/builtin-timezone-no-observable-calls.js
@@ -0,0 +1,37 @@
+// |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.zoneddatetime.prototype.since
+description: >
+ Calling the method on an instance constructed with a builtin time zone causes
+ no observable lookups or calls to time zone methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+const getPossibleInstantsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getPossibleInstantsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.since(new Temporal.ZonedDateTime(0n, "UTC"));
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", getPossibleInstantsForOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/builtin.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/builtin.js
new file mode 100644
index 0000000000..a3958bbb48
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/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.zoneddatetime.prototype.since
+description: >
+ Tests that Temporal.ZonedDateTime.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.ZonedDateTime.prototype.since),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.since),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.since),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.ZonedDateTime.prototype.since.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/calendar-dateadd-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/calendar-dateadd-called-with-options-undefined.js
new file mode 100644
index 0000000000..7a5367bf29
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/calendar-dateadd-called-with-options-undefined.js
@@ -0,0 +1,40 @@
+// |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.zoneddatetime.prototype.since
+description: >
+ BuiltinTimeZoneGetInstantFor calls Calendar.dateAdd with undefined as the
+ options value
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarDateAddUndefinedOptions();
+const timeZone = TemporalHelpers.oneShiftTimeZone(new Temporal.Instant(0n), 3600e9);
+const earlier = new Temporal.ZonedDateTime(0n, timeZone, calendar);
+
+// Basic difference with largestUnit larger than days.
+// The call comes from this path:
+// ZonedDateTime.since() -> DifferenceZonedDateTime -> AddZonedDateTime -> calendar.dateAdd()
+
+const later1 = new Temporal.ZonedDateTime(1_213_200_000_000_000n, timeZone, calendar);
+later1.since(earlier, { largestUnit: "weeks" });
+assert.sameValue(calendar.dateAddCallCount, 1, "basic difference with largestUnit >days");
+
+// Difference with rounding, with smallestUnit a calendar unit.
+// The calls come from these paths:
+// ZonedDateTime.since() ->
+// DifferenceZonedDateTime -> AddZonedDateTime -> calendar.dateAdd()
+// RoundDuration ->
+// MoveRelativeZonedDateTime -> AddZonedDateTime -> calendar.dateAdd()
+// MoveRelativeDate -> calendar.dateAdd()
+// BalanceDurationRelative -> MoveRelativeDate -> calendar.dateAdd()
+
+calendar.dateAddCallCount = 0;
+
+later1.since(earlier, { smallestUnit: "weeks" });
+assert.sameValue(calendar.dateAddCallCount, 4, "rounding difference with calendar smallestUnit");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/calendar-datefromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..6508201c05
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/calendar-datefromfields-called-with-null-prototype-fields.js
@@ -0,0 +1,20 @@
+// |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.zoneddatetime.prototype.since
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+const arg = { year: 2000, month: 5, day: 2, timeZone, calendar };
+instance.since(arg);
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/calendar-dateuntil-called-with-copy-of-options.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/calendar-dateuntil-called-with-copy-of-options.js
new file mode 100644
index 0000000000..91db3a3298
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/calendar-dateuntil-called-with-copy-of-options.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.zoneddatetime.prototype.since
+description: The dateUntil() method on the calendar is called with a copy of the options bag
+features: [Temporal]
+---*/
+
+const originalOptions = {
+ largestUnit: "year",
+ shouldBeCopied: {},
+};
+let called = false;
+
+class Calendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+
+ dateUntil(d1, d2, options) {
+ called = true;
+ assert.notSameValue(options, originalOptions, "options bag should be a copy");
+ assert.sameValue(options.shouldBeCopied, originalOptions.shouldBeCopied, "options bag should be a shallow copy");
+ return new Temporal.Duration(-1);
+ }
+}
+const calendar = new Calendar();
+const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar);
+// exactly one year later; avoids NanosecondsToDays path
+const later = new Temporal.ZonedDateTime(1_031_536_000_000_000_000n, "UTC", calendar);
+later.since(earlier, originalOptions);
+assert(called, "calendar.dateUntil must be called");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/calendar-dateuntil-called-with-null-prototype-options.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/calendar-dateuntil-called-with-null-prototype-options.js
new file mode 100644
index 0000000000..0bcc949ed2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/calendar-dateuntil-called-with-null-prototype-options.js
@@ -0,0 +1,20 @@
+// |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.zoneddatetime.prototype.since
+description: >
+ Calendar.dateUntil method is called with a null-prototype object as the
+ options value when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckOptionsPrototypePollution();
+const instance = new Temporal.ZonedDateTime(0n, "UTC", calendar);
+const argument = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar);
+instance.since(argument, { largestUnit: "year" });
+assert.sameValue(calendar.dateUntilCallCount, 1, "dateUntil should have been called on the calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/calendar-dateuntil-called-with-singular-largestunit.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/calendar-dateuntil-called-with-singular-largestunit.js
new file mode 100644
index 0000000000..6474c9aa96
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/calendar-dateuntil-called-with-singular-largestunit.js
@@ -0,0 +1,117 @@
+// |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.zoneddatetime.prototype.since
+description: The options object passed to calendar.dateUntil has a largestUnit property with its value in the singular form
+info: |
+ sec-temporal.zoneddatetime.prototype.since steps 14–18:
+ 14. If _largestUnit_ is not one of *"year"*, *"month"*, *"week"*, or *"day"*, then
+ ...
+ c. Return ...
+ 15. ...
+ 16. Let _difference_ be ? DifferenceZonedDateTime(_zonedDateTime_.[[Nanoseconds]], _other_.[[Nanoseconds]], _zonedDateTime_.[[TimeZone]], _zonedDateTime_.[[Calendar]], _largestUnit_).
+ 17. Let _roundResult_ be ? RoundDuration(_difference_.[[Years]], _difference_.[[Months]], _difference_.[[Weeks]], _difference_.[[Days]], _difference_.[[Hours]], _difference_.[[Minutes]], _difference_.[[Seconds]], _difference_.[[Milliseconds]], _difference_.[[Microseconds]], _difference_.[[Nanoseconds]], _roundingIncrement_, _smallestUnit_, _roundingMode_, _zonedDateTime_).
+ 18. Let _result_ be ? AdjustRoundedDurationDays(_roundResult_.[[Years]], _roundResult_.[[Months]], _roundResult_.[[Weeks]], _roundResult_.[[Days]], _roundResult_.[[Hours]], _roundResult_.[[Minutes]], _roundResult_.[[Seconds]], _roundResult_.[[Milliseconds]], _roundResult_.[[Microseconds]], _roundResult_.[[Nanoseconds]], _roundingIncrement_, _smallestUnit_, _roundingMode_, _zonedDateTime_).
+ sec-temporal-differencezoneddatetime steps 7 and 11:
+ 7. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _calendar_, _largestUnit_, _options_).
+ 11. Let _result_ be ? NanosecondsToDays(_timeRemainderNs_, _intermediate_).
+ sec-temporal-roundduration steps 5.d and 8.n–p:
+ 5. If _unit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*, then
+ ...
+ d. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _intermediate_).
+ ...
+ 8. If _unit_ is *"year"*, then
+ ...
+ n. Let _untilOptions_ be ! OrdinaryObjectCreate(*null*).
+ o. Perform ! CreateDataPropertyOrThrow(_untilOptions_, *"largestUnit"*, *"year"*).
+ p. Let _timePassed_ be ? CalendarDateUntil(_calendar_, _relativeTo_, _daysLater_, _untilOptions_)
+ sec-temporal-adjustroundeddurationdays steps 1 and 9:
+ 1. If _relativeTo_ does not have an [[InitializedTemporalZonedDateTime]] internal slot; or _unit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*; or _unit_ is *"nanosecond"* and _increment_ is 1, then
+ a. Return ...
+ ...
+ 9. Let _adjustedDateDuration_ be ? AddDuration(_years_, _months_, _weeks_, _days_, 0, 0, 0, 0, 0, 0, 0, 0, 0, _direction_, 0, 0, 0, 0, 0, 0, _relativeTo_).
+ sec-temporal-addduration step 7.a–g:
+ a. Assert: _relativeTo_ has an [[IntializedTemporalZonedDateTime]] internal slot.
+ ...
+ f. If _largestUnit_ is not one of *"year"*, *"month"*, *"week"*, or *"day"*, then
+ ...
+ g. Else,
+ i. Let _result_ be ? DifferenceZonedDateTime(_relativeTo_.[[Nanoseconds]], _endNs_, _timeZone_, _calendar_, _largestUnit_).
+ sec-temporal-nanosecondstodays step 11:
+ 11. 1. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _relativeTo_.[[Calendar]], *"day"*).
+ sec-temporal-differenceisodatetime steps 9–11:
+ 9. Let _dateLargestUnit_ be ! LargerOfTwoTemporalUnits(*"day"*, _largestUnit_).
+ 10. Let _untilOptions_ be ? MergeLargestUnitOption(_options_, _dateLargestUnit_).
+ 11. Let _dateDifference_ be ? CalendarDateUntil(_calendar_, _date1_, _date2_, _untilOptions_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkCalendarDateUntilLargestUnitSingular(
+ (calendar, largestUnit) => {
+ const earlier = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC", calendar);
+ const later = new Temporal.ZonedDateTime(1_086_403_661_988_655_322n, "UTC", calendar);
+ later.since(earlier, { largestUnit });
+ },
+ {
+ years: ["year"],
+ months: ["month"],
+ weeks: ["week"],
+ days: [],
+ hours: [],
+ minutes: [],
+ seconds: [],
+ milliseconds: [],
+ microseconds: [],
+ nanoseconds: []
+ }
+);
+
+// Additionally check the path that goes through AdjustRoundedDurationDays
+
+TemporalHelpers.checkCalendarDateUntilLargestUnitSingular(
+ (calendar, largestUnit) => {
+ const earlier = new Temporal.ZonedDateTime(-31536000_000_000_000n /* = -365 days */, "UTC", calendar);
+ const later = new Temporal.ZonedDateTime(86_399_999_999_999n, "UTC", calendar);
+ later.since(earlier, { largestUnit, roundingIncrement: 2, roundingMode: 'ceil' });
+ },
+ {
+ years: ["year", "year", "year"],
+ months: ["month", "month", "month"],
+ weeks: ["week", "week", "week"],
+ days: [],
+ hours: [],
+ minutes: [],
+ seconds: [],
+ milliseconds: [],
+ microseconds: [],
+ nanoseconds: []
+ }
+);
+
+// Also check the path that goes through RoundDuration when smallestUnit is
+// given
+
+TemporalHelpers.checkCalendarDateUntilLargestUnitSingular(
+ (calendar, smallestUnit) => {
+ const earlier = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC", calendar);
+ const later = new Temporal.ZonedDateTime(1_086_403_661_988_655_322n, "UTC", calendar);
+ later.since(earlier, { smallestUnit });
+ },
+ {
+ years: ["year", "year", "year"],
+ months: ["month", "month", "month"],
+ weeks: ["week", "week", "week"],
+ days: [],
+ hours: [],
+ minutes: [],
+ seconds: [],
+ milliseconds: [],
+ microseconds: [],
+ nanoseconds: []
+ }
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/calendar-fields-iterable.js
new file mode 100644
index 0000000000..b3fe7c118c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/calendar-fields-iterable.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.zoneddatetime.prototype.since
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.zoneddatetime.prototype.since step 3:
+ 3. Set _other_ to ? ToTemporalZonedDateTime(_other_).
+ sec-temporal-totemporalzoneddatetime step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"hour"*, *"microsecond"*, *"millisecond"*, *"minute"*, *"month"*, *"monthCode"*, *"nanosecond"*, *"second"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar1);
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+datetime.since({ year: 2005, month: 6, day: 2, timeZone: "UTC", calendar: calendar2 });
+
+assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/calendar-temporal-object.js
new file mode 100644
index 0000000000..600a7c4560
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/calendar-temporal-object.js
@@ -0,0 +1,29 @@
+// |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.zoneddatetime.prototype.since
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.zoneddatetime.prototype.since step 3:
+ 3. Set _other_ to ? ToTemporalZonedDateTime(_other_).
+ sec-temporal-totemporalzoneddatetime step 2.b:
+ b. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", temporalObject);
+ datetime.since({ year: 2005, month: 6, day: 2, timeZone: "UTC", calendar: temporalObject });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..38fbbcdbae
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/constructor-in-calendar-fields.js
@@ -0,0 +1,18 @@
+// |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.zoneddatetime.prototype.since
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const timeZone = 'Europe/Paris'
+const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar, timeZone: timeZone };
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+
+assert.throws(RangeError, () => instance.since(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/date-and-time-durations-opposite-signs.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/date-and-time-durations-opposite-signs.js
new file mode 100644
index 0000000000..63378ff0d0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/date-and-time-durations-opposite-signs.js
@@ -0,0 +1,47 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2024 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.zoneddatetime.prototype.since
+description: >
+ Rounding calculation in difference method can result in duration date and
+ time components with opposite signs
+info: |
+ DifferenceTemporalZonedDateTime ( operation, zonedDateTime, other, options )
+ 17. If _roundingGranularityIsNoop_ is *false*, then
+ ...
+ e. Let _adjustResult_ be ? AdjustRoundedDurationDays(_roundResult_.[[Years]], _roundResult_.[[Months]],
+ _roundResult_.[[Weeks]], _days_, _daysResult_.[[NormalizedTime]], _settings_.[[RoundingIncrement]],
+ _settings_.[[SmallestUnit]], _settings_.[[RoundingMode]], _zonedDateTime_, _calendarRec_, _timeZoneRec_,
+ _precalculatedPlainDateTime_).
+ f. Let _balanceResult_ be ? BalanceDateDurationRelative(_adjustResult_.[[Years]], _adjustResult_.[[Months]],
+ _adjustResult_.[[Weeks]], _adjustResult_.[[Days]], _settings_.[[LargestUnit]], _settings_.[[SmallestUnit]],
+ _plainRelativeTo_, _calendarRec_).
+ g. Set _result_ to ? CombineDateAndNormalizedTimeDuration(_balanceResult_, _adjustResult_.[[NormalizedTime]]).
+features: [Temporal]
+---*/
+
+// Based on a test case by André Bargull
+
+const calendar = new class extends Temporal.Calendar {
+ #dateUntil = 0;
+
+ dateUntil(one, two, options) {
+ let result = super.dateUntil(one, two, options);
+ if (++this.#dateUntil === 2) {
+ result = result.negated();
+ }
+ return result;
+ }
+}("iso8601");
+
+const oneDay = 86400_000_000_000;
+const start = new Temporal.ZonedDateTime(0n, "UTC", calendar);
+const end = new Temporal.ZonedDateTime(BigInt(500.5 * oneDay), "UTC", calendar);
+
+assert.throws(RangeError, () => end.since(start, {
+ largestUnit: "years",
+ smallestUnit: "hours",
+}));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/duplicate-calendar-fields.js
new file mode 100644
index 0000000000..9c308ecd5c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/duplicate-calendar-fields.js
@@ -0,0 +1,20 @@
+// |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.zoneddatetime.prototype.since
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['day'], ['hour'], ['microsecond'], ['millisecond'], ['minute'], ['month'], ['monthCode'], ['nanosecond'], ['second'], ['year'], ['timeZone'], ['offset']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const timeZone = 'Europe/Paris'
+ const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar, timeZone: timeZone };
+ const instance = new Temporal.ZonedDateTime(0n, timeZone);
+
+ assert.throws(RangeError, () => instance.since(arg));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..2fd5bc22e4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/infinity-throws-rangeerror.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.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.zoneddatetime.prototype.since
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+const base = { year: 2000, month: 5, day: 2, hour: 15, minute: 30, second: 45, millisecond: 987, microsecond: 654, nanosecond: 321, timeZone: "UTC" };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.since({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.since({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/largestunit-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/largestunit-invalid-string.js
new file mode 100644
index 0000000000..c4634b3162
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/largestunit-invalid-string.js
@@ -0,0 +1,31 @@
+// |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.zoneddatetime.prototype.since
+description: RangeError thrown when largestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+const later = new Temporal.ZonedDateTime(1_000_090_061_987_654_321n, "UTC");
+const badValues = [
+ "era",
+ "eraYear",
+ "millisecond\0",
+ "mill\u0131second",
+ "SECOND",
+ "eras",
+ "eraYears",
+ "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/ZonedDateTime/prototype/since/largestunit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/largestunit-plurals-accepted.js
new file mode 100644
index 0000000000..31b4b7dea9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/largestunit-plurals-accepted.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.zoneddatetime.prototype.since
+description: Plural units are accepted as well for the largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+const later = new Temporal.ZonedDateTime(1_086_403_661_988_655_322n, "UTC");
+const validUnits = [
+ "year",
+ "month",
+ "week",
+ "day",
+ "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/ZonedDateTime/prototype/since/largestunit-smallestunit-mismatch.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/largestunit-smallestunit-mismatch.js
new file mode 100644
index 0000000000..5898a41959
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/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.zoneddatetime.prototype.since
+description: RangeError thrown when smallestUnit is larger than largestUnit
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+const later = new Temporal.ZonedDateTime(1_000_090_061_987_654_321n, "UTC");
+const units = ["years", "months", "weeks", "days", "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/ZonedDateTime/prototype/since/largestunit-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/largestunit-undefined.js
new file mode 100644
index 0000000000..bc79f17917
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/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.zoneddatetime.prototype.since
+description: Fallback value for largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+const later = new Temporal.ZonedDateTime(1_000_090_061_987_654_321n, "UTC");
+
+const explicit = later.since(earlier, { largestUnit: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 0, 25, 1, 1, 987, 654, 321, "default largestUnit is hour");
+const implicit = later.since(earlier, {});
+TemporalHelpers.assertDuration(implicit, 0, 0, 0, 0, 25, 1, 1, 987, 654, 321, "default largestUnit is hour");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/largestunit-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/largestunit-wrong-type.js
new file mode 100644
index 0000000000..bca7496654
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/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.zoneddatetime.prototype.since
+description: Type conversions for largestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+const later = new Temporal.ZonedDateTime(1_000_090_061_987_654_321n, "UTC");
+TemporalHelpers.checkStringOptionWrongType("largestUnit", "year",
+ (largestUnit) => later.since(earlier, { largestUnit }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 1, 1, 1, 1, 987, 654, 321, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/largestunit.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/largestunit.js
new file mode 100644
index 0000000000..58736d1b1e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/largestunit.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.zoneddatetime.prototype.since
+description: Specify behavior of ZonedDateTime.since when largest specified unit is specified
+includes: [temporalHelpers.js]
+features: [Temporal, BigInt]
+---*/
+const thePast = new Temporal.ZonedDateTime(1234567890123456789n, '-08:00');
+const theFuture = new Temporal.ZonedDateTime(2345678901234567890n, '-08:00');
+TemporalHelpers.assertDuration(theFuture.since(thePast), 0, 0, 0, 0, 308641, 56, 51, 111, 111, 101, 'does not include higher units than necessary (largest unit unspecified)');
+TemporalHelpers.assertDuration(theFuture.since(thePast, { largestUnit: 'years' }), 35, 2, 0, 15, 1, 56, 51, 111, 111, 101, 'does not include higher units than necessary (largest unit is years)');
+TemporalHelpers.assertDuration(theFuture.since(thePast, { largestUnit: 'months' }), 0, 422, 0, 15, 1, 56, 51, 111, 111, 101, 'does not include higher units than necessary (largest unit is months)');
+TemporalHelpers.assertDuration(theFuture.since(thePast, { largestUnit: 'weeks' }), 0, 0, 1837, 1, 1, 56, 51, 111, 111, 101, 'does not include higher units than necessary (largest unit is weeks)');
+TemporalHelpers.assertDuration(theFuture.since(thePast, { largestUnit: 'days' }), 0, 0, 0, 12860, 1, 56, 51, 111, 111, 101, 'does not include higher units than necessary (largest unit is days)');
+TemporalHelpers.assertDuration(theFuture.since(thePast, { largestUnit: 'hours' }), 0, 0, 0, 0, 308641, 56, 51, 111, 111, 101, 'does not include higher units than necessary (largest unit is hours)');
+TemporalHelpers.assertDuration(theFuture.since(thePast, { largestUnit: 'minutes' }), 0, 0, 0, 0, 0, 18518516, 51, 111, 111, 101, 'does not include higher units than necessary (largest unit is minutes)');
+TemporalHelpers.assertDuration(theFuture.since(thePast, { largestUnit: 'seconds' }), 0, 0, 0, 0, 0, 0, 1111111011, 111, 111, 101, 'does not include higher units than necessary (largest unit is seconds)');
+TemporalHelpers.assertDuration(theFuture.since(thePast, { largestUnit: 'milliseconds' }), 0, 0, 0, 0, 0, 0, 0, 1111111011111, 111, 101, 'does not include higher units than necessary (largest unit is milliseconds)');
+TemporalHelpers.assertDuration(theFuture.since(thePast, { largestUnit: 'microseconds' }), 0, 0, 0, 0, 0, 0, 0, 0, 1111111011111111, 101, 'does not include higher units than necessary (largest unit is microseconds)');
+TemporalHelpers.assertDuration(theFuture.since(thePast, { largestUnit: 'nanoseconds' }), 0, 0, 0, 0, 0, 0, 0, 0, 0, 1111111011111111000, 'does not include higher units than necessary (largest unit is nanoseconds)');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/leap-second.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/leap-second.js
new file mode 100644
index 0000000000..a9b6619fb8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/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.zoneddatetime.prototype.since
+description: Leap second is a valid ISO string for ZonedDateTime
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(1_483_228_799_000_000_000n, timeZone);
+
+let arg = "2016-12-31T23:59:60+00:00[UTC]";
+const result = instance.since(arg);
+TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "leap second is a valid ISO string for ZonedDateTime"
+);
+
+arg = "2000-05-02T12:34:56+23:59[+23:59:60]";
+assert.throws(
+ RangeError,
+ () => instance.since(arg),
+ "leap second in time zone name not valid"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/length.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/length.js
new file mode 100644
index 0000000000..7e7f1a1dff
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/length.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.zoneddatetime.prototype.since
+description: Temporal.ZonedDateTime.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.ZonedDateTime.prototype.since, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/name.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/name.js
new file mode 100644
index 0000000000..b742e2620c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/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.zoneddatetime.prototype.since
+description: Temporal.ZonedDateTime.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.ZonedDateTime.prototype.since, "name", {
+ value: "since",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/negative-epochnanoseconds.js
new file mode 100644
index 0000000000..501f4ae30c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/negative-epochnanoseconds.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.zoneddatetime.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 result = datetime.since(new Temporal.ZonedDateTime(0n, "UTC"), { largestUnit: "month" });
+TemporalHelpers.assertDuration(result, 0, -5, 0, -7, -7, -9, -24, -999, -999, -999);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/normalized-time-duration-to-days-loop-arbitrarily.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/normalized-time-duration-to-days-loop-arbitrarily.js
new file mode 100644
index 0000000000..23f993f25f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/normalized-time-duration-to-days-loop-arbitrarily.js
@@ -0,0 +1,78 @@
+// |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.zoneddatetime.prototype.since
+description: >
+ NormalizedTimeDurationToDays can loop arbitrarily up to max safe integer
+info: |
+ NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ , precalculatedPlainDatetime ] )
+ ...
+ 21. Repeat, while done is false,
+ a. Let oneDayFarther be ? AddDaysToZonedDateTime(relativeResult.[[Instant]],
+ relativeResult.[[DateTime]], timeZoneRec, zonedRelativeTo.[[Calendar]], sign).
+ b. Set dayLengthNs to NormalizedTimeDurationFromEpochNanosecondsDifference(oneDayFarther.[[EpochNanoseconds]],
+ relativeResult.[[EpochNanoseconds]]).
+ c. Let oneDayLess be ? SubtractNormalizedTimeDuration(norm, dayLengthNs).
+ c. If NormalizedTimeDurationSign(oneDayLess) × sign ≥ 0, then
+ i. Set norm to oneDayLess.
+ ii. Set relativeResult to oneDayFarther.
+ iii. Set days to days + sign.
+ d. Else,
+ i. Set done to true.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calls = [];
+const dayLengthNs = 86400000000000n;
+const other = new Temporal.ZonedDateTime(dayLengthNs, "UTC", "iso8601");
+
+function createRelativeTo(count) {
+ const dayInstant = new Temporal.Instant(dayLengthNs);
+ const substitutions = [];
+ const timeZone = new Temporal.TimeZone("UTC");
+ // Return constant value for first _count_ calls
+ TemporalHelpers.substituteMethod(
+ timeZone,
+ "getPossibleInstantsFor",
+ substitutions
+ );
+ substitutions.length = count;
+ let i = 0;
+ for (i = 0; i < substitutions.length; i++) {
+ // (this value)
+ substitutions[i] = [dayInstant];
+ }
+ // Record calls in calls[]
+ TemporalHelpers.observeMethod(calls, timeZone, "getPossibleInstantsFor");
+ return new Temporal.ZonedDateTime(0n, timeZone);
+}
+
+let zdt = createRelativeTo(50);
+calls.splice(0); // Reset calls list after ZonedDateTime construction
+zdt.since(other, {
+ largestUnit: "day",
+});
+assert.sameValue(
+ calls.length,
+ 50 + 1,
+ "Expected ZonedDateTime.since to call getPossibleInstantsFor correct number of times"
+);
+
+zdt = createRelativeTo(100);
+calls.splice(0); // Reset calls list after previous loop + ZonedDateTime construction
+zdt.since(other, {
+ largestUnit: "day",
+});
+assert.sameValue(
+ calls.length,
+ 100 + 1,
+ "Expected ZonedDateTime.since to call getPossibleInstantsFor correct number of times"
+);
+
+zdt = createRelativeTo(105);
+assert.throws(RangeError, () => zdt.since(other, { largestUnit: "day" }), "105 days > 2⁵³ ns");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/normalized-time-duration-to-days-range-errors.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/normalized-time-duration-to-days-range-errors.js
new file mode 100644
index 0000000000..0c0b07f5dc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/normalized-time-duration-to-days-range-errors.js
@@ -0,0 +1,126 @@
+// |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.zoneddatetime.prototype.since
+description: >
+ Abstract operation NormalizedTimeDurationToDays can throw four different
+ RangeErrors.
+info: |
+ NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ , precalculatedPlainDateTime ] )
+ 22. If days < 0 and sign = 1, throw a RangeError exception.
+ 23. If days > 0 and sign = -1, throw a RangeError exception.
+ ...
+ 25. If NormalizedTimeDurationSign(_norm_) = 1 and sign = -1, throw a RangeError exception.
+ ...
+ 28. If dayLength ≥ 2⁵³, throw a RangeError exception.
+features: [Temporal, BigInt]
+includes: [temporalHelpers.js]
+---*/
+
+function timeZoneSubstituteValues(
+ getPossibleInstantsFor,
+ getOffsetNanosecondsFor
+) {
+ const tz = new Temporal.TimeZone("UTC");
+ TemporalHelpers.substituteMethod(
+ tz,
+ "getPossibleInstantsFor",
+ getPossibleInstantsFor
+ );
+ TemporalHelpers.substituteMethod(
+ tz,
+ "getOffsetNanosecondsFor",
+ getOffsetNanosecondsFor
+ );
+ return tz;
+}
+
+const dayNs = 86_400_000_000_000;
+const zeroZDT = new Temporal.ZonedDateTime(0n, "UTC");
+const oneZDT = new Temporal.ZonedDateTime(1n, "UTC");
+const epochInstant = new Temporal.Instant(0n);
+const options = { largestUnit: "days" };
+
+// Step 22: days < 0 and sign = 1
+let start = new Temporal.ZonedDateTime(
+ 0n, // Sets DifferenceZonedDateTime _ns1_
+ timeZoneSubstituteValues(
+ [[epochInstant]], // Returned in step 16, setting _relativeResult_
+ [
+ // Behave normally in 2 calls made prior to NormalizedTimeDurationToDays
+ TemporalHelpers.SUBSTITUTE_SKIP,
+ TemporalHelpers.SUBSTITUTE_SKIP,
+ dayNs - 1, // Returned in step 8, setting _startDateTime_
+ -dayNs + 1, // Returned in step 9, setting _endDateTime_
+ ]
+ )
+);
+assert.throws(RangeError, () =>
+ start.since(
+ oneZDT, // Sets DifferenceZonedDateTime _ns2_
+ options
+ )
+);
+
+// Step 23: days > 0 and sign = -1
+start = new Temporal.ZonedDateTime(
+ 1n, // Sets DifferenceZonedDateTime _ns1_
+ timeZoneSubstituteValues(
+ [[epochInstant]], // Returned in step 16, setting _relativeResult_
+ [
+ // Behave normally in 2 calls made prior to NormalizedTimeDurationToDays
+ TemporalHelpers.SUBSTITUTE_SKIP,
+ TemporalHelpers.SUBSTITUTE_SKIP,
+ -dayNs + 1, // Returned in step 8, setting _startDateTime_
+ dayNs - 1, // Returned in step 9, setting _endDateTime_
+ ]
+ )
+);
+assert.throws(RangeError, () =>
+ start.since(
+ zeroZDT, // Sets DifferenceZonedDateTime _ns2_
+ options
+ )
+);
+
+// Step 25: nanoseconds > 0 and sign = -1
+start = new Temporal.ZonedDateTime(
+ 1n, // Sets DifferenceZonedDateTime _ns1_
+ timeZoneSubstituteValues(
+ [[new Temporal.Instant(-1n)]], // Returned in step 16, setting _relativeResult_
+ [
+ // Behave normally in 2 calls made prior to NormalizedTimeDurationToDays
+ TemporalHelpers.SUBSTITUTE_SKIP,
+ TemporalHelpers.SUBSTITUTE_SKIP,
+ dayNs - 1, // Returned in step 8, setting _startDateTime_
+ -dayNs + 1, // Returned in step 9, setting _endDateTime_
+ ]
+ )
+);
+assert.throws(RangeError, () =>
+ start.since(
+ zeroZDT, // Sets DifferenceZonedDateTime _ns2_
+ options
+ )
+);
+
+// Step 28: day length is an unsafe integer
+start = new Temporal.ZonedDateTime(
+ 0n,
+ timeZoneSubstituteValues(
+ // Not called in step 16 because _days_ = 0
+ // Returned in step 21.a, making _oneDayFarther_ 2^53 ns later than _relativeResult_
+ [[new Temporal.Instant(2n ** 53n)]],
+ []
+ )
+);
+assert.throws(RangeError, () =>
+ start.since(
+ oneZDT,
+ options
+ ),
+ "Should throw RangeError when time zone calculates an outrageous day length"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/not-a-constructor.js
new file mode 100644
index 0000000000..3f1b838344
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/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.zoneddatetime.prototype.since
+description: >
+ Temporal.ZonedDateTime.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.ZonedDateTime.prototype.since();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.since), false,
+ "isConstructor(Temporal.ZonedDateTime.prototype.since)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/options-object.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/options-object.js
new file mode 100644
index 0000000000..26a66315d0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/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.zoneddatetime.prototype.since
+description: Empty or a function object may be used as options
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(0n, "UTC");
+
+const result1 = instance.since(new Temporal.ZonedDateTime(3600_000_000_000n, "UTC"), {});
+TemporalHelpers.assertDuration(
+ result1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0,
+ "options may be an empty plain object"
+);
+
+const result2 = instance.since(new Temporal.ZonedDateTime(3600_000_000_000n, "UTC"), () => {});
+TemporalHelpers.assertDuration(
+ result2, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0,
+ "options may be a function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/options-undefined.js
new file mode 100644
index 0000000000..925553fc93
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/options-undefined.js
@@ -0,0 +1,30 @@
+// |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.zoneddatetime.prototype.since
+description: Verify that undefined options are handled correctly.
+features: [BigInt, Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(957270896_987_654_321n, "UTC");
+const later = new Temporal.ZonedDateTime(959949296_987_654_322n, "UTC");
+
+const explicit = later.since(earlier, undefined);
+assert.sameValue(explicit.years, 0, "default largest unit is hours");
+assert.sameValue(explicit.months, 0, "default largest unit is hours");
+assert.sameValue(explicit.weeks, 0, "default largest unit is hours");
+assert.sameValue(explicit.days, 0, "default largest unit is hours");
+assert.sameValue(explicit.hours, 744, "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.years, 0, "default largest unit is hours");
+assert.sameValue(implicit.months, 0, "default largest unit is hours");
+assert.sameValue(implicit.weeks, 0, "default largest unit is hours");
+assert.sameValue(implicit.days, 0, "default largest unit is hours");
+assert.sameValue(implicit.hours, 744, "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/ZonedDateTime/prototype/since/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/options-wrong-type.js
new file mode 100644
index 0000000000..3789038a5b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/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.zoneddatetime.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.ZonedDateTime(0n, "UTC");
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.since(new Temporal.ZonedDateTime(3600_000_000_000n, "UTC"), value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/order-of-operations.js
new file mode 100644
index 0000000000..566a1ce192
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/order-of-operations.js
@@ -0,0 +1,408 @@
+// |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.zoneddatetime.prototype.since
+description: Properties on objects passed to since() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ // ToTemporalZonedDateTime
+ "get other.calendar",
+ "has other.calendar.dateAdd",
+ "has other.calendar.dateFromFields",
+ "has other.calendar.dateUntil",
+ "has other.calendar.day",
+ "has other.calendar.dayOfWeek",
+ "has other.calendar.dayOfYear",
+ "has other.calendar.daysInMonth",
+ "has other.calendar.daysInWeek",
+ "has other.calendar.daysInYear",
+ "has other.calendar.fields",
+ "has other.calendar.id",
+ "has other.calendar.inLeapYear",
+ "has other.calendar.mergeFields",
+ "has other.calendar.month",
+ "has other.calendar.monthCode",
+ "has other.calendar.monthDayFromFields",
+ "has other.calendar.monthsInYear",
+ "has other.calendar.weekOfYear",
+ "has other.calendar.year",
+ "has other.calendar.yearMonthFromFields",
+ "has other.calendar.yearOfWeek",
+ "get other.calendar.dateFromFields",
+ "get other.calendar.fields",
+ "call other.calendar.fields",
+ "get other.day",
+ "get other.day.valueOf",
+ "call other.day.valueOf",
+ "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.month",
+ "get other.month.valueOf",
+ "call other.month.valueOf",
+ "get other.monthCode",
+ "get other.monthCode.toString",
+ "call other.monthCode.toString",
+ "get other.nanosecond",
+ "get other.nanosecond.valueOf",
+ "call other.nanosecond.valueOf",
+ "get other.offset",
+ "get other.offset.toString",
+ "call other.offset.toString",
+ "get other.second",
+ "get other.second.valueOf",
+ "call other.second.valueOf",
+ "get other.timeZone",
+ "get other.year",
+ "get other.year.valueOf",
+ "call other.year.valueOf",
+ "has other.timeZone.getOffsetNanosecondsFor",
+ "has other.timeZone.getPossibleInstantsFor",
+ "has other.timeZone.id",
+ "call other.calendar.dateFromFields",
+ "get other.timeZone.getOffsetNanosecondsFor",
+ "get other.timeZone.getPossibleInstantsFor",
+ "call other.timeZone.getPossibleInstantsFor",
+ "call other.timeZone.getOffsetNanosecondsFor",
+ // CalendarEquals
+ "get this.calendar.id",
+ "get other.calendar.id",
+ // 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 ownTimeZone = TemporalHelpers.timeZoneObserver(actual, "this.timeZone");
+const ownCalendar = TemporalHelpers.calendarObserver(actual, "this.calendar");
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, ownTimeZone, ownCalendar);
+
+const dstTimeZone = TemporalHelpers.springForwardFallBackTimeZone();
+const ownDstTimeZone = TemporalHelpers.timeZoneObserver(actual, "this.timeZone", {
+ getOffsetNanosecondsFor: dstTimeZone.getOffsetNanosecondsFor,
+ getPossibleInstantsFor: dstTimeZone.getPossibleInstantsFor,
+});
+const otherDstTimeZone = TemporalHelpers.timeZoneObserver(actual, "other.timeZone", {
+ getOffsetNanosecondsFor: dstTimeZone.getOffsetNanosecondsFor,
+ getPossibleInstantsFor: dstTimeZone.getPossibleInstantsFor,
+});
+/* 2000-10-29T01:30-07:00, in the middle of the first repeated hour: */
+const fallBackInstance = new Temporal.ZonedDateTime(972808200_000_000_000n, ownDstTimeZone, ownCalendar);
+
+const otherDateTimePropertyBag = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2004,
+ month: 5,
+ monthCode: "M05",
+ day: 12,
+ hour: 1,
+ minute: 46,
+ second: 40,
+ millisecond: 250,
+ microsecond: 500,
+ nanosecond: 750,
+ offset: "+00:00",
+ calendar: TemporalHelpers.calendarObserver(actual, "other.calendar"),
+ timeZone: TemporalHelpers.timeZoneObserver(actual, "other.timeZone"),
+}, "other");
+
+function createOptionsObserver({ smallestUnit = "nanoseconds", largestUnit = "auto", roundingMode = "halfExpand", roundingIncrement = 1 } = {}) {
+ return TemporalHelpers.propertyBagObserver(actual, {
+ // order is significant, due to iterating through properties in order to
+ // copy them to an internal null-prototype object:
+ roundingIncrement,
+ roundingMode,
+ largestUnit,
+ smallestUnit,
+ additional: "property",
+ }, "options");
+}
+
+// clear any observable things that happened while constructing the objects
+actual.splice(0);
+
+// basic order of observable operations, without rounding:
+instance.since(otherDateTimePropertyBag, createOptionsObserver());
+assert.compareArray(actual, expected, "order of operations");
+actual.splice(0); // clear
+
+// short-circuit for identical objects will still test TimeZoneEquals if
+// largestUnit is a calendar unit:
+const identicalPropertyBag = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2001,
+ month: 9,
+ monthCode: "M09",
+ day: 9,
+ hour: 1,
+ minute: 46,
+ second: 40,
+ millisecond: 0,
+ microsecond: 0,
+ nanosecond: 0,
+ offset: "+00:00",
+ calendar: TemporalHelpers.calendarObserver(actual, "other.calendar"),
+ timeZone: TemporalHelpers.timeZoneObserver(actual, "other.timeZone"),
+}, "other");
+
+instance.since(identicalPropertyBag, createOptionsObserver({ largestUnit: "years" }));
+assert.compareArray(actual, expected.concat([
+ "get this.timeZone.id",
+ "get other.timeZone.id",
+]), "order of operations with identical dates and largestUnit a calendar unit");
+actual.splice(0); // clear
+
+// two ZonedDateTimes that denote the same wall-clock time in the time zone can
+// avoid calling some calendar methods:
+const fallBackPropertyBag = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2000,
+ month: 10,
+ monthCode: "M10",
+ day: 29,
+ hour: 1,
+ minute: 30,
+ second: 0,
+ millisecond: 0,
+ microsecond: 0,
+ nanosecond: 0,
+ offset: "-08:00",
+ calendar: TemporalHelpers.calendarObserver(actual, "other.calendar"),
+ timeZone: otherDstTimeZone,
+}, "other");
+fallBackInstance.since(fallBackPropertyBag, createOptionsObserver({ largestUnit: "days" }));
+assert.compareArray(actual, [
+ // ToTemporalZonedDateTime
+ "get other.calendar",
+ "has other.calendar.dateAdd",
+ "has other.calendar.dateFromFields",
+ "has other.calendar.dateUntil",
+ "has other.calendar.day",
+ "has other.calendar.dayOfWeek",
+ "has other.calendar.dayOfYear",
+ "has other.calendar.daysInMonth",
+ "has other.calendar.daysInWeek",
+ "has other.calendar.daysInYear",
+ "has other.calendar.fields",
+ "has other.calendar.id",
+ "has other.calendar.inLeapYear",
+ "has other.calendar.mergeFields",
+ "has other.calendar.month",
+ "has other.calendar.monthCode",
+ "has other.calendar.monthDayFromFields",
+ "has other.calendar.monthsInYear",
+ "has other.calendar.weekOfYear",
+ "has other.calendar.year",
+ "has other.calendar.yearMonthFromFields",
+ "has other.calendar.yearOfWeek",
+ "get other.calendar.dateFromFields",
+ "get other.calendar.fields",
+ "call other.calendar.fields",
+ "get other.day",
+ "get other.day.valueOf",
+ "call other.day.valueOf",
+ "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.month",
+ "get other.month.valueOf",
+ "call other.month.valueOf",
+ "get other.monthCode",
+ "get other.monthCode.toString",
+ "call other.monthCode.toString",
+ "get other.nanosecond",
+ "get other.nanosecond.valueOf",
+ "call other.nanosecond.valueOf",
+ "get other.offset",
+ "get other.offset.toString",
+ "call other.offset.toString",
+ "get other.second",
+ "get other.second.valueOf",
+ "call other.second.valueOf",
+ "get other.timeZone",
+ "get other.year",
+ "get other.year.valueOf",
+ "call other.year.valueOf",
+ "has other.timeZone.getOffsetNanosecondsFor",
+ "has other.timeZone.getPossibleInstantsFor",
+ "has other.timeZone.id",
+ "call other.calendar.dateFromFields",
+ "get other.timeZone.getOffsetNanosecondsFor",
+ "get other.timeZone.getPossibleInstantsFor",
+ "call other.timeZone.getPossibleInstantsFor",
+ "call other.timeZone.getOffsetNanosecondsFor",
+ // NOTE: extra because of wall-clock time ambiguity:
+ "call other.timeZone.getOffsetNanosecondsFor",
+ // CalendarEquals
+ "get this.calendar.id",
+ "get other.calendar.id",
+ // 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",
+ // TimeZoneEquals
+ "get this.timeZone.id",
+ "get other.timeZone.id",
+ // lookup
+ "get this.timeZone.getOffsetNanosecondsFor",
+ "get this.timeZone.getPossibleInstantsFor",
+ "get this.calendar.dateAdd",
+ "get this.calendar.dateUntil",
+ // DifferenceZonedDateTime
+ "call this.timeZone.getOffsetNanosecondsFor",
+ "call this.timeZone.getOffsetNanosecondsFor",
+ // NanosecondsToDays
+ "call this.timeZone.getOffsetNanosecondsFor",
+ "call this.timeZone.getOffsetNanosecondsFor",
+ // NanosecondsToDays → AddDaysToZonedDateTime
+ "call this.timeZone.getPossibleInstantsFor",
+], "order of operations with identical wall-clock times and largestUnit a calendar unit");
+actual.splice(0); // clear
+
+// Making largestUnit a calendar unit adds the following observable operations:
+const expectedOpsForCalendarDifference = [
+ // TimeZoneEquals
+ "get this.timeZone.id",
+ "get other.timeZone.id",
+ // lookup
+ "get this.timeZone.getOffsetNanosecondsFor",
+ "get this.timeZone.getPossibleInstantsFor",
+ "get this.calendar.dateAdd",
+ "get this.calendar.dateUntil",
+ // precalculate PlainDateTime
+ "call this.timeZone.getOffsetNanosecondsFor",
+ // DifferenceZonedDateTime
+ "call this.timeZone.getOffsetNanosecondsFor",
+ // DifferenceISODateTime
+ "call this.calendar.dateUntil",
+ // AddZonedDateTime
+ "call this.calendar.dateAdd",
+ "call this.timeZone.getPossibleInstantsFor",
+ // NanosecondsToDays
+ "call this.timeZone.getOffsetNanosecondsFor",
+ "call this.timeZone.getOffsetNanosecondsFor",
+ // NanosecondsToDays → AddDaysToZonedDateTime
+ "call this.timeZone.getPossibleInstantsFor",
+ "call this.timeZone.getPossibleInstantsFor",
+];
+
+const expectedOpsForCalendarRounding = [
+ // RoundDuration → MoveRelativeZonedDateTime → AddZonedDateTime
+ "call this.calendar.dateAdd",
+ "call this.timeZone.getPossibleInstantsFor",
+ // RoundDuration → NanosecondsToDays
+ "call this.timeZone.getOffsetNanosecondsFor",
+ "call this.timeZone.getOffsetNanosecondsFor",
+ // RoundDuration → NanosecondsToDays → AddDaysToZonedDateTime
+ "call this.timeZone.getPossibleInstantsFor",
+];
+
+// code path that skips RoundDuration:
+instance.since(otherDateTimePropertyBag, createOptionsObserver({ largestUnit: "years", smallestUnit: "nanoseconds", roundingIncrement: 1 }));
+assert.compareArray(actual, expected.concat(expectedOpsForCalendarDifference), "order of operations with largestUnit years and no rounding");
+actual.splice(0); // clear
+
+// code path through RoundDuration that rounds to the nearest year:
+const expectedOpsForYearRounding = expected.concat(expectedOpsForCalendarDifference, expectedOpsForCalendarRounding, [
+ // RoundDuration
+ "call this.calendar.dateAdd", // 12.d
+ "call this.calendar.dateAdd", // 12.f
+ "call this.calendar.dateUntil", // 12.n
+ "call this.calendar.dateAdd", // 12.x MoveRelativeDate
+ // (12.r not called because other units can't add up to >1 year at this point)
+ // BalanceDateDurationRelative
+ "call this.calendar.dateAdd", // 9.c
+ "call this.calendar.dateUntil" // 9.d
+]);
+instance.since(otherDateTimePropertyBag, createOptionsObserver({ smallestUnit: "years" }));
+assert.compareArray(actual, expectedOpsForYearRounding, "order of operations with smallestUnit = years");
+actual.splice(0); // clear
+
+// code path through RoundDuration that rounds to the nearest month:
+const expectedOpsForMonthRounding = expected.concat(expectedOpsForCalendarDifference, expectedOpsForCalendarRounding, [
+ // RoundDuration
+ "call this.calendar.dateAdd", // 13.c
+ "call this.calendar.dateAdd", // 13.e
+ "call this.calendar.dateUntil", // 13.m
+ "call this.calendar.dateAdd", // 13.w MoveRelativeDate
+ // BalanceDateDurationRelative
+ "call this.calendar.dateAdd", // 10.d
+ "call this.calendar.dateUntil", // 10.e
+]);
+instance.since(otherDateTimePropertyBag, createOptionsObserver({ smallestUnit: "months" }));
+assert.compareArray(actual, expectedOpsForMonthRounding, "order of operations with smallestUnit = months");
+actual.splice(0); // clear
+
+// code path through RoundDuration that rounds to the nearest week:
+const expectedOpsForWeekRounding = expected.concat(expectedOpsForCalendarDifference, expectedOpsForCalendarRounding, [
+ // RoundDuration
+ "call this.calendar.dateUntil", // 14.f
+ "call this.calendar.dateAdd", // 14.p MoveRelativeDate
+ // BalanceDateDurationRelative
+ "call this.calendar.dateAdd", // 16
+ "call this.calendar.dateUntil", // 17
+]);
+instance.since(otherDateTimePropertyBag, createOptionsObserver({ smallestUnit: "weeks" }));
+assert.compareArray(actual, expectedOpsForWeekRounding, "order of operations with smallestUnit = weeks");
+actual.splice(0); // clear
+
+instance.since(otherDateTimePropertyBag, createOptionsObserver({ largestUnit: "hours" }));
+assert.compareArray(actual, expected, "order of operations with largestUnit being a time unit");
+actual.splice(0); // clear
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/prop-desc.js
new file mode 100644
index 0000000000..32c5015ca3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/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.zoneddatetime.prototype.since
+description: The "since" property of Temporal.ZonedDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.ZonedDateTime.prototype.since,
+ "function",
+ "`typeof ZonedDateTime.prototype.since` is `function`"
+);
+
+verifyProperty(Temporal.ZonedDateTime.prototype, "since", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/proto-in-calendar-fields.js
new file mode 100644
index 0000000000..2347de2c3f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/proto-in-calendar-fields.js
@@ -0,0 +1,18 @@
+// |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.zoneddatetime.prototype.since
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const timeZone = 'Europe/Paris'
+const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar, timeZone: timeZone };
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+
+assert.throws(RangeError, () => instance.since(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/read-time-fields-before-datefromfields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/read-time-fields-before-datefromfields.js
new file mode 100644
index 0000000000..9b8bf8f410
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/read-time-fields-before-datefromfields.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.zoneddatetime.prototype.since
+description: The time fields are read from the object before being passed to dateFromFields().
+info: |
+ sec-temporal.zoneddatetime.prototype.since step 3:
+ 3. Set _other_ to ? ToTemporalZonedDateTime(_other_).
+ sec-temporal-totemporalzoneddatetime step 2.j:
+ j. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+ sec-temporal-interprettemporaldatetimefields steps 1–2:
+ 1. Let _timeResult_ be ? ToTemporalTimeRecord(_fields_).
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarMakeInfinityTime();
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+const duration = datetime.since({ year: 2001, month: 9, day: 9, timeZone: "UTC", calendar });
+
+TemporalHelpers.assertDuration(duration, 0, 0, 0, 0, 1, 46, 40, 987, 654, 321);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/round-cross-unit-boundary.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/round-cross-unit-boundary.js
new file mode 100644
index 0000000000..6c185f0bea
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/round-cross-unit-boundary.js
@@ -0,0 +1,36 @@
+// |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.zoneddatetime.prototype.since
+description: Rounding can cross unit boundaries up to largestUnit
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+// Date units
+{
+ const earlier = new Temporal.ZonedDateTime(1640995200_000_000_000n /* = 2022-01-01T00 */, "UTC");
+ const later = new Temporal.ZonedDateTime(1703462400_000_000_000n /* = 2023-12-25T00 */, "UTC");
+ const duration = earlier.since(later, { largestUnit: "years", smallestUnit: "months", roundingMode: "expand" });
+ TemporalHelpers.assertDuration(duration, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, "-1 year -11 months balances to -2 years");
+}
+
+// Time units
+{
+ const earlier = new Temporal.ZonedDateTime(0n, "UTC");
+ const later = new Temporal.ZonedDateTime(7199_000_000_000n, "UTC");
+ 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:59 balances to -2 hours");
+}
+
+// Both
+{
+ const earlier = new Temporal.ZonedDateTime(0n, "UTC");
+ const later = new Temporal.ZonedDateTime(63071999_999_999_999n /* = 1971-12-31T23:59:59.999999999 */, "UTC");
+ const duration = earlier.since(later, { largestUnit: "years", smallestUnit: "microseconds", roundingMode: "expand" });
+ TemporalHelpers.assertDuration(duration, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, "rounding down 1 ns balances to -2 years");
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/rounding-zero-year-month-week-length.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/rounding-zero-year-month-week-length.js
new file mode 100644
index 0000000000..8efd41b7f7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/rounding-zero-year-month-week-length.js
@@ -0,0 +1,34 @@
+// |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.zoneddatetime.prototype.since
+description: >
+ A malicious calendar resulting in a year, month, or week length of zero is
+ handled correctly
+info: |
+ RoundDuration
+ 10.z. If _oneYearDays_ = 0, throw a *RangeError* exception.
+ ...
+ 11.z. If _oneMonthDays_ = 0, throw a *RangeError* exception.
+ ...
+ 12.s. If _oneWeekDays_ = 0, throw a *RangeError* exception.
+features: [Temporal]
+---*/
+
+const cal = new class extends Temporal.Calendar {
+ dateAdd(date, duration, options) {
+ // Called several times, last call sets oneYear/Month/WeekDays to 0
+ return new Temporal.PlainDate(1970, 1, 1);
+ }
+}("iso8601");
+
+const dt1 = new Temporal.ZonedDateTime(0n, "UTC", cal);
+const dt2 = new Temporal.ZonedDateTime(365n * 86400_000_000_000n + 1n, "UTC", cal);
+
+assert.throws(RangeError, () => dt1.since(dt2, { smallestUnit: "years" }), "zero year length handled correctly");
+assert.throws(RangeError, () => dt1.since(dt2, { smallestUnit: "months" }), "zero month length handled correctly");
+assert.throws(RangeError, () => dt1.since(dt2, { smallestUnit: "weeks" }), "zero week length handled correctly");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingincrement-nan.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingincrement-nan.js
new file mode 100644
index 0000000000..274a5eb9fc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/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.zoneddatetime.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.zoneddatetime.prototype.since step 13:
+ 13. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*).
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+const later = new Temporal.ZonedDateTime(1_000_090_061_988_655_322n, "UTC");
+assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: NaN }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingincrement-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingincrement-non-integer.js
new file mode 100644
index 0000000000..d2b4198b43
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingincrement-non-integer.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.zoneddatetime.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.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+const later = new Temporal.ZonedDateTime(1_000_000_000_000_000_005n, "UTC");
+const result = later.since(earlier, { roundingIncrement: 2.5, roundingMode: "trunc" });
+TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, "roundingIncrement 2.5 truncates to 2");
+const result2 = later.since(earlier, { smallestUnit: "days", roundingIncrement: 1e9 + 0.5, roundingMode: "expand" });
+TemporalHelpers.assertDuration(result2, 0, 0, 0, 1e9, 0, 0, 0, 0, 0, 0, "roundingIncrement 1e9 + 0.5 truncates to 1e9");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingincrement-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingincrement-out-of-range.js
new file mode 100644
index 0000000000..055d9a8eae
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/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.zoneddatetime.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.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+const later = new Temporal.ZonedDateTime(1_000_000_000_000_000_005n, "UTC");
+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/ZonedDateTime/prototype/since/roundingincrement-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingincrement-undefined.js
new file mode 100644
index 0000000000..c0e260c6f0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/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.zoneddatetime.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.zoneddatetime.prototype.since step 13:
+ 13. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+const later = new Temporal.ZonedDateTime(1_000_090_061_988_655_322n, "UTC");
+
+const explicit = later.since(earlier, { roundingIncrement: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 0, 25, 1, 1, 1, 1, 1, "default roundingIncrement is 1");
+
+const implicit = later.since(earlier, {});
+TemporalHelpers.assertDuration(implicit, 0, 0, 0, 0, 25, 1, 1, 1, 1, 1, "default roundingIncrement is 1");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingincrement-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingincrement-wrong-type.js
new file mode 100644
index 0000000000..52e13eb6cb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/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.zoneddatetime.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.zoneddatetime.prototype.since step 13:
+ 13. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*).
+includes: [temporalHelpers.js, compareArray.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+const later = new Temporal.ZonedDateTime(1_000_090_061_988_655_322n, "UTC");
+
+TemporalHelpers.checkRoundingIncrementOptionWrongType(
+ (roundingIncrement) => later.since(earlier, { roundingIncrement }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 25, 1, 1, 1, 1, 1, descr),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 25, 1, 1, 1, 1, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-ceil.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-ceil.js
new file mode 100644
index 0000000000..ef202aead8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-ceil.js
@@ -0,0 +1,45 @@
+// |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.zoneddatetime.prototype.since
+description: Tests calculations with roundingMode "ceil".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1546935756_123_456_789n /* 2019-01-08T08:22:36.123456789+00:00 */, "UTC");
+const later = new Temporal.ZonedDateTime(1631018380_987_654_289n /* 2021-09-07T12:39:40.987654289+00:00 */, "UTC");
+
+const expected = [
+ ["years", [3], [-2]],
+ ["months", [0, 32], [0, -31]],
+ ["weeks", [0, 0, 140], [0, 0, -139]],
+ ["days", [0, 0, 0, 974], [0, 0, 0, -973]],
+ ["hours", [0, 0, 0, 0, 23357], [0, 0, 0, 0, -23356]],
+ ["minutes", [0, 0, 0, 0, 23356, 18], [0, 0, 0, 0, -23356, -17]],
+ ["seconds", [0, 0, 0, 0, 23356, 17, 5], [0, 0, 0, 0, -23356, -17, -4]],
+ ["milliseconds", [0, 0, 0, 0, 23356, 17, 4, 865], [0, 0, 0, 0, -23356, -17, -4, -864]],
+ ["microseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 198], [0, 0, 0, 0, -23356, -17, -4, -864, -197]],
+ ["nanoseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 197, 500], [0, 0, 0, 0, -23356, -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/ZonedDateTime/prototype/since/roundingmode-expand.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-expand.js
new file mode 100644
index 0000000000..81759f95bc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-expand.js
@@ -0,0 +1,45 @@
+// |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.zoneddatetime.prototype.since
+description: Tests calculations with roundingMode "expand".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1546935756_123_456_789n /* 2019-01-08T08:22:36.123456789+00:00 */, "UTC");
+const later = new Temporal.ZonedDateTime(1631018380_987_654_289n /* 2021-09-07T12:39:40.987654289+00:00 */, "UTC");
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [0, 32], [0, -32]],
+ ["weeks", [0, 0, 140], [0, 0, -140]],
+ ["days", [0, 0, 0, 974], [0, 0, 0, -974]],
+ ["hours", [0, 0, 0, 0, 23357], [0, 0, 0, 0, -23357]],
+ ["minutes", [0, 0, 0, 0, 23356, 18], [0, 0, 0, 0, -23356, -18]],
+ ["seconds", [0, 0, 0, 0, 23356, 17, 5], [0, 0, 0, 0, -23356, -17, -5]],
+ ["milliseconds", [0, 0, 0, 0, 23356, 17, 4, 865], [0, 0, 0, 0, -23356, -17, -4, -865]],
+ ["microseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 198], [0, 0, 0, 0, -23356, -17, -4, -864, -198]],
+ ["nanoseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 197, 500], [0, 0, 0, 0, -23356, -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/ZonedDateTime/prototype/since/roundingmode-floor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-floor.js
new file mode 100644
index 0000000000..f5ec3ecb6e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-floor.js
@@ -0,0 +1,45 @@
+// |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.zoneddatetime.prototype.since
+description: Tests calculations with roundingMode "floor".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1546935756_123_456_789n /* 2019-01-08T08:22:36.123456789+00:00 */, "UTC");
+const later = new Temporal.ZonedDateTime(1631018380_987_654_289n /* 2021-09-07T12:39:40.987654289+00:00 */, "UTC");
+
+const expected = [
+ ["years", [2], [-3]],
+ ["months", [0, 31], [0, -32]],
+ ["weeks", [0, 0, 139], [0, 0, -140]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -974]],
+ ["hours", [0, 0, 0, 0, 23356], [0, 0, 0, 0, -23357]],
+ ["minutes", [0, 0, 0, 0, 23356, 17], [0, 0, 0, 0, -23356, -18]],
+ ["seconds", [0, 0, 0, 0, 23356, 17, 4], [0, 0, 0, 0, -23356, -17, -5]],
+ ["milliseconds", [0, 0, 0, 0, 23356, 17, 4, 864], [0, 0, 0, 0, -23356, -17, -4, -865]],
+ ["microseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 197], [0, 0, 0, 0, -23356, -17, -4, -864, -198]],
+ ["nanoseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 197, 500], [0, 0, 0, 0, -23356, -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/ZonedDateTime/prototype/since/roundingmode-halfCeil.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-halfCeil.js
new file mode 100644
index 0000000000..fa6428bd02
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-halfCeil.js
@@ -0,0 +1,45 @@
+// |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.zoneddatetime.prototype.since
+description: Tests calculations with roundingMode "halfCeil".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1546935756_123_456_789n /* 2019-01-08T08:22:36.123456789+00:00 */, "UTC");
+const later = new Temporal.ZonedDateTime(1631018380_987_654_289n /* 2021-09-07T12:39:40.987654289+00:00 */, "UTC");
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [0, 32], [0, -32]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+ ["hours", [0, 0, 0, 0, 23356], [0, 0, 0, 0, -23356]],
+ ["minutes", [0, 0, 0, 0, 23356, 17], [0, 0, 0, 0, -23356, -17]],
+ ["seconds", [0, 0, 0, 0, 23356, 17, 5], [0, 0, 0, 0, -23356, -17, -5]],
+ ["milliseconds", [0, 0, 0, 0, 23356, 17, 4, 864], [0, 0, 0, 0, -23356, -17, -4, -864]],
+ ["microseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 198], [0, 0, 0, 0, -23356, -17, -4, -864, -197]],
+ ["nanoseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 197, 500], [0, 0, 0, 0, -23356, -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/ZonedDateTime/prototype/since/roundingmode-halfEven.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-halfEven.js
new file mode 100644
index 0000000000..9058ecd380
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-halfEven.js
@@ -0,0 +1,45 @@
+// |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.zoneddatetime.prototype.since
+description: Tests calculations with roundingMode "halfEven".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1546935756_123_456_789n /* 2019-01-08T08:22:36.123456789+00:00 */, "UTC");
+const later = new Temporal.ZonedDateTime(1631018380_987_654_289n /* 2021-09-07T12:39:40.987654289+00:00 */, "UTC");
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [0, 32], [0, -32]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+ ["hours", [0, 0, 0, 0, 23356], [0, 0, 0, 0, -23356]],
+ ["minutes", [0, 0, 0, 0, 23356, 17], [0, 0, 0, 0, -23356, -17]],
+ ["seconds", [0, 0, 0, 0, 23356, 17, 5], [0, 0, 0, 0, -23356, -17, -5]],
+ ["milliseconds", [0, 0, 0, 0, 23356, 17, 4, 864], [0, 0, 0, 0, -23356, -17, -4, -864]],
+ ["microseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 198], [0, 0, 0, 0, -23356, -17, -4, -864, -198]],
+ ["nanoseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 197, 500], [0, 0, 0, 0, -23356, -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/ZonedDateTime/prototype/since/roundingmode-halfExpand.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-halfExpand.js
new file mode 100644
index 0000000000..7c35b8f8d1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-halfExpand.js
@@ -0,0 +1,45 @@
+// |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.zoneddatetime.prototype.since
+description: Tests calculations with roundingMode "halfExpand".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1546935756_123_456_789n /* 2019-01-08T08:22:36.123456789+00:00 */, "UTC");
+const later = new Temporal.ZonedDateTime(1631018380_987_654_289n /* 2021-09-07T12:39:40.987654289+00:00 */, "UTC");
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [0, 32], [0, -32]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+ ["hours", [0, 0, 0, 0, 23356], [0, 0, 0, 0, -23356]],
+ ["minutes", [0, 0, 0, 0, 23356, 17], [0, 0, 0, 0, -23356, -17]],
+ ["seconds", [0, 0, 0, 0, 23356, 17, 5], [0, 0, 0, 0, -23356, -17, -5]],
+ ["milliseconds", [0, 0, 0, 0, 23356, 17, 4, 864], [0, 0, 0, 0, -23356, -17, -4, -864]],
+ ["microseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 198], [0, 0, 0, 0, -23356, -17, -4, -864, -198]],
+ ["nanoseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 197, 500], [0, 0, 0, 0, -23356, -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/ZonedDateTime/prototype/since/roundingmode-halfFloor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-halfFloor.js
new file mode 100644
index 0000000000..f781610ec5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-halfFloor.js
@@ -0,0 +1,45 @@
+// |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.zoneddatetime.prototype.since
+description: Tests calculations with roundingMode "halfFloor".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1546935756_123_456_789n /* 2019-01-08T08:22:36.123456789+00:00 */, "UTC");
+const later = new Temporal.ZonedDateTime(1631018380_987_654_289n /* 2021-09-07T12:39:40.987654289+00:00 */, "UTC");
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [0, 32], [0, -32]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+ ["hours", [0, 0, 0, 0, 23356], [0, 0, 0, 0, -23356]],
+ ["minutes", [0, 0, 0, 0, 23356, 17], [0, 0, 0, 0, -23356, -17]],
+ ["seconds", [0, 0, 0, 0, 23356, 17, 5], [0, 0, 0, 0, -23356, -17, -5]],
+ ["milliseconds", [0, 0, 0, 0, 23356, 17, 4, 864], [0, 0, 0, 0, -23356, -17, -4, -864]],
+ ["microseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 197], [0, 0, 0, 0, -23356, -17, -4, -864, -198]],
+ ["nanoseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 197, 500], [0, 0, 0, 0, -23356, -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/ZonedDateTime/prototype/since/roundingmode-halfTrunc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-halfTrunc.js
new file mode 100644
index 0000000000..3277cae514
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-halfTrunc.js
@@ -0,0 +1,45 @@
+// |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.zoneddatetime.prototype.since
+description: Tests calculations with roundingMode "halfTrunc".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1546935756_123_456_789n /* 2019-01-08T08:22:36.123456789+00:00 */, "UTC");
+const later = new Temporal.ZonedDateTime(1631018380_987_654_289n /* 2021-09-07T12:39:40.987654289+00:00 */, "UTC");
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [0, 32], [0, -32]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+ ["hours", [0, 0, 0, 0, 23356], [0, 0, 0, 0, -23356]],
+ ["minutes", [0, 0, 0, 0, 23356, 17], [0, 0, 0, 0, -23356, -17]],
+ ["seconds", [0, 0, 0, 0, 23356, 17, 5], [0, 0, 0, 0, -23356, -17, -5]],
+ ["milliseconds", [0, 0, 0, 0, 23356, 17, 4, 864], [0, 0, 0, 0, -23356, -17, -4, -864]],
+ ["microseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 197], [0, 0, 0, 0, -23356, -17, -4, -864, -197]],
+ ["nanoseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 197, 500], [0, 0, 0, 0, -23356, -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/ZonedDateTime/prototype/since/roundingmode-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-invalid-string.js
new file mode 100644
index 0000000000..4f2b40168b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/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.zoneddatetime.prototype.since
+description: RangeError thrown when roundingMode option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+const later = new Temporal.ZonedDateTime(1_000_090_061_123_987_500n, "UTC");
+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/ZonedDateTime/prototype/since/roundingmode-trunc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-trunc.js
new file mode 100644
index 0000000000..10bfe6c805
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-trunc.js
@@ -0,0 +1,45 @@
+// |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.zoneddatetime.prototype.since
+description: Tests calculations with roundingMode "trunc".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1546935756_123_456_789n /* 2019-01-08T08:22:36.123456789+00:00 */, "UTC");
+const later = new Temporal.ZonedDateTime(1631018380_987_654_289n /* 2021-09-07T12:39:40.987654289+00:00 */, "UTC");
+
+const expected = [
+ ["years", [2], [-2]],
+ ["months", [0, 31], [0, -31]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+ ["hours", [0, 0, 0, 0, 23356], [0, 0, 0, 0, -23356]],
+ ["minutes", [0, 0, 0, 0, 23356, 17], [0, 0, 0, 0, -23356, -17]],
+ ["seconds", [0, 0, 0, 0, 23356, 17, 4], [0, 0, 0, 0, -23356, -17, -4]],
+ ["milliseconds", [0, 0, 0, 0, 23356, 17, 4, 864], [0, 0, 0, 0, -23356, -17, -4, -864]],
+ ["microseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 197], [0, 0, 0, 0, -23356, -17, -4, -864, -197]],
+ ["nanoseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 197, 500], [0, 0, 0, 0, -23356, -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/ZonedDateTime/prototype/since/roundingmode-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-undefined.js
new file mode 100644
index 0000000000..a086ded204
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-undefined.js
@@ -0,0 +1,30 @@
+// |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.zoneddatetime.prototype.since
+description: Fallback value for roundingMode option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+const later = new Temporal.ZonedDateTime(1_000_090_061_123_987_500n, "UTC");
+
+const explicit1 = later.since(earlier, { smallestUnit: "microsecond", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit1, 0, 0, 0, 0, 25, 1, 1, 123, 987, 0, "default roundingMode is trunc");
+const implicit1 = later.since(earlier, { smallestUnit: "microsecond" });
+TemporalHelpers.assertDuration(implicit1, 0, 0, 0, 0, 25, 1, 1, 123, 987, 0, "default roundingMode is trunc");
+
+const explicit2 = later.since(earlier, { smallestUnit: "millisecond", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit2, 0, 0, 0, 0, 25, 1, 1, 123, 0, 0, "default roundingMode is trunc");
+const implicit2 = later.since(earlier, { smallestUnit: "millisecond" });
+TemporalHelpers.assertDuration(implicit2, 0, 0, 0, 0, 25, 1, 1, 123, 0, 0, "default roundingMode is trunc");
+
+const explicit3 = later.since(earlier, { smallestUnit: "second", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit3, 0, 0, 0, 0, 25, 1, 1, 0, 0, 0, "default roundingMode is trunc");
+const implicit3 = later.since(earlier, { smallestUnit: "second" });
+TemporalHelpers.assertDuration(implicit3, 0, 0, 0, 0, 25, 1, 1, 0, 0, 0, "default roundingMode is trunc");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-wrong-type.js
new file mode 100644
index 0000000000..00c84cfdba
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/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.zoneddatetime.prototype.since
+description: Type conversions for roundingMode option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+const later = new Temporal.ZonedDateTime(1_000_090_061_123_987_500n, "UTC");
+TemporalHelpers.checkStringOptionWrongType("roundingMode", "trunc",
+ (roundingMode) => later.since(earlier, { smallestUnit: "microsecond", roundingMode }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 25, 1, 1, 123, 987, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/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/ZonedDateTime/prototype/since/smallestunit-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/smallestunit-invalid-string.js
new file mode 100644
index 0000000000..b27a214e85
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/smallestunit-invalid-string.js
@@ -0,0 +1,31 @@
+// |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.zoneddatetime.prototype.since
+description: RangeError thrown when smallestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+const later = new Temporal.ZonedDateTime(1_000_090_061_987_654_321n, "UTC");
+const badValues = [
+ "era",
+ "eraYear",
+ "millisecond\0",
+ "mill\u0131second",
+ "SECOND",
+ "eras",
+ "eraYears",
+ "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/ZonedDateTime/prototype/since/smallestunit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/smallestunit-plurals-accepted.js
new file mode 100644
index 0000000000..f6985998f7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/smallestunit-plurals-accepted.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.zoneddatetime.prototype.since
+description: Plural units are accepted as well for the smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+const later = new Temporal.ZonedDateTime(1_086_403_661_988_655_322n, "UTC");
+const validUnits = [
+ "year",
+ "month",
+ "week",
+ "day",
+ "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/ZonedDateTime/prototype/since/smallestunit-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/smallestunit-undefined.js
new file mode 100644
index 0000000000..5b7863939a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/smallestunit-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.zoneddatetime.prototype.since
+description: Fallback value for smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+const later = new Temporal.ZonedDateTime(1_000_090_061_987_654_321n, "UTC");
+
+const explicit = later.since(earlier, { smallestUnit: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 0, 25, 1, 1, 987, 654, 321, "default smallestUnit is nanosecond");
+const implicit = later.since(earlier, {});
+TemporalHelpers.assertDuration(implicit, 0, 0, 0, 0, 25, 1, 1, 987, 654, 321, "default smallestUnit is nanosecond");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/smallestunit-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/smallestunit-wrong-type.js
new file mode 100644
index 0000000000..15876eff64
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/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.zoneddatetime.prototype.since
+description: Type conversions for smallestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+const later = new Temporal.ZonedDateTime(1_000_090_061_987_654_321n, "UTC");
+TemporalHelpers.checkStringOptionWrongType("smallestUnit", "microsecond",
+ (smallestUnit) => later.since(earlier, { smallestUnit }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 25, 1, 1, 987, 654, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..6f44690729
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/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.zoneddatetime.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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const other = new Temporal.ZonedDateTime(1_100_000_000_123_456_789n, timeZone);
+ assert.throws(RangeError, () => datetime.since(other, { largestUnit: "days" }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..6fd71013ab
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/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.zoneddatetime.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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const other = new Temporal.ZonedDateTime(1_100_000_000_123_456_789n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => datetime.since(other, { largestUnit: "days" }),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..23b6e8bb60
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/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.zoneddatetime.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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const other = new Temporal.ZonedDateTime(1_100_000_000_123_456_789n, timeZone);
+ assert.throws(RangeError, () => datetime.since(other, { largestUnit: "days" }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..94158e7004
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/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.zoneddatetime.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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const other = new Temporal.ZonedDateTime(1_100_000_000_123_456_789n, timeZone);
+ assert.throws(TypeError, () => datetime.since(other, { largestUnit: "days" }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/timezone-getpossibleinstantsfor-iterable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/timezone-getpossibleinstantsfor-iterable.js
new file mode 100644
index 0000000000..17d5b5f84a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/timezone-getpossibleinstantsfor-iterable.js
@@ -0,0 +1,41 @@
+// |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.zoneddatetime.prototype.since
+description: An iterable returned from timeZone.getPossibleInstantsFor is consumed after each call
+info: |
+ sec-temporal.zoneddatetime.prototype.since step 3:
+ 3. Set _other_ to ? ToTemporalZonedDateTime(_other_).
+ sec-temporal-totemporalzoneddatetime step 7:
+ 7. Let _epochNanoseconds_ be ? InterpretISODateTimeOffset(_result_.[[Year]], [...], _result_.[[Nanosecond]], _offsetNanoseconds_, _timeZone_, _disambiguation_, _offset_).
+ sec-temporal-interpretisodatetimeoffset step 7:
+ 7. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_).
+ sec-temporal-getpossibleinstantsfor step 2:
+ 2. Let _list_ be ? IterableToList(_possibleInstants_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+// Not called on the instance's time zone
+
+const expected1 = [];
+
+TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => {
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, timeZone);
+ datetime.since({ year: 2005, month: 6, day: 2, timeZone: "UTC" });
+}, expected1);
+
+// Called on the argument's time zone
+
+const expected2 = [
+ "2005-06-02T00:00:00",
+];
+
+TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => {
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+ datetime.since({ year: 2005, month: 6, day: 2, timeZone });
+}, expected2);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/year-zero.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/year-zero.js
new file mode 100644
index 0000000000..e1063a41b0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/year-zero.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.zoneddatetime.prototype.since
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-0000000-01-01T00:02Z[UTC]",
+ "-0000000-01-01T00:02+00:00[UTC]",
+ "-0000000-01-01T00:02:00.000000000+00:00[UTC]",
+];
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.since(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/zoneddatetime-string-multiple-offsets.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/zoneddatetime-string-multiple-offsets.js
new file mode 100644
index 0000000000..779537326e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/zoneddatetime-string-multiple-offsets.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.zoneddatetime.prototype.since
+description: Sub-minute offset trailing zeroes allowed in ISO string but not in bracketed offset
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("+01:35");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+let str = "1970-01-01T01:35:30+01:35:00.000000000[+01:35]";
+
+const result = instance.since(str);
+TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, -30, 0, 0, 0, "ISO offset, sub-minute offset trailing-zeroes");
+
+str = "1970-01-01T01:35:30+01:35:00.000000000[+01:35:00.000000000]";
+assert.throws(
+ RangeError,
+ () => instance.since(str),
+ "Trailing zeroes not allowed for sub-minute time zone identifiers"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/zoneddatetime-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/zoneddatetime-string.js
new file mode 100644
index 0000000000..a11ad11aa0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/zoneddatetime-string.js
@@ -0,0 +1,37 @@
+// |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.zoneddatetime.prototype.since
+description: Conversion of ISO date-time strings to Temporal.ZonedDateTime instances
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+
+let str = "1970-01-01T00:00";
+assert.throws(RangeError, () => instance.since(str), "bare date-time string is not a ZonedDateTime");
+str = "1970-01-01T00:00Z";
+assert.throws(RangeError, () => instance.since(str), "date-time + Z is not a ZonedDateTime");
+str = "1970-01-01T00:00+01:00";
+assert.throws(RangeError, () => instance.since(str), "date-time + offset is not a ZonedDateTime");
+
+str = "1970-01-01T00:00[+01:00]";
+const result1 = instance.since(str);
+TemporalHelpers.assertDuration(result1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, "date-time + IANA annotation preserves wall time in the time zone");
+
+str = "1970-01-01T00:00Z[+01:00]";
+const result2 = instance.since(str);
+TemporalHelpers.assertDuration(result2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "date-time + Z + IANA annotation preserves exact time in the time zone");
+
+str = "1970-01-01T00:00+01:00[+01:00]";
+const result3 = instance.since(str);
+TemporalHelpers.assertDuration(result3, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, "date-time + offset + IANA annotation ensures both exact and wall time match");
+
+str = "1970-01-01T00:00-04:15[+01:00]";
+assert.throws(RangeError, () => instance.since(str), "date-time + offset + IANA annotation throws if wall time and exact time mismatch");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/branding.js
new file mode 100644
index 0000000000..4fe873fadc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/branding.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.zoneddatetime.prototype.startofday
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const startOfDay = Temporal.ZonedDateTime.prototype.startOfDay;
+
+assert.sameValue(typeof startOfDay, "function");
+
+assert.throws(TypeError, () => startOfDay.call(undefined), "undefined");
+assert.throws(TypeError, () => startOfDay.call(null), "null");
+assert.throws(TypeError, () => startOfDay.call(true), "true");
+assert.throws(TypeError, () => startOfDay.call(""), "empty string");
+assert.throws(TypeError, () => startOfDay.call(Symbol()), "symbol");
+assert.throws(TypeError, () => startOfDay.call(1), "1");
+assert.throws(TypeError, () => startOfDay.call({}), "plain object");
+assert.throws(TypeError, () => startOfDay.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime");
+assert.throws(TypeError, () => startOfDay.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/builtin-timezone-no-observable-calls.js
new file mode 100644
index 0000000000..b2ccc52635
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/builtin-timezone-no-observable-calls.js
@@ -0,0 +1,37 @@
+// |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.zoneddatetime.prototype.startofday
+description: >
+ Calling the method on an instance constructed with a builtin time zone causes
+ no observable lookups or calls to time zone methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+const getPossibleInstantsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getPossibleInstantsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.startOfDay();
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", getPossibleInstantsForOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/builtin.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/builtin.js
new file mode 100644
index 0000000000..e67c761fbb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/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.zoneddatetime.prototype.startofday
+description: >
+ Tests that Temporal.ZonedDateTime.prototype.startOfDay
+ 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.ZonedDateTime.prototype.startOfDay),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.startOfDay),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.startOfDay),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.ZonedDateTime.prototype.startOfDay.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/getpossibleinstantsfor-called-with-iso8601-calendar.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/getpossibleinstantsfor-called-with-iso8601-calendar.js
new file mode 100644
index 0000000000..247f94a19d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/getpossibleinstantsfor-called-with-iso8601-calendar.js
@@ -0,0 +1,58 @@
+// |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.zoneddatetime.prototype.startofday
+description: >
+ Time zone's getPossibleInstantsFor is called with a PlainDateTime with the
+ built-in ISO 8601 calendar
+features: [Temporal]
+info: |
+ DisambiguatePossibleInstants:
+ 2. Let _n_ be _possibleInstants_'s length.
+ ...
+ 5. Assert: _n_ = 0.
+ ...
+ 19. If _disambiguation_ is *"earlier"*, then
+ ...
+ c. Let _earlierDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*).
+ d. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _earlierDateTime_).
+ ...
+ 20. Assert: _disambiguation_ is *"compatible"* or *"later"*.
+ ...
+ 23. Let _laterDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*).
+ 24. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _laterDateTime_).
+---*/
+
+class SkippedDateTime extends Temporal.TimeZone {
+ constructor() {
+ super("UTC");
+ this.calls = 0;
+ }
+
+ getPossibleInstantsFor(dateTime) {
+ // Calls occur in pairs. For the first one return no possible instants so
+ // that DisambiguatePossibleInstants will call it again
+ if (this.calls++ % 2 == 0) {
+ return [];
+ }
+
+ assert.sameValue(
+ dateTime.getISOFields().calendar,
+ "iso8601",
+ "getPossibleInstantsFor called with dateTime with built-in ISO 8601 calendar"
+ );
+ return super.getPossibleInstantsFor(dateTime);
+ }
+}
+
+const nonBuiltinISOCalendar = new Temporal.Calendar("iso8601");
+const timeZone = new SkippedDateTime();
+
+const instance = new Temporal.ZonedDateTime(0n, timeZone, nonBuiltinISOCalendar);
+instance.startOfDay();
+
+assert.sameValue(timeZone.calls, 2, "getPossibleInstantsFor should have been called 2 times");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/length.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/length.js
new file mode 100644
index 0000000000..9a93ac0f2e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/length.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.zoneddatetime.prototype.startofday
+description: Temporal.ZonedDateTime.prototype.startOfDay.length is 0
+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.ZonedDateTime.prototype.startOfDay, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/name.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/name.js
new file mode 100644
index 0000000000..72dd5b4b55
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/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.zoneddatetime.prototype.startofday
+description: Temporal.ZonedDateTime.prototype.startOfDay.name is "startOfDay".
+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.ZonedDateTime.prototype.startOfDay, "name", {
+ value: "startOfDay",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/not-a-constructor.js
new file mode 100644
index 0000000000..ca1bd66664
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/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.zoneddatetime.prototype.startofday
+description: >
+ Temporal.ZonedDateTime.prototype.startOfDay 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.ZonedDateTime.prototype.startOfDay();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.startOfDay), false,
+ "isConstructor(Temporal.ZonedDateTime.prototype.startOfDay)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/order-of-operations.js
new file mode 100644
index 0000000000..d41ddce253
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/order-of-operations.js
@@ -0,0 +1,69 @@
+// |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.zoneddatetime.prototype.startofday
+description: User code calls happen in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ // lookup
+ "get this.timeZone.getOffsetNanosecondsFor",
+ "get this.timeZone.getPossibleInstantsFor",
+ // GetPlainDateTimeFor
+ "call this.timeZone.getOffsetNanosecondsFor",
+ // GetInstantFor on preceding midnight
+ "call this.timeZone.getPossibleInstantsFor",
+];
+const actual = [];
+
+const calendar = TemporalHelpers.calendarObserver(actual, "this.calendar");
+const instance = new Temporal.ZonedDateTime(
+ 1_000_000_000_000_000_000n,
+ TemporalHelpers.timeZoneObserver(actual, "this.timeZone"),
+ calendar,
+);
+
+const fallBackTimeZone = TemporalHelpers.oneShiftTimeZone(Temporal.Instant.fromEpochSeconds(1800), -3600_000_000_000);
+const fallBackInstance = new Temporal.ZonedDateTime(
+ 0n,
+ TemporalHelpers.timeZoneObserver(actual, "this.timeZone", {
+ getOffsetNanosecondsFor: fallBackTimeZone.getOffsetNanosecondsFor.bind(fallBackTimeZone),
+ getPossibleInstantsFor: fallBackTimeZone.getPossibleInstantsFor.bind(fallBackTimeZone),
+ }),
+ calendar,
+);
+const springForwardTimeZone = TemporalHelpers.oneShiftTimeZone(Temporal.Instant.fromEpochSeconds(-1800), 3600_000_000_000);
+const springForwardInstance = new Temporal.ZonedDateTime(
+ 0n,
+ TemporalHelpers.timeZoneObserver(actual, "this.timeZone", {
+ getOffsetNanosecondsFor: springForwardTimeZone.getOffsetNanosecondsFor.bind(springForwardTimeZone),
+ getPossibleInstantsFor: springForwardTimeZone.getPossibleInstantsFor.bind(springForwardTimeZone),
+ }),
+ calendar,
+);
+// clear any observable operations that happen due to time zone or calendar
+// calls in the constructors
+actual.splice(0);
+
+instance.startOfDay();
+assert.compareArray(actual, expected, "order of operations");
+actual.splice(0); // clear
+
+fallBackInstance.startOfDay();
+assert.compareArray(actual, expected, "order of operations with preceding midnight at repeated wall-clock time");
+actual.splice(0); // clear
+
+springForwardInstance.startOfDay();
+assert.compareArray(actual, expected.concat([
+ // DisambiguatePossibleInstants
+ "call this.timeZone.getOffsetNanosecondsFor",
+ "call this.timeZone.getOffsetNanosecondsFor",
+ "call this.timeZone.getPossibleInstantsFor",
+]), "order of operations with preceding midnight at skipped wall-clock time");
+actual.splice(0); // clear
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/prop-desc.js
new file mode 100644
index 0000000000..4a674d1619
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/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.zoneddatetime.prototype.startofday
+description: The "startOfDay" property of Temporal.ZonedDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.ZonedDateTime.prototype.startOfDay,
+ "function",
+ "`typeof ZonedDateTime.prototype.startOfDay` is `function`"
+);
+
+verifyProperty(Temporal.ZonedDateTime.prototype, "startOfDay", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/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/ZonedDateTime/prototype/startOfDay/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/subclassing-ignored.js
new file mode 100644
index 0000000000..b4e0ebccfc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/subclassing-ignored.js
@@ -0,0 +1,31 @@
+// |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.zoneddatetime.prototype.startofday
+description: Objects of a subclass are never created as return values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.ZonedDateTime,
+ [88000_123_456_789n, "UTC"],
+ "startOfDay",
+ [],
+ (result) => {
+ assert.sameValue(result.epochNanoseconds, 86400_000_000_000n, "epochNanoseconds result");
+ assert.sameValue(result.year, 1970, "year result");
+ assert.sameValue(result.month, 1, "month result");
+ assert.sameValue(result.day, 2, "day result");
+ assert.sameValue(result.hour, 0, "hour result");
+ assert.sameValue(result.minute, 0, "minute result");
+ assert.sameValue(result.second, 0, "second result");
+ assert.sameValue(result.millisecond, 0, "millisecond result");
+ assert.sameValue(result.microsecond, 0, "microsecond result");
+ assert.sameValue(result.nanosecond, 0, "nanosecond result");
+ },
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..6dcfa91513
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,18 @@
+// |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.zoneddatetime.prototype.startofday
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.startOfDay());
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..cce770ce1d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/timezone-getoffsetnanosecondsfor-not-callable.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.zoneddatetime.prototype.startofday
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => datetime.startOfDay(),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..6f9d7d472c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,18 @@
+// |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.zoneddatetime.prototype.startofday
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.startOfDay());
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..494b7a1b70
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/timezone-getoffsetnanosecondsfor-wrong-type.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.zoneddatetime.prototype.startofday
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.startOfDay());
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/argument-duration-max.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/argument-duration-max.js
new file mode 100644
index 0000000000..c8590dc610
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/argument-duration-max.js
@@ -0,0 +1,57 @@
+// |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.zoneddatetime.prototype.subtract
+description: Maximum allowed duration
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(0n, "UTC");
+
+const maxCases = [
+ ["P273790Y8M11D", "string with max years"],
+ [{ years: 273790, months: 8, days: 11 }, "property bag with max years"],
+ ["P3285488M11D", "string with max months"],
+ [{ months: 3285488, days: 11 }, "property bag with max months"],
+ ["P14285714W2D", "string with max weeks"],
+ [{ weeks: 14285714, days: 2 }, "property bag with max weeks"],
+ ["P100000000D", "string with max days"],
+ [{ days: 100000000 }, "property bag with max days"],
+ ["PT2400000000H", "string with max hours"],
+ [{ hours: 2400000000 }, "property bag with max hours"],
+ ["PT144000000000M", "string with max minutes"],
+ [{ minutes: 144000000000 }, "property bag with max minutes"],
+ ["PT8640000000000S", "string with max seconds"],
+ [{ seconds: 8640000000000 }, "property bag with max seconds"],
+];
+
+for (const [arg, descr] of maxCases) {
+ const result = instance.subtract(arg);
+ assert.sameValue(result.epochNanoseconds, -8640000000000000000000n, `operation succeeds with ${descr}`);
+}
+
+const minCases = [
+ ["-P273790Y8M12D", "string with min years"],
+ [{ years: -273790, months: -8, days: -12 }, "property bag with min years"],
+ ["-P3285488M12D", "string with min months"],
+ [{ months: -3285488, days: -12 }, "property bag with min months"],
+ ["-P14285714W2D", "string with min weeks"],
+ [{ weeks: -14285714, days: -2 }, "property bag with min weeks"],
+ ["-P100000000D", "string with min days"],
+ [{ days: -100000000 }, "property bag with min days"],
+ ["-PT2400000000H", "string with min hours"],
+ [{ hours: -2400000000 }, "property bag with min hours"],
+ ["-PT144000000000M", "string with min minutes"],
+ [{ minutes: -144000000000 }, "property bag with min minutes"],
+ ["-PT8640000000000S", "string with min seconds"],
+ [{ seconds: -8640000000000 }, "property bag with min seconds"],
+];
+
+for (const [arg, descr] of minCases) {
+ const result = instance.subtract(arg);
+ assert.sameValue(result.epochNanoseconds, 8640000000000000000000n, `operation succeeds with ${descr}`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/argument-duration-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/argument-duration-out-of-range.js
new file mode 100644
index 0000000000..969ed8b5b9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/argument-duration-out-of-range.js
@@ -0,0 +1,75 @@
+// |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.zoneddatetime.prototype.subtract
+description: Duration-like argument that is out of range
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(0n, "UTC");
+
+const cases = [
+ // 2^32 = 4294967296
+ ["P4294967296Y", "string with years > max"],
+ [{ years: 4294967296 }, "property bag with years > max"],
+ ["-P4294967296Y", "string with years < min"],
+ [{ years: -4294967296 }, "property bag with years < min"],
+ ["P4294967296M", "string with months > max"],
+ [{ months: 4294967296 }, "property bag with months > max"],
+ ["-P4294967296M", "string with months < min"],
+ [{ months: -4294967296 }, "property bag with months < min"],
+ ["P4294967296W", "string with weeks > max"],
+ [{ weeks: 4294967296 }, "property bag with weeks > max"],
+ ["-P4294967296W", "string with weeks < min"],
+ [{ weeks: -4294967296 }, "property bag with weeks < min"],
+
+ // ceil(max safe integer / 86400) = 104249991375
+ ["P104249991375D", "string with days > max"],
+ [{ days: 104249991375 }, "property bag with days > max"],
+ ["P104249991374DT24H", "string where hours balance into days > max"],
+ [{ days: 104249991374, hours: 24 }, "property bag where hours balance into days > max"],
+ ["-P104249991375D", "string with days < min"],
+ [{ days: -104249991375 }, "property bag with days < min"],
+ ["-P104249991374DT24H", "string where hours balance into days < min"],
+ [{ days: -104249991374, hours: -24 }, "property bag where hours balance into days < min"],
+
+ // ceil(max safe integer / 3600) = 2501999792984
+ ["PT2501999792984H", "string with hours > max"],
+ [{ hours: 2501999792984 }, "property bag with hours > max"],
+ ["PT2501999792983H60M", "string where minutes balance into hours > max"],
+ [{ hours: 2501999792983, minutes: 60 }, "property bag where minutes balance into hours > max"],
+ ["-PT2501999792984H", "string with hours < min"],
+ [{ hours: -2501999792984 }, "property bag with hours < min"],
+ ["-PT2501999792983H60M", "string where minutes balance into hours < min"],
+ [{ hours: -2501999792983, minutes: -60 }, "property bag where minutes balance into hours < min"],
+
+ // ceil(max safe integer / 60) = 150119987579017
+ ["PT150119987579017M", "string with minutes > max"],
+ [{ minutes: 150119987579017 }, "property bag with minutes > max"],
+ ["PT150119987579016M60S", "string where seconds balance into minutes > max"],
+ [{ minutes: 150119987579016, seconds: 60 }, "property bag where seconds balance into minutes > max"],
+ ["-PT150119987579017M", "string with minutes < min"],
+ [{ minutes: -150119987579017 }, "property bag with minutes < min"],
+ ["-PT150119987579016M60S", "string where seconds balance into minutes < min"],
+ [{ minutes: -150119987579016, seconds: -60 }, "property bag where seconds balance into minutes < min"],
+
+ // 2^53 = 9007199254740992
+ ["PT9007199254740992S", "string with seconds > max"],
+ [{ seconds: 9007199254740992 }, "property bag with seconds > max"],
+ [{ seconds: 9007199254740991, milliseconds: 1000 }, "property bag where milliseconds balance into seconds > max"],
+ [{ seconds: 9007199254740991, microseconds: 1000000 }, "property bag where microseconds balance into seconds > max"],
+ [{ seconds: 9007199254740991, nanoseconds: 1000000000 }, "property bag where nanoseconds balance into seconds > max"],
+ ["-PT9007199254740992S", "string with seconds < min"],
+ [{ seconds: -9007199254740992 }, "property bag with seconds < min"],
+ [{ seconds: -9007199254740991, milliseconds: -1000 }, "property bag where milliseconds balance into seconds < min"],
+ [{ seconds: -9007199254740991, microseconds: -1000000 }, "property bag where microseconds balance into seconds < min"],
+ [{ seconds: -9007199254740991, nanoseconds: -1000000000 }, "property bag where nanoseconds balance into seconds < min"],
+];
+
+for (const [arg, descr] of cases) {
+ assert.throws(RangeError, () => instance.subtract(arg), `${descr} is out of range`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/argument-invalid-property.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/argument-invalid-property.js
new file mode 100644
index 0000000000..f09c025bef
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/argument-invalid-property.js
@@ -0,0 +1,31 @@
+// |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.zoneddatetime.prototype.subtract
+description: temporalDurationLike object must contain at least one correctly spelled property
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+
+assert.throws(
+ TypeError,
+ () => instance.subtract({}),
+ "Throws TypeError if no property is present"
+);
+
+assert.throws(
+ TypeError,
+ () => instance.subtract({ nonsense: true }),
+ "Throws TypeError if no recognized property is present"
+);
+
+assert.throws(
+ TypeError,
+ () => instance.subtract({ sign: 1 }),
+ "Sign property is not recognized"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/argument-mixed-sign.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/argument-mixed-sign.js
new file mode 100644
index 0000000000..87dc248624
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/argument-mixed-sign.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.zoneddatetime.prototype.subtract
+description: Positive and negative values in the temporalDurationLike argument are not acceptable
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+
+["constrain", "reject"].forEach((overflow) => {
+ assert.throws(
+ RangeError,
+ () => instance.subtract({ hours: 1, minutes: -30 }, { overflow }),
+ `mixed positive and negative values always throw (overflow = "${overflow}")`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/argument-not-object.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/argument-not-object.js
new file mode 100644
index 0000000000..0c8dc80e23
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/argument-not-object.js
@@ -0,0 +1,22 @@
+// |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.zoneddatetime.prototype.subtract
+description: Passing a primitive other than string to subtract() throws
+features: [Symbol, Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+assert.throws(TypeError, () => instance.subtract(undefined), "undefined");
+assert.throws(TypeError, () => instance.subtract(null), "null");
+assert.throws(TypeError, () => instance.subtract(true), "boolean");
+assert.throws(RangeError, () => instance.subtract(""), "empty string");
+assert.throws(TypeError, () => instance.subtract(Symbol()), "Symbol");
+assert.throws(TypeError, () => instance.subtract(7), "number");
+assert.throws(TypeError, () => instance.subtract(7n), "bigint");
+assert.throws(TypeError, () => instance.subtract([]), "array");
+assert.throws(TypeError, () => instance.subtract(() => {}), "function");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/argument-singular-properties.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/argument-singular-properties.js
new file mode 100644
index 0000000000..416f794333
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/argument-singular-properties.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.zoneddatetime.prototype.subtract
+description: Singular properties in the property bag are always ignored
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+
+[
+ { year: 1 },
+ { month: 2 },
+ { week: 3 },
+ { day: 4 },
+ { hour: 5 },
+ { minute: 6 },
+ { second: 7 },
+ { millisecond: 8 },
+ { microsecond: 9 },
+ { nanosecond: 10 },
+].forEach((badObject) => {
+ assert.throws(TypeError, () => instance.subtract(badObject),
+ "Throw TypeError if temporalDurationLike is not valid");
+});
+
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/argument-string-fractional-units-rounding-mode.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/argument-string-fractional-units-rounding-mode.js
new file mode 100644
index 0000000000..88af40381c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/argument-string-fractional-units-rounding-mode.js
@@ -0,0 +1,18 @@
+// |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.zoneddatetime.prototype.subtract
+description: Strings with fractional duration units are rounded with the correct rounding mode
+features: [Temporal]
+---*/
+
+const epoch = new Temporal.ZonedDateTime(0n, "UTC");
+
+assert.sameValue(epoch.subtract("PT1.03125H").epochNanoseconds, -3712_500_000_000n,
+ "positive fractional units rounded with correct rounding mode");
+assert.sameValue(epoch.subtract("-PT1.03125H").epochNanoseconds, 3712_500_000_000n,
+ "negative fractional units rounded with correct rounding mode");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/argument-string-negative-fractional-units.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/argument-string-negative-fractional-units.js
new file mode 100644
index 0000000000..966cf660a2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/argument-string-negative-fractional-units.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.zoneddatetime.prototype.subtract
+description: Strings with fractional duration units are treated with the correct sign
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+
+const resultHours = instance.subtract("-PT24.567890123H");
+assert.sameValue(resultHours.epochNanoseconds, 1_000_088_444_404_442_800n, "negative fractional hours");
+
+const resultMinutes = instance.subtract("-PT1440.567890123M");
+assert.sameValue(resultMinutes.epochNanoseconds, 1_000_086_434_073_407_380n, "negative fractional minutes");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/branding.js
new file mode 100644
index 0000000000..e7b0cb2cdd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/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.zoneddatetime.prototype.subtract
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const subtract = Temporal.ZonedDateTime.prototype.subtract;
+
+assert.sameValue(typeof subtract, "function");
+
+const args = [new Temporal.Duration(5)];
+
+assert.throws(TypeError, () => subtract.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => subtract.apply(null, args), "null");
+assert.throws(TypeError, () => subtract.apply(true, args), "true");
+assert.throws(TypeError, () => subtract.apply("", args), "empty string");
+assert.throws(TypeError, () => subtract.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => subtract.apply(1, args), "1");
+assert.throws(TypeError, () => subtract.apply({}, args), "plain object");
+assert.throws(TypeError, () => subtract.apply(Temporal.ZonedDateTime, args), "Temporal.ZonedDateTime");
+assert.throws(TypeError, () => subtract.apply(Temporal.ZonedDateTime.prototype, args), "Temporal.ZonedDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..5938e6eb78
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.zoneddatetime.prototype.subtract
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dateAddOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dateAdd");
+Object.defineProperty(Temporal.Calendar.prototype, "dateAdd", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("dateAdd should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.subtract(new Temporal.Duration(1));
+
+Object.defineProperty(Temporal.Calendar.prototype, "dateAdd", dateAddOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/builtin-timezone-no-observable-calls.js
new file mode 100644
index 0000000000..fcca75fbfd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/builtin-timezone-no-observable-calls.js
@@ -0,0 +1,37 @@
+// |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.zoneddatetime.prototype.subtract
+description: >
+ Calling the method on an instance constructed with a builtin time zone causes
+ no observable lookups or calls to time zone methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+const getPossibleInstantsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getPossibleInstantsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.subtract(new Temporal.Duration(1));
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", getPossibleInstantsForOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/builtin.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/builtin.js
new file mode 100644
index 0000000000..f3c13cbee2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/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.zoneddatetime.prototype.subtract
+description: >
+ Tests that Temporal.ZonedDateTime.prototype.subtract
+ 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.ZonedDateTime.prototype.subtract),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.subtract),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.subtract),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.ZonedDateTime.prototype.subtract.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/calendar-dateadd.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/calendar-dateadd.js
new file mode 100644
index 0000000000..5a97b28c19
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/calendar-dateadd.js
@@ -0,0 +1,33 @@
+// |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.zoneddatetime.prototype.subtract
+description: ZonedDateTime.prototype.subtract should call dateAdd with the appropriate values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ dateAdd(plainDate, duration, options) {
+ ++calls;
+ TemporalHelpers.assertPlainDate(plainDate, 2020, 3, "M03", 14, "plainDate argument");
+ TemporalHelpers.assertDuration(duration, 0, -10, 0, 0, 0, 0, 0, 0, 0, 0, "duration argument");
+ assert.sameValue(typeof options, "object", "options argument: type");
+ assert.sameValue(Object.getPrototypeOf(options), null, "options argument: prototype");
+ return super.dateAdd(plainDate, duration, options);
+ }
+}
+
+const zonedDateTime = new Temporal.ZonedDateTime(1584189296_987654321n,
+ new Temporal.TimeZone("UTC"), new CustomCalendar());
+const result = zonedDateTime.subtract({ months: 10, hours: 14 });
+assert.sameValue(result.epochNanoseconds, 1557786896_987654321n);
+assert.sameValue(calls, 1, "should have called dateAdd");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/length.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/length.js
new file mode 100644
index 0000000000..53d8cf9f8a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/length.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.zoneddatetime.prototype.subtract
+description: Temporal.ZonedDateTime.prototype.subtract.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.ZonedDateTime.prototype.subtract, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/name.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/name.js
new file mode 100644
index 0000000000..999ed448fc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/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.zoneddatetime.prototype.subtract
+description: Temporal.ZonedDateTime.prototype.subtract.name is "subtract".
+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.ZonedDateTime.prototype.subtract, "name", {
+ value: "subtract",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/negative-epochnanoseconds.js
new file mode 100644
index 0000000000..28f3f2bbf9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/negative-epochnanoseconds.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.zoneddatetime.prototype.subtract
+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]
+---*/
+
+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 result = datetime.subtract(new Temporal.Duration(0, 0, 0, 1));
+assert.sameValue(result.epochNanoseconds, -13936164_999_999_999n);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/non-integer-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/non-integer-throws-rangeerror.js
new file mode 100644
index 0000000000..50f508601b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/non-integer-throws-rangeerror.js
@@ -0,0 +1,29 @@
+// |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.zoneddatetime.prototype.subtract
+description: A non-integer value for any recognized property in the property bag, throws a RangeError
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+const fields = [
+ "years",
+ "months",
+ "weeks",
+ "days",
+ "hours",
+ "minutes",
+ "seconds",
+ "milliseconds",
+ "microseconds",
+ "nanoseconds",
+];
+fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.subtract({ [field]: 1.5 }));
+ assert.throws(RangeError, () => instance.subtract({ [field]: -1.5 }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/not-a-constructor.js
new file mode 100644
index 0000000000..37bee46093
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/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.zoneddatetime.prototype.subtract
+description: >
+ Temporal.ZonedDateTime.prototype.subtract 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.ZonedDateTime.prototype.subtract();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.subtract), false,
+ "isConstructor(Temporal.ZonedDateTime.prototype.subtract)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/options-object.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/options-object.js
new file mode 100644
index 0000000000..432b9879d2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/options-object.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.zoneddatetime.prototype.subtract
+description: Empty or a function object may be used as options
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(0n, "UTC");
+
+const result1 = instance.subtract({ years: 1 }, {});
+assert.sameValue(
+ result1.epochNanoseconds, -31536000000000000n, "UTC",
+ "options may be an empty plain object"
+);
+
+const result2 = instance.subtract({ years: 1 }, () => {});
+assert.sameValue(
+ result2.epochNanoseconds, -31536000000000000n, "UTC",
+ "options may be a function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/options-undefined.js
new file mode 100644
index 0000000000..7fd18df8fe
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/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.zoneddatetime.prototype.subtract
+description: Verify that undefined options are handled correctly.
+features: [BigInt, Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(954506096_987_654_321n, "UTC");
+const duration = { months: 1 };
+
+const explicit = datetime.subtract(duration, undefined);
+assert.sameValue(explicit.month, 2, "default overflow is constrain");
+assert.sameValue(explicit.day, 29, "default overflow is constrain");
+
+const implicit = datetime.subtract(duration);
+assert.sameValue(implicit.month, 2, "default overflow is constrain");
+assert.sameValue(implicit.day, 29, "default overflow is constrain");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/options-wrong-type.js
new file mode 100644
index 0000000000..42e2ebe02d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/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.zoneddatetime.prototype.subtract
+description: TypeError thrown when options argument is a primitive
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const badOptions = [
+ null,
+ true,
+ "some string",
+ Symbol(),
+ 1,
+ 2n,
+];
+
+const instance = new Temporal.ZonedDateTime(0n, "UTC");
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.subtract({ years: 1 }, value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/order-of-operations.js
new file mode 100644
index 0000000000..84d7fb7a24
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/order-of-operations.js
@@ -0,0 +1,84 @@
+// |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.zoneddatetime.prototype.subtract
+description: Properties on objects passed to subtract() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ // ToTemporalDurationRecord
+ "get duration.days",
+ "get duration.days.valueOf",
+ "call duration.days.valueOf",
+ "get duration.hours",
+ "get duration.hours.valueOf",
+ "call duration.hours.valueOf",
+ "get duration.microseconds",
+ "get duration.microseconds.valueOf",
+ "call duration.microseconds.valueOf",
+ "get duration.milliseconds",
+ "get duration.milliseconds.valueOf",
+ "call duration.milliseconds.valueOf",
+ "get duration.minutes",
+ "get duration.minutes.valueOf",
+ "call duration.minutes.valueOf",
+ "get duration.months",
+ "get duration.months.valueOf",
+ "call duration.months.valueOf",
+ "get duration.nanoseconds",
+ "get duration.nanoseconds.valueOf",
+ "call duration.nanoseconds.valueOf",
+ "get duration.seconds",
+ "get duration.seconds.valueOf",
+ "call duration.seconds.valueOf",
+ "get duration.weeks",
+ "get duration.weeks.valueOf",
+ "call duration.weeks.valueOf",
+ "get duration.years",
+ "get duration.years.valueOf",
+ "call duration.years.valueOf",
+ // lookup
+ "get this.timeZone.getOffsetNanosecondsFor",
+ "get this.timeZone.getPossibleInstantsFor",
+ "get this.calendar.dateAdd",
+ // AddZonedDateTime
+ "call this.timeZone.getOffsetNanosecondsFor",
+ "call this.calendar.dateAdd",
+ // ... inside Calendar.p.dateAdd
+ "get options.overflow",
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+ // AddZonedDateTime again
+ "call this.timeZone.getPossibleInstantsFor",
+];
+const actual = [];
+
+const timeZone = TemporalHelpers.timeZoneObserver(actual, "this.timeZone");
+const calendar = TemporalHelpers.calendarObserver(actual, "this.calendar");
+const instance = new Temporal.ZonedDateTime(0n, timeZone, calendar);
+// clear observable operations that occurred during the constructor call
+actual.splice(0);
+
+const duration = TemporalHelpers.propertyBagObserver(actual, {
+ years: 1,
+ months: 1,
+ weeks: 1,
+ days: 1,
+ hours: 1,
+ minutes: 1,
+ seconds: 1,
+ milliseconds: 1,
+ microseconds: 1,
+ nanoseconds: 1,
+}, "duration");
+
+const options = TemporalHelpers.propertyBagObserver(actual, { overflow: "constrain" }, "options");
+
+instance.subtract(duration, options);
+assert.compareArray(actual, expected, "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/overflow-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/overflow-invalid-string.js
new file mode 100644
index 0000000000..e1ffb534cb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/overflow-invalid-string.js
@@ -0,0 +1,33 @@
+// |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.zoneddatetime.prototype.subtract
+description: RangeError thrown when overflow option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal.calendar.prototype.dateadd step 7:
+ 7. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal-addzoneddatetime step 6:
+ 6. Let _addedDate_ be ? CalendarDateAdd(_calendar_, _datePart_, _dateDuration_, _options_).
+ sec-temporal.zoneddatetime.prototype.subtract step 7:
+ 7. Let _epochNanoseconds_ be ? AddZonedDateTime(_zonedDateTime_.[[Nanoseconds]], _timeZone_, _calendar_, −_duration_.[[Years]], −_duration_.[[Months]], −_duration_.[[Weeks]], −_duration_.[[Days]], −_duration_.[[Hours]], −_duration_.[[Minutes]], −_duration_.[[Seconds]], −_duration_.[[Milliseconds]], −_duration_.[[Microseconds]], −_duration_.[[Nanoseconds]], _options_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+const duration = new Temporal.Duration(0, 0, 0, 1);
+const badOverflows = ["", "CONSTRAIN", "balance", "other string", "constra\u0131n", "reject\0"];
+for (const overflow of badOverflows) {
+ assert.throws(
+ RangeError,
+ () => datetime.subtract(duration, { overflow }),
+ `invalid overflow ("${overflow}")`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/overflow-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/overflow-undefined.js
new file mode 100644
index 0000000000..faf4baa15f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/overflow-undefined.js
@@ -0,0 +1,30 @@
+// |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.zoneddatetime.prototype.subtract
+description: Fallback value for overflow option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal.calendar.prototype.dateadd step 7:
+ 7. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal-addzoneddatetime step 6:
+ 6. Let _addedDate_ be ? CalendarDateAdd(_calendar_, _datePart_, _dateDuration_, _options_).
+ sec-temporal.zoneddatetime.prototype.subtract step 7:
+ 7. Let _epochNanoseconds_ be ? AddZonedDateTime(_zonedDateTime_.[[Nanoseconds]], _timeZone_, _calendar_, −_duration_.[[Years]], −_duration_.[[Months]], −_duration_.[[Weeks]], −_duration_.[[Days]], −_duration_.[[Hours]], −_duration_.[[Minutes]], −_duration_.[[Seconds]], −_duration_.[[Milliseconds]], −_duration_.[[Microseconds]], −_duration_.[[Nanoseconds]], _options_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(-1n, "UTC");
+const duration = new Temporal.Duration(0, 1);
+
+const explicit = datetime.subtract(duration, { overflow: undefined });
+assert.sameValue(explicit.epochNanoseconds, -2678400_000_000_001n, "default overflow is constrain");
+const implicit = datetime.subtract(duration, {});
+assert.sameValue(implicit.epochNanoseconds, -2678400_000_000_001n, "default overflow is constrain");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/overflow-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/overflow-wrong-type.js
new file mode 100644
index 0000000000..93e0c376f9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/overflow-wrong-type.js
@@ -0,0 +1,30 @@
+// |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.zoneddatetime.prototype.subtract
+description: Type conversions for overflow option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal.calendar.prototype.dateadd step 7:
+ 7. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal-addzoneddatetime step 6:
+ 6. Let _addedDate_ be ? CalendarDateAdd(_calendar_, _datePart_, _dateDuration_, _options_).
+ sec-temporal.zoneddatetime.prototype.subtract step 7:
+ 7. Let _epochNanoseconds_ be ? AddZonedDateTime(_zonedDateTime_.[[Nanoseconds]], _timeZone_, _calendar_, −_duration_.[[Years]], −_duration_.[[Months]], −_duration_.[[Weeks]], −_duration_.[[Days]], −_duration_.[[Hours]], −_duration_.[[Minutes]], −_duration_.[[Seconds]], −_duration_.[[Milliseconds]], −_duration_.[[Microseconds]], −_duration_.[[Nanoseconds]], _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+const duration = new Temporal.Duration(0, 0, 0, 1);
+TemporalHelpers.checkStringOptionWrongType("overflow", "constrain",
+ (overflow) => datetime.subtract(duration, { overflow }),
+ (result, descr) => assert.sameValue(result.epochNanoseconds, 999_913_600_987_654_321n, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/prop-desc.js
new file mode 100644
index 0000000000..b73eca66e7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/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.zoneddatetime.prototype.subtract
+description: The "subtract" property of Temporal.ZonedDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.ZonedDateTime.prototype.subtract,
+ "function",
+ "`typeof ZonedDateTime.prototype.subtract` is `function`"
+);
+
+verifyProperty(Temporal.ZonedDateTime.prototype, "subtract", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/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/ZonedDateTime/prototype/subtract/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/subclassing-ignored.js
new file mode 100644
index 0000000000..5607f98d09
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/subclassing-ignored.js
@@ -0,0 +1,31 @@
+// |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.zoneddatetime.prototype.subtract
+description: Objects of a subclass are never created as return values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.ZonedDateTime,
+ [10n, "UTC"],
+ "subtract",
+ [{ nanoseconds: 5 }],
+ (result) => {
+ assert.sameValue(result.epochNanoseconds, 5n, "epochNanoseconds result");
+ assert.sameValue(result.year, 1970, "year result");
+ assert.sameValue(result.month, 1, "month result");
+ assert.sameValue(result.day, 1, "day result");
+ assert.sameValue(result.hour, 0, "hour result");
+ assert.sameValue(result.minute, 0, "minute result");
+ assert.sameValue(result.second, 0, "second result");
+ assert.sameValue(result.millisecond, 0, "millisecond result");
+ assert.sameValue(result.microsecond, 0, "microsecond result");
+ assert.sameValue(result.nanosecond, 5, "nanosecond result");
+ },
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..2c20b22fd6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/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.zoneddatetime.prototype.subtract
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const duration = new Temporal.Duration(1);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.subtract(duration));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..f6db8e31fe
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/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.zoneddatetime.prototype.subtract
+description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable
+features: [BigInt, Symbol, Temporal, arrow-function]
+---*/
+
+[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => {
+ const timeZone = new Temporal.TimeZone("UTC");
+ const duration = new Temporal.Duration(1);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => datetime.subtract(duration),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..ebefefcd5e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/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.zoneddatetime.prototype.subtract
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const duration = new Temporal.Duration(1);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.subtract(duration));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..784eeddde0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/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.zoneddatetime.prototype.subtract
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const duration = new Temporal.Duration(1);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.subtract(duration));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/timeZoneId/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/timeZoneId/branding.js
new file mode 100644
index 0000000000..141652a2b6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/timeZoneId/branding.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-get-temporal.zoneddatetime.prototype.timezoneid
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const timeZoneId = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "timeZoneId").get;
+
+assert.sameValue(typeof timeZoneId, "function");
+
+assert.throws(TypeError, () => timeZoneId.call(undefined), "undefined");
+assert.throws(TypeError, () => timeZoneId.call(null), "null");
+assert.throws(TypeError, () => timeZoneId.call(true), "true");
+assert.throws(TypeError, () => timeZoneId.call(""), "empty string");
+assert.throws(TypeError, () => timeZoneId.call(Symbol()), "symbol");
+assert.throws(TypeError, () => timeZoneId.call(1), "1");
+assert.throws(TypeError, () => timeZoneId.call({}), "plain object");
+assert.throws(TypeError, () => timeZoneId.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime");
+assert.throws(TypeError, () => timeZoneId.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/timeZoneId/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/timeZoneId/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/timeZoneId/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/timeZoneId/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/timeZoneId/builtin-timezone-no-observable-calls.js
new file mode 100644
index 0000000000..0dc8bd72b0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/timeZoneId/builtin-timezone-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.zoneddatetime.prototype.timezoneid
+description: >
+ Calling the method on an instance constructed with a builtin time zone causes
+ no observable lookups or calls to time zone methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const idOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "id");
+Object.defineProperty(Temporal.TimeZone.prototype, "id", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("id should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.timeZoneId;
+
+Object.defineProperty(Temporal.TimeZone.prototype, "id", idOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/timeZoneId/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/timeZoneId/prop-desc.js
new file mode 100644
index 0000000000..b21f020fd2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/timeZoneId/prop-desc.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-get-temporal.zoneddatetime.prototype.timezoneid
+description: The "timeZoneId" property of Temporal.ZonedDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "timeZoneId");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/timeZoneId/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/timeZoneId/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/timeZoneId/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/timeZoneId/timezone-id-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/timeZoneId/timezone-id-wrong-type.js
new file mode 100644
index 0000000000..8604204bae
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/timeZoneId/timezone-id-wrong-type.js
@@ -0,0 +1,40 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Justin Grant. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.equals
+description: TypeError thrown if time zone reports an id that is not a String
+features: [Temporal]
+---*/
+
+class CustomTimeZone extends Temporal.TimeZone {
+ constructor(id) {
+ super("UTC");
+ this._id = id;
+ }
+ get id() {
+ return this._id;
+ }
+}
+
+[
+ undefined,
+ null,
+ true,
+ -1000,
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ {
+ valueOf() {
+ return 3600_000_000_000;
+ }
+ }
+].forEach((wrongId) => {
+ const timeZone = new CustomTimeZone(wrongId);
+ const zdt = Temporal.ZonedDateTime.from({ year: 1970, month: 1, day: 1, timeZone });
+ assert.throws(TypeError, () => zdt.timeZoneId);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toInstant/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toInstant/branding.js
new file mode 100644
index 0000000000..8840d75cda
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toInstant/branding.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.zoneddatetime.prototype.toinstant
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toInstant = Temporal.ZonedDateTime.prototype.toInstant;
+
+assert.sameValue(typeof toInstant, "function");
+
+assert.throws(TypeError, () => toInstant.call(undefined), "undefined");
+assert.throws(TypeError, () => toInstant.call(null), "null");
+assert.throws(TypeError, () => toInstant.call(true), "true");
+assert.throws(TypeError, () => toInstant.call(""), "empty string");
+assert.throws(TypeError, () => toInstant.call(Symbol()), "symbol");
+assert.throws(TypeError, () => toInstant.call(1), "1");
+assert.throws(TypeError, () => toInstant.call({}), "plain object");
+assert.throws(TypeError, () => toInstant.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime");
+assert.throws(TypeError, () => toInstant.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toInstant/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toInstant/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toInstant/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toInstant/builtin.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toInstant/builtin.js
new file mode 100644
index 0000000000..ec7c1bcdbf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toInstant/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.zoneddatetime.prototype.toinstant
+description: >
+ Tests that Temporal.ZonedDateTime.prototype.toInstant
+ 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.ZonedDateTime.prototype.toInstant),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.toInstant),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.toInstant),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.ZonedDateTime.prototype.toInstant.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toInstant/length.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toInstant/length.js
new file mode 100644
index 0000000000..af0f57deaf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toInstant/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.zoneddatetime.prototype.toinstant
+description: Temporal.ZonedDateTime.prototype.toInstant.length is 0
+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.ZonedDateTime.prototype.toInstant, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toInstant/name.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toInstant/name.js
new file mode 100644
index 0000000000..9de6d78822
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toInstant/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.zoneddatetime.prototype.toinstant
+description: Temporal.ZonedDateTime.prototype.toInstant.name is "toInstant".
+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.ZonedDateTime.prototype.toInstant, "name", {
+ value: "toInstant",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toInstant/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toInstant/not-a-constructor.js
new file mode 100644
index 0000000000..cb8cabbef8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toInstant/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.zoneddatetime.prototype.toinstant
+description: >
+ Temporal.ZonedDateTime.prototype.toInstant 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.ZonedDateTime.prototype.toInstant();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.toInstant), false,
+ "isConstructor(Temporal.ZonedDateTime.prototype.toInstant)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toInstant/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toInstant/prop-desc.js
new file mode 100644
index 0000000000..795a6fea6b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toInstant/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.zoneddatetime.prototype.toinstant
+description: The "toInstant" property of Temporal.ZonedDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.ZonedDateTime.prototype.toInstant,
+ "function",
+ "`typeof ZonedDateTime.prototype.toInstant` is `function`"
+);
+
+verifyProperty(Temporal.ZonedDateTime.prototype, "toInstant", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toInstant/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toInstant/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toInstant/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/ZonedDateTime/prototype/toJSON/balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/balance-negative-time-units.js
new file mode 100644
index 0000000000..8496ba4b0e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/balance-negative-time-units.js
@@ -0,0 +1,44 @@
+// |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.zoneddatetime.prototype.tojson
+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-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-temporalzoneddatetimetostring step 9:
+ 9. Let _dateTime_ be ? BuiltinTimeZoneGetPlainDateTimeFor(_timeZone_, _instant_, _isoCalendar_).
+ sec-get-temporal.zoneddatetime.prototype.tojson step 3:
+ 3. Return ? TemporalZonedDateTimeToString(_zonedDateTime_, *"auto"*, *"auto"*, *"auto"*, *"auto"*).
+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
+// ZonedDateTime
+const tz = TemporalHelpers.specificOffsetTimeZone(-2);
+const datetime = new Temporal.ZonedDateTime(1001n, tz);
+
+const jsonString = datetime.toJSON();
+
+assert.sameValue(jsonString, "1970-01-01T00:00:00.000000999+00:00[-00:00:00.000000002]");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/basic.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/basic.js
new file mode 100644
index 0000000000..a82fcfa23f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/basic.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.zoneddatetime.prototype.tojson
+description: Basic behavior for toJSON
+features: [BigInt, Temporal]
+---*/
+
+const tests = [
+ [new Temporal.ZonedDateTime(192_258_181_000_000_000n, "UTC"), "1976-02-04T05:03:01+00:00[UTC]"],
+ [new Temporal.ZonedDateTime(0n, "UTC"), "1970-01-01T00:00:00+00:00[UTC]"],
+ [new Temporal.ZonedDateTime(30_000_000_000n, "UTC"), "1970-01-01T00:00:30+00:00[UTC]"],
+ [new Temporal.ZonedDateTime(30_123_400_000n, "UTC"), "1970-01-01T00:00:30.1234+00:00[UTC]"],
+];
+
+const options = new Proxy({}, {
+ get() { throw new Test262Error("should not get properties off argument") }
+});
+for (const [datetime, expected] of tests) {
+ assert.sameValue(datetime.toJSON(), expected, "toJSON without argument");
+ assert.sameValue(datetime.toJSON(options), expected, "toJSON with argument");
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/branding.js
new file mode 100644
index 0000000000..6be0b98e47
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/branding.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.zoneddatetime.prototype.tojson
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toJSON = Temporal.ZonedDateTime.prototype.toJSON;
+
+assert.sameValue(typeof toJSON, "function");
+
+assert.throws(TypeError, () => toJSON.call(undefined), "undefined");
+assert.throws(TypeError, () => toJSON.call(null), "null");
+assert.throws(TypeError, () => toJSON.call(true), "true");
+assert.throws(TypeError, () => toJSON.call(""), "empty string");
+assert.throws(TypeError, () => toJSON.call(Symbol()), "symbol");
+assert.throws(TypeError, () => toJSON.call(1), "1");
+assert.throws(TypeError, () => toJSON.call({}), "plain object");
+assert.throws(TypeError, () => toJSON.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime");
+assert.throws(TypeError, () => toJSON.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..0ce3ae9c34
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.zoneddatetime.prototype.tojson
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const idOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "id");
+Object.defineProperty(Temporal.Calendar.prototype, "id", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("id should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.toJSON();
+
+Object.defineProperty(Temporal.Calendar.prototype, "id", idOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/builtin-timezone-no-observable-calls.js
new file mode 100644
index 0000000000..bbab91de0e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/builtin-timezone-no-observable-calls.js
@@ -0,0 +1,37 @@
+// |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.zoneddatetime.prototype.tojson
+description: >
+ Calling the method on an instance constructed with a builtin time zone causes
+ no observable lookups or calls to time zone methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+const idOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "id");
+Object.defineProperty(Temporal.TimeZone.prototype, "id", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("id should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.toJSON();
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+Object.defineProperty(Temporal.TimeZone.prototype, "id", idOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/builtin.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/builtin.js
new file mode 100644
index 0000000000..1cc3e874df
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/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.zoneddatetime.prototype.tojson
+description: >
+ Tests that Temporal.ZonedDateTime.prototype.toJSON
+ 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.ZonedDateTime.prototype.toJSON),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.toJSON),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.toJSON),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.ZonedDateTime.prototype.toJSON.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/length.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/length.js
new file mode 100644
index 0000000000..9d6f0cedee
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/length.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.zoneddatetime.prototype.tojson
+description: Temporal.ZonedDateTime.prototype.toJSON.length is 0
+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.ZonedDateTime.prototype.toJSON, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/name.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/name.js
new file mode 100644
index 0000000000..0f9a9dad74
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/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.zoneddatetime.prototype.tojson
+description: Temporal.ZonedDateTime.prototype.toJSON.name is "toJSON".
+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.ZonedDateTime.prototype.toJSON, "name", {
+ value: "toJSON",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/negative-epochnanoseconds.js
new file mode 100644
index 0000000000..57f7d0b3a2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/negative-epochnanoseconds.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.zoneddatetime.prototype.tojson
+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]
+---*/
+
+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 result = datetime.toJSON();
+assert.sameValue(result, "1969-07-24T16:50:35.000000001+00:00[UTC]");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/not-a-constructor.js
new file mode 100644
index 0000000000..b57ee43d71
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/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.zoneddatetime.prototype.tojson
+description: >
+ Temporal.ZonedDateTime.prototype.toJSON 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.ZonedDateTime.prototype.toJSON();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.toJSON), false,
+ "isConstructor(Temporal.ZonedDateTime.prototype.toJSON)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/offset.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/offset.js
new file mode 100644
index 0000000000..abe92bea4c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/offset.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.zoneddatetime.prototype.tojson
+description: The time zone offset part of the string serialization
+features: [BigInt, Temporal]
+---*/
+
+function test(timeZoneIdentifier, expected, description) {
+ const timeZone = new Temporal.TimeZone(timeZoneIdentifier);
+ const datetime = new Temporal.ZonedDateTime(0n, timeZone);
+ assert.sameValue(datetime.toJSON(), expected, description);
+}
+
+test("UTC", "1970-01-01T00:00:00+00:00[UTC]", "offset of UTC is +00:00");
+test("+01:00", "1970-01-01T01:00:00+01:00[+01:00]", "positive offset");
+test("-05:00", "1969-12-31T19:00:00-05:00[-05:00]", "negative offset");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/order-of-operations.js
new file mode 100644
index 0000000000..516bf5103d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/order-of-operations.js
@@ -0,0 +1,29 @@
+// |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.zoneddatetime.prototype.tojson
+description: Properties on objects passed to toJSON() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "get this.timeZone.getOffsetNanosecondsFor",
+ "call this.timeZone.getOffsetNanosecondsFor",
+ "get this.timeZone.id",
+ "get this.calendar.id",
+];
+const actual = [];
+
+const timeZone = TemporalHelpers.timeZoneObserver(actual, "this.timeZone");
+const calendar = TemporalHelpers.calendarObserver(actual, "this.calendar");
+const instance = new Temporal.ZonedDateTime(0n, timeZone, calendar);
+// clear observable operations that occurred during the constructor call
+actual.splice(0);
+
+instance.toJSON();
+assert.compareArray(actual, expected, "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/prop-desc.js
new file mode 100644
index 0000000000..0f060aa215
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/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.zoneddatetime.prototype.tojson
+description: The "toJSON" property of Temporal.ZonedDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.ZonedDateTime.prototype.toJSON,
+ "function",
+ "`typeof ZonedDateTime.prototype.toJSON` is `function`"
+);
+
+verifyProperty(Temporal.ZonedDateTime.prototype, "toJSON", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/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/ZonedDateTime/prototype/toJSON/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..ab9d955c51
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,18 @@
+// |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.zoneddatetime.prototype.tojson
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.toJSON());
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..e4ee22087d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/timezone-getoffsetnanosecondsfor-not-callable.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.zoneddatetime.prototype.tojson
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => datetime.toJSON(),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..011224be4c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,18 @@
+// |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.zoneddatetime.prototype.tojson
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.toJSON());
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..62c5cf286a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/timezone-getoffsetnanosecondsfor-wrong-type.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.zoneddatetime.prototype.tojson
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.toJSON());
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/timezone-id-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/timezone-id-wrong-type.js
new file mode 100644
index 0000000000..8cbcf29f0d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/timezone-id-wrong-type.js
@@ -0,0 +1,40 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Justin Grant. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.equals
+description: TypeError thrown if time zone reports an id that is not a String
+features: [Temporal]
+---*/
+
+class CustomTimeZone extends Temporal.TimeZone {
+ constructor(id) {
+ super("UTC");
+ this._id = id;
+ }
+ get id() {
+ return this._id;
+ }
+}
+
+[
+ undefined,
+ null,
+ true,
+ -1000,
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ {
+ valueOf() {
+ return 3600_000_000_000;
+ }
+ }
+].forEach((wrongId) => {
+ const timeZone = new CustomTimeZone(wrongId);
+ const zdt = Temporal.ZonedDateTime.from({ year: 1970, month: 1, day: 1, timeZone });
+ assert.throws(TypeError, () => zdt.toJSON());
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/year-format.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/year-format.js
new file mode 100644
index 0000000000..571a9bc74d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/year-format.js
@@ -0,0 +1,58 @@
+// |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.zoneddatetime.prototype.tojson
+description: Verify that the year is appropriately formatted as 4 or 6 digits
+features: [Temporal]
+---*/
+
+function epochNsInYear(year) {
+ // Return an epoch nanoseconds value near the middle of the given year
+ const avgNsPerYear = 31_556_952_000_000_000n;
+ return (year - 1970n) * avgNsPerYear + (avgNsPerYear / 2n);
+}
+
+const utc = new Temporal.TimeZone("UTC");
+
+let instance = new Temporal.ZonedDateTime(epochNsInYear(-100000n), utc);
+assert.sameValue(instance.toJSON(), "-100000-07-01T21:30:36+00:00[UTC]", "large negative year formatted as 6-digit");
+
+instance = new Temporal.ZonedDateTime(epochNsInYear(-10000n), utc);
+assert.sameValue(instance.toJSON(), "-010000-07-01T21:30:36+00:00[UTC]", "smallest 5-digit negative year formatted as 6-digit");
+
+instance = new Temporal.ZonedDateTime(epochNsInYear(-9999n), utc);
+assert.sameValue(instance.toJSON(), "-009999-07-02T03:19:48+00:00[UTC]", "largest 4-digit negative year formatted as 6-digit");
+
+instance = new Temporal.ZonedDateTime(epochNsInYear(-1000n), utc);
+assert.sameValue(instance.toJSON(), "-001000-07-02T09:30:36+00:00[UTC]", "smallest 4-digit negative year formatted as 6-digit");
+
+instance = new Temporal.ZonedDateTime(epochNsInYear(-999n), utc);
+assert.sameValue(instance.toJSON(), "-000999-07-02T15:19:48+00:00[UTC]", "largest 3-digit negative year formatted as 6-digit");
+
+instance = new Temporal.ZonedDateTime(epochNsInYear(-1n), utc);
+assert.sameValue(instance.toJSON(), "-000001-07-02T15:41:24+00:00[UTC]", "year -1 formatted as 6-digit");
+
+instance = new Temporal.ZonedDateTime(epochNsInYear(0n), utc);
+assert.sameValue(instance.toJSON(), "0000-07-01T21:30:36+00:00[UTC]", "year 0 formatted as 4-digit");
+
+instance = new Temporal.ZonedDateTime(epochNsInYear(1n), utc);
+assert.sameValue(instance.toJSON(), "0001-07-02T03:19:48+00:00[UTC]", "year 1 formatted as 4-digit");
+
+instance = new Temporal.ZonedDateTime(epochNsInYear(999n), utc);
+assert.sameValue(instance.toJSON(), "0999-07-02T03:41:24+00:00[UTC]", "largest 3-digit positive year formatted as 4-digit");
+
+instance = new Temporal.ZonedDateTime(epochNsInYear(1000n), utc);
+assert.sameValue(instance.toJSON(), "1000-07-02T09:30:36+00:00[UTC]", "smallest 4-digit positive year formatted as 4-digit");
+
+instance = new Temporal.ZonedDateTime(epochNsInYear(9999n), utc);
+assert.sameValue(instance.toJSON(), "9999-07-02T15:41:24+00:00[UTC]", "largest 4-digit positive year formatted as 4-digit");
+
+instance = new Temporal.ZonedDateTime(epochNsInYear(10000n), utc);
+assert.sameValue(instance.toJSON(), "+010000-07-01T21:30:36+00:00[UTC]", "smallest 5-digit positive year formatted as 6-digit");
+
+instance = new Temporal.ZonedDateTime(epochNsInYear(100000n), utc);
+assert.sameValue(instance.toJSON(), "+100000-07-01T21:30:36+00:00[UTC]", "large positive year formatted as 6-digit");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/branding.js
new file mode 100644
index 0000000000..a59ba2cebb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/branding.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.zoneddatetime.prototype.tolocalestring
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toLocaleString = Temporal.ZonedDateTime.prototype.toLocaleString;
+
+assert.sameValue(typeof toLocaleString, "function");
+
+assert.throws(TypeError, () => toLocaleString.call(undefined), "undefined");
+assert.throws(TypeError, () => toLocaleString.call(null), "null");
+assert.throws(TypeError, () => toLocaleString.call(true), "true");
+assert.throws(TypeError, () => toLocaleString.call(""), "empty string");
+assert.throws(TypeError, () => toLocaleString.call(Symbol()), "symbol");
+assert.throws(TypeError, () => toLocaleString.call(1), "1");
+assert.throws(TypeError, () => toLocaleString.call({}), "plain object");
+assert.throws(TypeError, () => toLocaleString.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime");
+assert.throws(TypeError, () => toLocaleString.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..cbb699c021
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.zoneddatetime.prototype.tolocalestring
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const idOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "id");
+Object.defineProperty(Temporal.Calendar.prototype, "id", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("id should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.toLocaleString();
+
+Object.defineProperty(Temporal.Calendar.prototype, "id", idOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/builtin-timezone-no-observable-calls.js
new file mode 100644
index 0000000000..181c111869
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/builtin-timezone-no-observable-calls.js
@@ -0,0 +1,37 @@
+// |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.zoneddatetime.prototype.tolocalestring
+description: >
+ Calling the method on an instance constructed with a builtin time zone causes
+ no observable lookups or calls to time zone methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+const idOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "id");
+Object.defineProperty(Temporal.TimeZone.prototype, "id", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("id should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.toLocaleString();
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+Object.defineProperty(Temporal.TimeZone.prototype, "id", idOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/builtin.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/builtin.js
new file mode 100644
index 0000000000..2b73b77098
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/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.zoneddatetime.prototype.tolocalestring
+description: >
+ Tests that Temporal.ZonedDateTime.prototype.toLocaleString
+ 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.ZonedDateTime.prototype.toLocaleString),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.toLocaleString),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.toLocaleString),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.ZonedDateTime.prototype.toLocaleString.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/length.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/length.js
new file mode 100644
index 0000000000..24b9b3b9e1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/length.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.zoneddatetime.prototype.tolocalestring
+description: Temporal.ZonedDateTime.prototype.toLocaleString.length is 0
+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.ZonedDateTime.prototype.toLocaleString, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/name.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/name.js
new file mode 100644
index 0000000000..677c3c6c87
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/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.zoneddatetime.prototype.tolocalestring
+description: Temporal.ZonedDateTime.prototype.toLocaleString.name is "toLocaleString".
+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.ZonedDateTime.prototype.toLocaleString, "name", {
+ value: "toLocaleString",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/not-a-constructor.js
new file mode 100644
index 0000000000..f22770d115
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/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.zoneddatetime.prototype.tolocalestring
+description: >
+ Temporal.ZonedDateTime.prototype.toLocaleString 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.ZonedDateTime.prototype.toLocaleString();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.toLocaleString), false,
+ "isConstructor(Temporal.ZonedDateTime.prototype.toLocaleString)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/prop-desc.js
new file mode 100644
index 0000000000..b35f9f7fb9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/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.zoneddatetime.prototype.tolocalestring
+description: The "toLocaleString" property of Temporal.ZonedDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.ZonedDateTime.prototype.toLocaleString,
+ "function",
+ "`typeof ZonedDateTime.prototype.toLocaleString` is `function`"
+);
+
+verifyProperty(Temporal.ZonedDateTime.prototype, "toLocaleString", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/return-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/return-string.js
new file mode 100644
index 0000000000..b22c79caea
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/return-string.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Kate Miháliková. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.tolocalestring
+description: >
+ toLocaleString return a string.
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(957270896_987_650_000n, "UTC");
+
+assert.sameValue(typeof datetime.toLocaleString("en", { dateStyle: "short" }), "string");
+assert.sameValue(typeof datetime.toLocaleString("en", { timeStyle: "short" }), "string");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/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/ZonedDateTime/prototype/toLocaleString/timezone-id-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/timezone-id-wrong-type.js
new file mode 100644
index 0000000000..002de3c45f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/timezone-id-wrong-type.js
@@ -0,0 +1,40 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Justin Grant. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.equals
+description: TypeError thrown if time zone reports an id that is not a String
+features: [Temporal]
+---*/
+
+class CustomTimeZone extends Temporal.TimeZone {
+ constructor(id) {
+ super("UTC");
+ this._id = id;
+ }
+ get id() {
+ return this._id;
+ }
+}
+
+[
+ undefined,
+ null,
+ true,
+ -1000,
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ {
+ valueOf() {
+ return 3600_000_000_000;
+ }
+ }
+].forEach((wrongId) => {
+ const timeZone = new CustomTimeZone(wrongId);
+ const zdt = Temporal.ZonedDateTime.from({ year: 1970, month: 1, day: 1, timeZone });
+ assert.throws(TypeError, () => zdt.toLocaleString());
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/branding.js
new file mode 100644
index 0000000000..fa9699b616
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/branding.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.zoneddatetime.prototype.toplaindate
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toPlainDate = Temporal.ZonedDateTime.prototype.toPlainDate;
+
+assert.sameValue(typeof toPlainDate, "function");
+
+assert.throws(TypeError, () => toPlainDate.call(undefined), "undefined");
+assert.throws(TypeError, () => toPlainDate.call(null), "null");
+assert.throws(TypeError, () => toPlainDate.call(true), "true");
+assert.throws(TypeError, () => toPlainDate.call(""), "empty string");
+assert.throws(TypeError, () => toPlainDate.call(Symbol()), "symbol");
+assert.throws(TypeError, () => toPlainDate.call(1), "1");
+assert.throws(TypeError, () => toPlainDate.call({}), "plain object");
+assert.throws(TypeError, () => toPlainDate.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime");
+assert.throws(TypeError, () => toPlainDate.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/builtin-timezone-no-observable-calls.js
new file mode 100644
index 0000000000..ab2f148bcb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/builtin-timezone-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.zoneddatetime.prototype.toplaindate
+description: >
+ Calling the method on an instance constructed with a builtin time zone causes
+ no observable lookups or calls to time zone methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.toPlainDate();
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/builtin.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/builtin.js
new file mode 100644
index 0000000000..f4b3991392
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/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.zoneddatetime.prototype.toplaindate
+description: >
+ Tests that Temporal.ZonedDateTime.prototype.toPlainDate
+ 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.ZonedDateTime.prototype.toPlainDate),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.toPlainDate),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.toPlainDate),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.ZonedDateTime.prototype.toPlainDate.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/length.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/length.js
new file mode 100644
index 0000000000..8a2ebcdb12
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/length.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.zoneddatetime.prototype.toplaindate
+description: Temporal.ZonedDateTime.prototype.toPlainDate.length is 0
+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.ZonedDateTime.prototype.toPlainDate, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/name.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/name.js
new file mode 100644
index 0000000000..1aac1481e8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/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.zoneddatetime.prototype.toplaindate
+description: Temporal.ZonedDateTime.prototype.toPlainDate.name is "toPlainDate".
+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.ZonedDateTime.prototype.toPlainDate, "name", {
+ value: "toPlainDate",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/not-a-constructor.js
new file mode 100644
index 0000000000..4a53da5fba
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/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.zoneddatetime.prototype.toplaindate
+description: >
+ Temporal.ZonedDateTime.prototype.toPlainDate 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.ZonedDateTime.prototype.toPlainDate();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.toPlainDate), false,
+ "isConstructor(Temporal.ZonedDateTime.prototype.toPlainDate)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/prop-desc.js
new file mode 100644
index 0000000000..5c55b55f75
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/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.zoneddatetime.prototype.toplaindate
+description: The "toPlainDate" property of Temporal.ZonedDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.ZonedDateTime.prototype.toPlainDate,
+ "function",
+ "`typeof ZonedDateTime.prototype.toPlainDate` is `function`"
+);
+
+verifyProperty(Temporal.ZonedDateTime.prototype, "toPlainDate", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/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/ZonedDateTime/prototype/toPlainDate/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..c4fc81e5a5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,18 @@
+// |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.zoneddatetime.prototype.toplaindate
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.toPlainDate());
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..94afa2426c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/timezone-getoffsetnanosecondsfor-not-callable.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.zoneddatetime.prototype.toplaindate
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => datetime.toPlainDate(),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..fbf216ad68
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,18 @@
+// |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.zoneddatetime.prototype.toplaindate
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.toPlainDate());
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..6e9b2f1490
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/timezone-getoffsetnanosecondsfor-wrong-type.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.zoneddatetime.prototype.toplaindate
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.toPlainDate());
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/balance-negative-time-units.js
new file mode 100644
index 0000000000..7c495c14f6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/balance-negative-time-units.js
@@ -0,0 +1,42 @@
+// |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.zoneddatetime.prototype.toplaindatetime
+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-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-get-temporal.zoneddatetime.prototype.toplaindatetime step 5:
+ 5. Return ? BuiltinTimeZoneGetPlainDateTimeFor(_timeZone_, _instant_, _zonedDateTime_.[[Calendar]]).
+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(1001n, tz);
+
+const pdt = datetime.toPlainDateTime();
+
+TemporalHelpers.assertPlainDateTime(pdt, 1970, 1, "M01", 1, 0, 0, 0, 0, 0, 999);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/branding.js
new file mode 100644
index 0000000000..cb5656f538
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/branding.js
@@ -0,0 +1,25 @@
+// |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.zoneddatetime.prototype.toplaindatetime
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toPlainDateTime = Temporal.ZonedDateTime.prototype.toPlainDateTime;
+
+assert.sameValue(typeof toPlainDateTime, "function");
+
+assert.throws(TypeError, () => toPlainDateTime.call(undefined), "undefined");
+assert.throws(TypeError, () => toPlainDateTime.call(null), "null");
+assert.throws(TypeError, () => toPlainDateTime.call(true), "true");
+assert.throws(TypeError, () => toPlainDateTime.call(""), "empty string");
+assert.throws(TypeError, () => toPlainDateTime.call(Symbol()), "symbol");
+assert.throws(TypeError, () => toPlainDateTime.call(1), "1");
+assert.throws(TypeError, () => toPlainDateTime.call({}), "plain object");
+assert.throws(TypeError, () => toPlainDateTime.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime");
+assert.throws(TypeError, () => toPlainDateTime.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/builtin-timezone-no-observable-calls.js
new file mode 100644
index 0000000000..207864ee22
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/builtin-timezone-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.zoneddatetime.prototype.toplaindatetime
+description: >
+ Calling the method on an instance constructed with a builtin time zone causes
+ no observable lookups or calls to time zone methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.toPlainDateTime();
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/builtin.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/builtin.js
new file mode 100644
index 0000000000..aaf8d295ed
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/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.zoneddatetime.prototype.toplaindatetime
+description: >
+ Tests that Temporal.ZonedDateTime.prototype.toPlainDateTime
+ 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.ZonedDateTime.prototype.toPlainDateTime),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.toPlainDateTime),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.toPlainDateTime),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.ZonedDateTime.prototype.toPlainDateTime.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/length.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/length.js
new file mode 100644
index 0000000000..0bff20504a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/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.zoneddatetime.prototype.toplaindatetime
+description: Temporal.ZonedDateTime.prototype.toPlainDateTime.length is 0
+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.ZonedDateTime.prototype.toPlainDateTime, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/name.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/name.js
new file mode 100644
index 0000000000..697d404dcd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/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.zoneddatetime.prototype.toplaindatetime
+description: Temporal.ZonedDateTime.prototype.toPlainDateTime.name is "toPlainDateTime".
+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.ZonedDateTime.prototype.toPlainDateTime, "name", {
+ value: "toPlainDateTime",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/negative-epochnanoseconds.js
new file mode 100644
index 0000000000..8f62f95941
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/negative-epochnanoseconds.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.zoneddatetime.prototype.toplaindatetime
+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 result = datetime.toPlainDateTime();
+TemporalHelpers.assertPlainDateTime(result, 1969, 7, "M07", 24, 16, 50, 35, 0, 0, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/not-a-constructor.js
new file mode 100644
index 0000000000..7a3d9fd77a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/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.zoneddatetime.prototype.toplaindatetime
+description: >
+ Temporal.ZonedDateTime.prototype.toPlainDateTime 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.ZonedDateTime.prototype.toPlainDateTime();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.toPlainDateTime), false,
+ "isConstructor(Temporal.ZonedDateTime.prototype.toPlainDateTime)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/plain-custom-timezone.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/plain-custom-timezone.js
new file mode 100644
index 0000000000..7bd3d23b3d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/plain-custom-timezone.js
@@ -0,0 +1,34 @@
+// |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.zoneddatetime.prototype.toplaindatetime
+description: timeZone.getOffsetNanosecondsFor() called
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const expected = [
+ "has timeZone.getOffsetNanosecondsFor",
+ "has timeZone.getPossibleInstantsFor",
+ "has timeZone.id",
+ "get timeZone.getOffsetNanosecondsFor",
+ "call timeZone.getOffsetNanosecondsFor",
+];
+
+const timeZone = TemporalHelpers.timeZoneObserver(actual, "timeZone", {
+ getOffsetNanosecondsFor: -8735135802468,
+});
+
+const zdt = new Temporal.ZonedDateTime(160583136123456789n, timeZone);
+const dateTime = Temporal.PlainDateTime.from("1975-02-02T12:00:00.987654321");
+const result = zdt.toPlainDateTime();
+for (const property of ["year", "month", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"]) {
+ assert.sameValue(result[property], dateTime[property], property);
+}
+
+assert.compareArray(actual, expected);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/prop-desc.js
new file mode 100644
index 0000000000..6d97b7e478
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/prop-desc.js
@@ -0,0 +1,24 @@
+// |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.zoneddatetime.prototype.toplaindatetime
+description: The "toPlainDateTime" property of Temporal.ZonedDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.ZonedDateTime.prototype.toPlainDateTime,
+ "function",
+ "`typeof ZonedDateTime.prototype.toPlainDateTime` is `function`"
+);
+
+verifyProperty(Temporal.ZonedDateTime.prototype, "toPlainDateTime", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/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/ZonedDateTime/prototype/toPlainDateTime/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..8d3578959b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,18 @@
+// |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.zoneddatetime.prototype.toplaindatetime
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.toPlainDateTime());
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..78a93f2c87
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/timezone-getoffsetnanosecondsfor-not-callable.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.zoneddatetime.prototype.toplaindatetime
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => datetime.toPlainDateTime(),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..cd6d086688
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,18 @@
+// |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.zoneddatetime.prototype.toplaindatetime
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.toPlainDateTime());
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..4f65674b9d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/timezone-getoffsetnanosecondsfor-wrong-type.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.zoneddatetime.prototype.toplaindatetime
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.toPlainDateTime());
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/branding.js
new file mode 100644
index 0000000000..d1b8111099
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/branding.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.zoneddatetime.prototype.toplainmonthday
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toPlainMonthDay = Temporal.ZonedDateTime.prototype.toPlainMonthDay;
+
+assert.sameValue(typeof toPlainMonthDay, "function");
+
+assert.throws(TypeError, () => toPlainMonthDay.call(undefined), "undefined");
+assert.throws(TypeError, () => toPlainMonthDay.call(null), "null");
+assert.throws(TypeError, () => toPlainMonthDay.call(true), "true");
+assert.throws(TypeError, () => toPlainMonthDay.call(""), "empty string");
+assert.throws(TypeError, () => toPlainMonthDay.call(Symbol()), "symbol");
+assert.throws(TypeError, () => toPlainMonthDay.call(1), "1");
+assert.throws(TypeError, () => toPlainMonthDay.call({}), "plain object");
+assert.throws(TypeError, () => toPlainMonthDay.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime");
+assert.throws(TypeError, () => toPlainMonthDay.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..dff21b9190
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/builtin-calendar-no-array-iteration.js
@@ -0,0 +1,23 @@
+// |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.zoneddatetime.prototype.toplainmonthday
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.toPlainMonthDay();
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..aa31f54010
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,37 @@
+// |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.zoneddatetime.prototype.toplainmonthday
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const fieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "fields");
+Object.defineProperty(Temporal.Calendar.prototype, "fields", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("fields should not be looked up");
+ },
+});
+const monthDayFromFieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "monthDayFromFields");
+Object.defineProperty(Temporal.Calendar.prototype, "monthDayFromFields", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("monthDayFromFields should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.toPlainMonthDay();
+
+Object.defineProperty(Temporal.Calendar.prototype, "fields", fieldsOriginal);
+Object.defineProperty(Temporal.Calendar.prototype, "monthDayFromFields", monthDayFromFieldsOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/builtin-timezone-no-observable-calls.js
new file mode 100644
index 0000000000..5c2f01374b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/builtin-timezone-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.zoneddatetime.prototype.toplainmonthday
+description: >
+ Calling the method on an instance constructed with a builtin time zone causes
+ no observable lookups or calls to time zone methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.toPlainMonthDay();
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/builtin.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/builtin.js
new file mode 100644
index 0000000000..de85dc4996
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/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.zoneddatetime.prototype.toplainmonthday
+description: >
+ Tests that Temporal.ZonedDateTime.prototype.toPlainMonthDay
+ 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.ZonedDateTime.prototype.toPlainMonthDay),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.toPlainMonthDay),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.toPlainMonthDay),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.ZonedDateTime.prototype.toPlainMonthDay.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/calendar-arguments.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/calendar-arguments.js
new file mode 100644
index 0000000000..4b088c3520
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/calendar-arguments.js
@@ -0,0 +1,32 @@
+// |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.zoneddatetime.prototype.toplainmonthday
+description: Correct options value is passed to calendar method
+info: |
+ MonthDayFromFields ( calendar, fields [ , options ] )
+
+ 3. If options is not present, then
+ a. Set options to undefined.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ monthDayFromFields(...args) {
+ assert.sameValue(args.length, 2, "args.length");
+ assert.sameValue(typeof args[0], "object", "args[0]");
+ assert.sameValue(args[1], undefined, "args[1]");
+ return super.monthDayFromFields(...args);
+ }
+}
+const zonedDateTime = new Temporal.ZonedDateTime(957270896123456789n, "UTC", new CustomCalendar());
+const result = zonedDateTime.toPlainMonthDay();
+TemporalHelpers.assertPlainMonthDay(result, "M05", 2);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/calendar-fields-iterable.js
new file mode 100644
index 0000000000..c23e255c14
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/calendar-fields-iterable.js
@@ -0,0 +1,30 @@
+// |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.zoneddatetime.prototype.toplainmonthday
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.zoneddatetime.prototype.toplainmonthday step 7:
+ 7. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"monthCode"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "monthCode",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar);
+datetime.toPlainMonthDay();
+
+assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/calendar-fromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/calendar-fromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..3a5a0d57a9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/calendar-fromfields-called-with-null-prototype-fields.js
@@ -0,0 +1,18 @@
+// |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.zoneddatetime.prototype.toplainmonthday
+description: >
+ Calendar.monthDayFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.ZonedDateTime(0n, "UTC", calendar);
+instance.toPlainMonthDay();
+assert.sameValue(calendar.monthDayFromFieldsCallCount, 1, "monthDayFromFields should have been called on the calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/calendar-monthdayfromfields-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/calendar-monthdayfromfields-called-with-options-undefined.js
new file mode 100644
index 0000000000..d124b0e2f1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/calendar-monthdayfromfields-called-with-options-undefined.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.zoneddatetime.prototype.toplainmonthday
+description: >
+ Calendar.monthDayFromFields method is called with undefined as the options
+ value when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar);
+instance.toPlainMonthDay();
+assert.sameValue(calendar.monthDayFromFieldsCallCount, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/calendar-result.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/calendar-result.js
new file mode 100644
index 0000000000..45fdb31d48
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/calendar-result.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.zoneddatetime.prototype.toplainmonthday
+description: TypeError thrown when calendar method returns an object with the wrong brand
+info: |
+ MonthDayFromFields ( calendar, fields, options )
+
+ 4. Perform ? RequireInternalSlot(monthDay, [[InitializedTemporalMonthDay]]).
+features: [Temporal]
+---*/
+
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ monthDayFromFields() {
+ return {};
+ }
+}
+const zonedDateTime = new Temporal.ZonedDateTime(957270896123456789n, "UTC", new CustomCalendar());
+assert.throws(TypeError, () => zonedDateTime.toPlainMonthDay());
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..eb95558c12
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/constructor-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |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.zoneddatetime.prototype.toplainmonthday
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const zoneddatetime = new Temporal.ZonedDateTime(1682892000000000000n, 'Europe/Madrid', calendar);
+
+assert.throws(RangeError, () => zoneddatetime.toPlainMonthDay());
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/duplicate-calendar-fields.js
new file mode 100644
index 0000000000..10110794a3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/duplicate-calendar-fields.js
@@ -0,0 +1,18 @@
+// |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.zoneddatetime.prototype.toplainmonthday
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['monthCode'], ['day']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const zoneddatetime = new Temporal.ZonedDateTime(1682892000000000000n, 'Europe/Madrid', calendar);
+
+ assert.throws(RangeError, () => zoneddatetime.toPlainMonthDay());
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/length.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/length.js
new file mode 100644
index 0000000000..93fdd16c7c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/length.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.zoneddatetime.prototype.toplainmonthday
+description: Temporal.ZonedDateTime.prototype.toPlainMonthDay.length is 0
+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.ZonedDateTime.prototype.toPlainMonthDay, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/name.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/name.js
new file mode 100644
index 0000000000..dd52a2400d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/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.zoneddatetime.prototype.toplainmonthday
+description: Temporal.ZonedDateTime.prototype.toPlainMonthDay.name is "toPlainMonthDay".
+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.ZonedDateTime.prototype.toPlainMonthDay, "name", {
+ value: "toPlainMonthDay",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/not-a-constructor.js
new file mode 100644
index 0000000000..3ce42e7be1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/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.zoneddatetime.prototype.toplainmonthday
+description: >
+ Temporal.ZonedDateTime.prototype.toPlainMonthDay 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.ZonedDateTime.prototype.toPlainMonthDay();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.toPlainMonthDay), false,
+ "isConstructor(Temporal.ZonedDateTime.prototype.toPlainMonthDay)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/order-of-operations.js
new file mode 100644
index 0000000000..b09f37d7a6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/order-of-operations.js
@@ -0,0 +1,51 @@
+// |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.zoneddatetime.prototype.toplainmonthday
+description: >
+ User-observable time zone and calendar accesses and calls in
+ toPlainMonthDay() happen the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "get this.calendar.fields",
+ "get this.calendar.monthDayFromFields",
+ "get this.timeZone.getOffsetNanosecondsFor",
+ "call this.timeZone.getOffsetNanosecondsFor",
+ "call this.calendar.fields",
+ "get this.calendar.day",
+ "call this.calendar.day",
+ "get this.calendar.monthCode",
+ "call this.calendar.monthCode",
+ "call this.calendar.monthDayFromFields",
+];
+const actual = [];
+
+const timeZone = TemporalHelpers.timeZoneObserver(actual, "this.timeZone");
+const calendar = TemporalHelpers.calendarObserver(actual, "this.calendar");
+const instance = new Temporal.ZonedDateTime(0n, timeZone, calendar);
+Object.defineProperties(instance, {
+ day: {
+ get() {
+ actual.push("get this.day");
+ return TemporalHelpers.toPrimitiveObserver(actual, 1, "this.day");
+ }
+ },
+ monthCode: {
+ get() {
+ actual.push("get this.monthCode");
+ return TemporalHelpers.toPrimitiveObserver(actual, "M01", "this.monthCode");
+ }
+ },
+});
+// clear observable operations that occurred during the constructor call
+actual.splice(0);
+
+instance.toPlainMonthDay();
+assert.compareArray(actual, expected, "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/prop-desc.js
new file mode 100644
index 0000000000..eae409c8b4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/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.zoneddatetime.prototype.toplainmonthday
+description: The "toPlainMonthDay" property of Temporal.ZonedDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.ZonedDateTime.prototype.toPlainMonthDay,
+ "function",
+ "`typeof ZonedDateTime.prototype.toPlainMonthDay` is `function`"
+);
+
+verifyProperty(Temporal.ZonedDateTime.prototype, "toPlainMonthDay", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/proto-in-calendar-fields.js
new file mode 100644
index 0000000000..37da40afa8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/proto-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |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.zoneddatetime.prototype.toplainmonthday
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const zoneddatetime = new Temporal.ZonedDateTime(1682892000000000000n, 'Europe/Madrid', calendar);
+
+assert.throws(RangeError, () => zoneddatetime.toPlainMonthDay());
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/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/ZonedDateTime/prototype/toPlainMonthDay/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..047e4fee38
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,18 @@
+// |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.zoneddatetime.prototype.toplainmonthday
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.toPlainMonthDay());
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..85b9809e84
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/timezone-getoffsetnanosecondsfor-not-callable.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.zoneddatetime.prototype.toplainmonthday
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => datetime.toPlainMonthDay(),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..ee65dbb110
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,18 @@
+// |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.zoneddatetime.prototype.toplainmonthday
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.toPlainMonthDay());
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..a958952db6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/timezone-getoffsetnanosecondsfor-wrong-type.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.zoneddatetime.prototype.toplainmonthday
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.toPlainMonthDay());
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/balance-negative-time-units.js
new file mode 100644
index 0000000000..3cff09ad52
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/balance-negative-time-units.js
@@ -0,0 +1,42 @@
+// |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.zoneddatetime.prototype.toplaintime
+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-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-get-temporal.zoneddatetime.prototype.toplaintime step 5:
+ 5. Let _temporalDateTime_ be ? BuiltinTimeZoneGetPlainDateTimeFor(_timeZone_, _instant_, _zonedDateTime_.[[Calendar]]).
+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(1001n, tz);
+
+const time = datetime.toPlainTime();
+
+TemporalHelpers.assertPlainTime(time, 0, 0, 0, 0, 0, 999);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/branding.js
new file mode 100644
index 0000000000..6f719cd2ad
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/branding.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.zoneddatetime.prototype.toplaintime
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toPlainTime = Temporal.ZonedDateTime.prototype.toPlainTime;
+
+assert.sameValue(typeof toPlainTime, "function");
+
+assert.throws(TypeError, () => toPlainTime.call(undefined), "undefined");
+assert.throws(TypeError, () => toPlainTime.call(null), "null");
+assert.throws(TypeError, () => toPlainTime.call(true), "true");
+assert.throws(TypeError, () => toPlainTime.call(""), "empty string");
+assert.throws(TypeError, () => toPlainTime.call(Symbol()), "symbol");
+assert.throws(TypeError, () => toPlainTime.call(1), "1");
+assert.throws(TypeError, () => toPlainTime.call({}), "plain object");
+assert.throws(TypeError, () => toPlainTime.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime");
+assert.throws(TypeError, () => toPlainTime.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/builtin-timezone-no-observable-calls.js
new file mode 100644
index 0000000000..e34b4197f7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/builtin-timezone-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.zoneddatetime.prototype.toplaintime
+description: >
+ Calling the method on an instance constructed with a builtin time zone causes
+ no observable lookups or calls to time zone methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.toPlainTime();
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/builtin.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/builtin.js
new file mode 100644
index 0000000000..6899a40700
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/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.zoneddatetime.prototype.toplaintime
+description: >
+ Tests that Temporal.ZonedDateTime.prototype.toPlainTime
+ 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.ZonedDateTime.prototype.toPlainTime),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.toPlainTime),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.toPlainTime),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.ZonedDateTime.prototype.toPlainTime.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/length.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/length.js
new file mode 100644
index 0000000000..e74970d6af
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/length.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.zoneddatetime.prototype.toplaintime
+description: Temporal.ZonedDateTime.prototype.toPlainTime.length is 0
+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.ZonedDateTime.prototype.toPlainTime, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/name.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/name.js
new file mode 100644
index 0000000000..64b8e9422d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/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.zoneddatetime.prototype.toplaintime
+description: Temporal.ZonedDateTime.prototype.toPlainTime.name is "toPlainTime".
+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.ZonedDateTime.prototype.toPlainTime, "name", {
+ value: "toPlainTime",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/negative-epochnanoseconds.js
new file mode 100644
index 0000000000..726ec7c3d1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/negative-epochnanoseconds.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.zoneddatetime.prototype.toplaintime
+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 result = datetime.toPlainTime();
+TemporalHelpers.assertPlainTime(result, 16, 50, 35, 0, 0, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/not-a-constructor.js
new file mode 100644
index 0000000000..9d4cddde8f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/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.zoneddatetime.prototype.toplaintime
+description: >
+ Temporal.ZonedDateTime.prototype.toPlainTime 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.ZonedDateTime.prototype.toPlainTime();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.toPlainTime), false,
+ "isConstructor(Temporal.ZonedDateTime.prototype.toPlainTime)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/prop-desc.js
new file mode 100644
index 0000000000..970a345c05
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/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.zoneddatetime.prototype.toplaintime
+description: The "toPlainTime" property of Temporal.ZonedDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.ZonedDateTime.prototype.toPlainTime,
+ "function",
+ "`typeof ZonedDateTime.prototype.toPlainTime` is `function`"
+);
+
+verifyProperty(Temporal.ZonedDateTime.prototype, "toPlainTime", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/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/ZonedDateTime/prototype/toPlainTime/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..f16bb9deb7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,18 @@
+// |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.zoneddatetime.prototype.toplaintime
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.toPlainTime());
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..d4270af22d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/timezone-getoffsetnanosecondsfor-not-callable.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.zoneddatetime.prototype.toplaintime
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => datetime.toPlainTime(),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..e4313799c4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,18 @@
+// |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.zoneddatetime.prototype.toplaintime
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.toPlainTime());
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..6f8a84f651
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/timezone-getoffsetnanosecondsfor-wrong-type.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.zoneddatetime.prototype.toplaintime
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.toPlainTime());
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/branding.js
new file mode 100644
index 0000000000..0e03bffc9c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/branding.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.zoneddatetime.prototype.toplainyearmonth
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toPlainYearMonth = Temporal.ZonedDateTime.prototype.toPlainYearMonth;
+
+assert.sameValue(typeof toPlainYearMonth, "function");
+
+assert.throws(TypeError, () => toPlainYearMonth.call(undefined), "undefined");
+assert.throws(TypeError, () => toPlainYearMonth.call(null), "null");
+assert.throws(TypeError, () => toPlainYearMonth.call(true), "true");
+assert.throws(TypeError, () => toPlainYearMonth.call(""), "empty string");
+assert.throws(TypeError, () => toPlainYearMonth.call(Symbol()), "symbol");
+assert.throws(TypeError, () => toPlainYearMonth.call(1), "1");
+assert.throws(TypeError, () => toPlainYearMonth.call({}), "plain object");
+assert.throws(TypeError, () => toPlainYearMonth.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime");
+assert.throws(TypeError, () => toPlainYearMonth.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..7ed8f1b128
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/builtin-calendar-no-array-iteration.js
@@ -0,0 +1,23 @@
+// |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.zoneddatetime.prototype.toplainyearmonth
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.toPlainYearMonth();
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..50b48cca02
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,37 @@
+// |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.zoneddatetime.prototype.toplainyearmonth
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const fieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "fields");
+Object.defineProperty(Temporal.Calendar.prototype, "fields", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("fields should not be looked up");
+ },
+});
+const yearMonthFromFieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "yearMonthFromFields");
+Object.defineProperty(Temporal.Calendar.prototype, "yearMonthFromFields", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("yearMonthFromFields should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.toPlainYearMonth();
+
+Object.defineProperty(Temporal.Calendar.prototype, "fields", fieldsOriginal);
+Object.defineProperty(Temporal.Calendar.prototype, "yearMonthFromFields", yearMonthFromFieldsOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/builtin-timezone-no-observable-calls.js
new file mode 100644
index 0000000000..e2e1f07886
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/builtin-timezone-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.zoneddatetime.prototype.toplainyearmonth
+description: >
+ Calling the method on an instance constructed with a builtin time zone causes
+ no observable lookups or calls to time zone methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.toPlainYearMonth();
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/builtin.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/builtin.js
new file mode 100644
index 0000000000..0e3f0b1e41
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/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.zoneddatetime.prototype.toplainyearmonth
+description: >
+ Tests that Temporal.ZonedDateTime.prototype.toPlainYearMonth
+ 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.ZonedDateTime.prototype.toPlainYearMonth),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.toPlainYearMonth),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.toPlainYearMonth),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.ZonedDateTime.prototype.toPlainYearMonth.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/calendar-arguments.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/calendar-arguments.js
new file mode 100644
index 0000000000..9a492cf44b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/calendar-arguments.js
@@ -0,0 +1,32 @@
+// |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.zoneddatetime.prototype.toplainyearmonth
+description: Correct options value is passed to calendar method
+info: |
+ YearMonthFromFields ( calendar, fields [ , options ] )
+
+ 3. If options is not present, then
+ a. Set options to undefined.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ yearMonthFromFields(...args) {
+ assert.sameValue(args.length, 2, "args.length");
+ assert.sameValue(typeof args[0], "object", "args[0]");
+ assert.sameValue(args[1], undefined, "args[1]");
+ return super.yearMonthFromFields(...args);
+ }
+}
+const zonedDateTime = new Temporal.ZonedDateTime(957270896123456789n, "UTC", new CustomCalendar());
+const result = zonedDateTime.toPlainYearMonth();
+TemporalHelpers.assertPlainYearMonth(result, 2000, 5, "M05");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/calendar-fields-iterable.js
new file mode 100644
index 0000000000..a1acc85bf5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/calendar-fields-iterable.js
@@ -0,0 +1,30 @@
+// |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.zoneddatetime.prototype.toplainyearmonth
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.zoneddatetime.prototype.toplainyearmonth step 7:
+ 7. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "monthCode",
+ "year",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar);
+datetime.toPlainYearMonth();
+
+assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/calendar-fromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/calendar-fromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..19fd8176cb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/calendar-fromfields-called-with-null-prototype-fields.js
@@ -0,0 +1,18 @@
+// |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.zoneddatetime.prototype.toplainyearmonth
+description: >
+ Calendar.yearMonthFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.ZonedDateTime(0n, "UTC", calendar);
+instance.toPlainYearMonth();
+assert.sameValue(calendar.yearMonthFromFieldsCallCount, 1, "yearMonthFromFields should have been called on the calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/calendar-result.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/calendar-result.js
new file mode 100644
index 0000000000..eae729859d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/calendar-result.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.zoneddatetime.prototype.toplainyearmonth
+description: TypeError thrown when calendar method returns an object with the wrong brand
+info: |
+ YearMonthFromFields ( calendar, fields [ , options ] )
+
+ 4. Perform ? RequireInternalSlot(yearMonth, [[InitializedTemporalYearMonth]]).
+features: [Temporal]
+---*/
+
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ yearMonthFromFields() {
+ return {};
+ }
+}
+const zonedDateTime = new Temporal.ZonedDateTime(957270896123456789n, "UTC", new CustomCalendar());
+assert.throws(TypeError, () => zonedDateTime.toPlainYearMonth());
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/calendar-yearmonthfromfields-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/calendar-yearmonthfromfields-called-with-options-undefined.js
new file mode 100644
index 0000000000..4b0aba2901
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/calendar-yearmonthfromfields-called-with-options-undefined.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.zoneddatetime.prototype.toplainyearmonth
+description: >
+ Calendar.yearMonthFromFields method is called with undefined as the options
+ value when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar);
+instance.toPlainYearMonth();
+assert.sameValue(calendar.yearMonthFromFieldsCallCount, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..5d6e709f1e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/constructor-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |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.zoneddatetime.prototype.toplainyearmonth
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const zoneddatetime = new Temporal.ZonedDateTime(1682892000000000000n, 'Europe/Madrid', calendar);
+
+assert.throws(RangeError, () => zoneddatetime.toPlainYearMonth());
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/duplicate-calendar-fields.js
new file mode 100644
index 0000000000..0314561a11
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/duplicate-calendar-fields.js
@@ -0,0 +1,18 @@
+// |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.zoneddatetime.prototype.toplainyearmonth
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['monthCode'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const zoneddatetime = new Temporal.ZonedDateTime(1682892000000000000n, 'Europe/Madrid', calendar);
+
+ assert.throws(RangeError, () => zoneddatetime.toPlainYearMonth());
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/length.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/length.js
new file mode 100644
index 0000000000..29809a71ed
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/length.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.zoneddatetime.prototype.toplainyearmonth
+description: Temporal.ZonedDateTime.prototype.toPlainYearMonth.length is 0
+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.ZonedDateTime.prototype.toPlainYearMonth, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/name.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/name.js
new file mode 100644
index 0000000000..a40c2d3a18
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/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.zoneddatetime.prototype.toplainyearmonth
+description: Temporal.ZonedDateTime.prototype.toPlainYearMonth.name is "toPlainYearMonth".
+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.ZonedDateTime.prototype.toPlainYearMonth, "name", {
+ value: "toPlainYearMonth",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/not-a-constructor.js
new file mode 100644
index 0000000000..fa4bb26929
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/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.zoneddatetime.prototype.toplainyearmonth
+description: >
+ Temporal.ZonedDateTime.prototype.toPlainYearMonth 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.ZonedDateTime.prototype.toPlainYearMonth();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.toPlainYearMonth), false,
+ "isConstructor(Temporal.ZonedDateTime.prototype.toPlainYearMonth)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/order-of-operations.js
new file mode 100644
index 0000000000..7ff72c5f8a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/order-of-operations.js
@@ -0,0 +1,51 @@
+// |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.zoneddatetime.prototype.toplainyearmonth
+description: >
+ User-observable time zone and calendar accesses and calls in
+ toPlainYearMonth() happen the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "get this.calendar.fields",
+ "get this.calendar.yearMonthFromFields",
+ "get this.timeZone.getOffsetNanosecondsFor",
+ "call this.timeZone.getOffsetNanosecondsFor",
+ "call this.calendar.fields",
+ "get this.calendar.monthCode",
+ "call this.calendar.monthCode",
+ "get this.calendar.year",
+ "call this.calendar.year",
+ "call this.calendar.yearMonthFromFields",
+];
+const actual = [];
+
+const timeZone = TemporalHelpers.timeZoneObserver(actual, "this.timeZone");
+const calendar = TemporalHelpers.calendarObserver(actual, "this.calendar");
+const instance = new Temporal.ZonedDateTime(0n, timeZone, calendar);
+Object.defineProperties(instance, {
+ monthCode: {
+ get() {
+ actual.push("get this.monthCode");
+ return TemporalHelpers.toPrimitiveObserver(actual, "M01", "this.monthCode");
+ }
+ },
+ year: {
+ get() {
+ actual.push("get this.year");
+ return TemporalHelpers.toPrimitiveObserver(actual, 1970, "this.year");
+ }
+ },
+});
+// clear observable operations that occurred during the constructor call
+actual.splice(0);
+
+instance.toPlainYearMonth();
+assert.compareArray(actual, expected, "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/prop-desc.js
new file mode 100644
index 0000000000..cba0686500
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/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.zoneddatetime.prototype.toplainyearmonth
+description: The "toPlainYearMonth" property of Temporal.ZonedDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.ZonedDateTime.prototype.toPlainYearMonth,
+ "function",
+ "`typeof ZonedDateTime.prototype.toPlainYearMonth` is `function`"
+);
+
+verifyProperty(Temporal.ZonedDateTime.prototype, "toPlainYearMonth", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/proto-in-calendar-fields.js
new file mode 100644
index 0000000000..55c00cc95f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/proto-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |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.zoneddatetime.prototype.toplainyearmonth
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const zoneddatetime = new Temporal.ZonedDateTime(1682892000000000000n, 'Europe/Madrid', calendar);
+
+assert.throws(RangeError, () => zoneddatetime.toPlainYearMonth());
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/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/ZonedDateTime/prototype/toPlainYearMonth/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..9d207815b0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,18 @@
+// |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.zoneddatetime.prototype.toplainyearmonth
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.toPlainYearMonth());
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..0cdd95f305
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/timezone-getoffsetnanosecondsfor-not-callable.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.zoneddatetime.prototype.toplainyearmonth
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => datetime.toPlainYearMonth(),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..98681b1c71
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,18 @@
+// |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.zoneddatetime.prototype.toplainyearmonth
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.toPlainYearMonth());
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..45ab56c39d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/timezone-getoffsetnanosecondsfor-wrong-type.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.zoneddatetime.prototype.toplainyearmonth
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.toPlainYearMonth());
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/balance-negative-time-units.js
new file mode 100644
index 0000000000..dd736305e6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/balance-negative-time-units.js
@@ -0,0 +1,44 @@
+// |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.zoneddatetime.prototype.tostring
+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-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-temporalzoneddatetimetostring step 9:
+ 9. Let _dateTime_ be ? BuiltinTimeZoneGetPlainDateTimeFor(_timeZone_, _instant_, _isoCalendar_).
+ sec-get-temporal.zoneddatetime.prototype.tostring step 9:
+ 9. Return ? TemporalZonedDateTimeToString(_zonedDateTime_, _precision_.[[Precision]], _showCalendar_, _showTimeZone_, _showOffset_, _precision_.[[Increment]], _precision_.[[Unit]], _roundingMode_).
+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
+// ZonedDateTime
+const tz = TemporalHelpers.specificOffsetTimeZone(-2);
+const datetime = new Temporal.ZonedDateTime(1001n, tz);
+
+const isoString = datetime.toString();
+
+assert.sameValue(isoString, "1970-01-01T00:00:00.000000999+00:00[-00:00:00.000000002]");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/branding.js
new file mode 100644
index 0000000000..79580e4591
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/branding.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.zoneddatetime.prototype.tostring
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toString = Temporal.ZonedDateTime.prototype.toString;
+
+assert.sameValue(typeof toString, "function");
+
+assert.throws(TypeError, () => toString.call(undefined), "undefined");
+assert.throws(TypeError, () => toString.call(null), "null");
+assert.throws(TypeError, () => toString.call(true), "true");
+assert.throws(TypeError, () => toString.call(""), "empty string");
+assert.throws(TypeError, () => toString.call(Symbol()), "symbol");
+assert.throws(TypeError, () => toString.call(1), "1");
+assert.throws(TypeError, () => toString.call({}), "plain object");
+assert.throws(TypeError, () => toString.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime");
+assert.throws(TypeError, () => toString.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..f04b7be82c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.zoneddatetime.prototype.tostring
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const idOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "id");
+Object.defineProperty(Temporal.Calendar.prototype, "id", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("id should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.toString();
+
+Object.defineProperty(Temporal.Calendar.prototype, "id", idOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/builtin-timezone-no-observable-calls.js
new file mode 100644
index 0000000000..eab024a413
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/builtin-timezone-no-observable-calls.js
@@ -0,0 +1,37 @@
+// |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.zoneddatetime.prototype.tostring
+description: >
+ Calling the method on an instance constructed with a builtin time zone causes
+ no observable lookups or calls to time zone methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+const idOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "id");
+Object.defineProperty(Temporal.TimeZone.prototype, "id", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("id should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.toString();
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+Object.defineProperty(Temporal.TimeZone.prototype, "id", idOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/builtin.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/builtin.js
new file mode 100644
index 0000000000..a67286e337
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/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.zoneddatetime.prototype.tostring
+description: >
+ Tests that Temporal.ZonedDateTime.prototype.toString
+ 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.ZonedDateTime.prototype.toString),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.toString),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.toString),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.ZonedDateTime.prototype.toString.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/calendar-tostring.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/calendar-tostring.js
new file mode 100644
index 0000000000..da39782390
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/calendar-tostring.js
@@ -0,0 +1,56 @@
+// |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.zoneddatetime.protoype.tostring
+description: Number of observable 'toString' calls on the calendar for each value of calendarName
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+let calls;
+const customCalendar = {
+ get id() {
+ ++calls;
+ return "custom";
+ },
+ toString() {
+ TemporalHelpers.assertUnreachable('toString should not be called');
+ },
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+const date = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC", customCalendar);
+[
+ ["always", "2001-09-09T01:46:40.987654321+00:00[UTC][u-ca=custom]", 1],
+ ["auto", "2001-09-09T01:46:40.987654321+00:00[UTC][u-ca=custom]", 1],
+ ["critical", "2001-09-09T01:46:40.987654321+00:00[UTC][!u-ca=custom]", 1],
+ ["never", "2001-09-09T01:46:40.987654321+00:00[UTC]", 0],
+ [undefined, "2001-09-09T01:46:40.987654321+00:00[UTC][u-ca=custom]", 1],
+].forEach(([calendarName, expectedResult, expectedCalls]) => {
+ calls = 0;
+ const result = date.toString({ calendarName });
+ assert.sameValue(result, expectedResult, `id for calendarName = ${calendarName}`);
+ assert.sameValue(calls, expectedCalls, `calls to id getter for calendarName = ${calendarName}`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/calendarname-always.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/calendarname-always.js
new file mode 100644
index 0000000000..64da01fe8f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/calendarname-always.js
@@ -0,0 +1,48 @@
+// |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.zoneddatetime.prototype.tostring
+description: If calendarName is "always", the calendar ID should be included.
+features: [Temporal]
+---*/
+
+const calendarMethods = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+
+const tests = [
+ [[], "1970-01-01T01:01:01.987654321+00:00[UTC][u-ca=iso8601]", "built-in ISO"],
+ [[{ id: "custom", ...calendarMethods }], "1970-01-01T01:01:01.987654321+00:00[UTC][u-ca=custom]", "custom"],
+ [[{ id: "iso8601", ...calendarMethods }], "1970-01-01T01:01:01.987654321+00:00[UTC][u-ca=iso8601]", "custom with iso8601 id"],
+ [[{ id: "ISO8601", ...calendarMethods }], "1970-01-01T01:01:01.987654321+00:00[UTC][u-ca=ISO8601]", "custom with caps id"],
+ [[{ id: "\u0131so8601", ...calendarMethods }], "1970-01-01T01:01:01.987654321+00:00[UTC][u-ca=\u0131so8601]", "custom with dotless i id"],
+];
+
+for (const [args, expected, description] of tests) {
+ const date = new Temporal.ZonedDateTime(3661_987_654_321n, "UTC", ...args);
+ const result = date.toString({ calendarName: "always" });
+ assert.sameValue(result, expected, `${description} calendar for calendarName = always`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/calendarname-auto.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/calendarname-auto.js
new file mode 100644
index 0000000000..116a22818b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/calendarname-auto.js
@@ -0,0 +1,48 @@
+// |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.zoneddatetime.prototype.tostring
+description: If calendarName is "auto", "iso8601" should be omitted.
+features: [Temporal]
+---*/
+
+const calendarMethods = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+
+const tests = [
+ [[], "1970-01-01T01:01:01.987654321+00:00[UTC]", "built-in ISO"],
+ [[{ id: "custom", ...calendarMethods }], "1970-01-01T01:01:01.987654321+00:00[UTC][u-ca=custom]", "custom"],
+ [[{ id: "iso8601", ...calendarMethods }], "1970-01-01T01:01:01.987654321+00:00[UTC]", "custom with iso8601 id"],
+ [[{ id: "ISO8601", ...calendarMethods }], "1970-01-01T01:01:01.987654321+00:00[UTC][u-ca=ISO8601]", "custom with caps id"],
+ [[{ id: "\u0131so8601", ...calendarMethods }], "1970-01-01T01:01:01.987654321+00:00[UTC][u-ca=\u0131so8601]", "custom with dotless i id"],
+];
+
+for (const [args, expected, description] of tests) {
+ const date = new Temporal.ZonedDateTime(3661_987_654_321n, "UTC", ...args);
+ const result = date.toString({ calendarName: "auto" });
+ assert.sameValue(result, expected, `${description} calendar for calendarName = auto`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/calendarname-critical.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/calendarname-critical.js
new file mode 100644
index 0000000000..7939d2152e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/calendarname-critical.js
@@ -0,0 +1,50 @@
+// |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.zoneddatetime.prototype.tostring
+description: >
+ If calendarName is "calendar", the calendar ID should be included and prefixed
+ with "!".
+features: [Temporal]
+---*/
+
+const calendarMethods = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+
+const tests = [
+ [[], "1970-01-01T01:01:01.987654321+00:00[UTC][!u-ca=iso8601]", "built-in ISO"],
+ [[{ id: "custom", ...calendarMethods }], "1970-01-01T01:01:01.987654321+00:00[UTC][!u-ca=custom]", "custom"],
+ [[{ id: "iso8601", ...calendarMethods }], "1970-01-01T01:01:01.987654321+00:00[UTC][!u-ca=iso8601]", "custom with iso8601 id"],
+ [[{ id: "ISO8601", ...calendarMethods }], "1970-01-01T01:01:01.987654321+00:00[UTC][!u-ca=ISO8601]", "custom with caps id"],
+ [[{ id: "\u0131so8601", ...calendarMethods }], "1970-01-01T01:01:01.987654321+00:00[UTC][!u-ca=\u0131so8601]", "custom with dotless i id"],
+];
+
+for (const [args, expected, description] of tests) {
+ const date = new Temporal.ZonedDateTime(3661_987_654_321n, "UTC", ...args);
+ const result = date.toString({ calendarName: "critical" });
+ assert.sameValue(result, expected, `${description} calendar for calendarName = critical`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/calendarname-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/calendarname-invalid-string.js
new file mode 100644
index 0000000000..c352fb52d7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/calendarname-invalid-string.js
@@ -0,0 +1,29 @@
+// |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.zoneddatetime.protoype.tostring
+description: RangeError thrown when calendarName option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-toshowcalendaroption step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"calendarName"*, « *"string"* », « *"auto"*, *"always"*, *"never"*, *"critical"* », *"auto"*).
+ sec-temporal.zoneddatetime.protoype.tostring step 6:
+ 6. Let _showCalendar_ be ? ToShowCalendarOption(_options_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+const invalidValues = ["ALWAYS", "sometimes", "other string", "auto\0"];
+
+for (const calendarName of invalidValues) {
+ assert.throws(
+ RangeError,
+ () => datetime.toString({ calendarName }),
+ `${calendarName} is an invalid value for calendarName option`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/calendarname-never.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/calendarname-never.js
new file mode 100644
index 0000000000..3180df0569
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/calendarname-never.js
@@ -0,0 +1,48 @@
+// |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.zoneddatetime.prototype.tostring
+description: If calendarName is "never", the calendar ID should be omitted.
+features: [Temporal]
+---*/
+
+const calendarMethods = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+
+const tests = [
+ [[], "1970-01-01T01:01:01.987654321+00:00[UTC]", "built-in ISO"],
+ [[{ id: "custom", ...calendarMethods }], "1970-01-01T01:01:01.987654321+00:00[UTC]", "custom"],
+ [[{ id: "iso8601", ...calendarMethods }], "1970-01-01T01:01:01.987654321+00:00[UTC]", "custom with iso8601 id"],
+ [[{ id: "ISO8601", ...calendarMethods }], "1970-01-01T01:01:01.987654321+00:00[UTC]", "custom with caps id"],
+ [[{ id: "\u0131so8601", ...calendarMethods }], "1970-01-01T01:01:01.987654321+00:00[UTC]", "custom with dotless i id"],
+];
+
+for (const [args, expected, description] of tests) {
+ const date = new Temporal.ZonedDateTime(3661_987_654_321n, "UTC", ...args);
+ const result = date.toString({ calendarName: "never" });
+ assert.sameValue(result, expected, `${description} calendar for calendarName = never`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/calendarname-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/calendarname-undefined.js
new file mode 100644
index 0000000000..d1ad1f6ac9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/calendarname-undefined.js
@@ -0,0 +1,56 @@
+// |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.zoneddatetime.protoype.tostring
+description: Fallback value for calendarName option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-toshowcalendaroption step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"calendarName"*, « *"string"* », « *"auto"*, *"always"*, *"never"*, *"critical"* », *"auto"*).
+ sec-temporal.zoneddatetime.protoype.tostring step 6:
+ 6. Let _showCalendar_ be ? ToShowCalendarOption(_options_).
+features: [Temporal]
+---*/
+
+const calendarMethods = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+
+const tests = [
+ [[], "1970-01-01T01:01:01.987654321+00:00[UTC]", "built-in ISO"],
+ [[{ id: "custom", ...calendarMethods }], "1970-01-01T01:01:01.987654321+00:00[UTC][u-ca=custom]", "custom"],
+ [[{ id: "iso8601", ...calendarMethods }], "1970-01-01T01:01:01.987654321+00:00[UTC]", "custom with iso8601 id"],
+ [[{ id: "ISO8601", ...calendarMethods }], "1970-01-01T01:01:01.987654321+00:00[UTC][u-ca=ISO8601]", "custom with caps id"],
+ [[{ id: "\u0131so8601", ...calendarMethods }], "1970-01-01T01:01:01.987654321+00:00[UTC][u-ca=\u0131so8601]", "custom with dotless i id"],
+];
+
+for (const [args, expected, description] of tests) {
+ const datetime = new Temporal.ZonedDateTime(3661_987_654_321n, "UTC", ...args);
+ const result = datetime.toString({ calendarName: undefined });
+ assert.sameValue(result, expected, `default calendarName option is auto with ${description} calendar`);
+ // See options-object.js for {} and options-undefined.js for absent options arg
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/calendarname-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/calendarname-wrong-type.js
new file mode 100644
index 0000000000..5e4f67edf5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/calendarname-wrong-type.js
@@ -0,0 +1,49 @@
+// |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.zoneddatetime.protoype.tostring
+description: Type conversions for calendarName option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-toshowcalendaroption step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"calendarName"*, « *"string"* », « *"auto"*, *"always"*, *"never"*, *"critical"* », *"auto"*).
+ sec-temporal.zoneddatetime.protoype.tostring step 6:
+ 6. Let _showCalendar_ be ? ToShowCalendarOption(_options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = {
+ id: "custom",
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC", calendar);
+
+TemporalHelpers.checkStringOptionWrongType("calendarName", "auto",
+ (calendarName) => datetime.toString({ calendarName }),
+ (result, descr) => assert.sameValue(result, "2001-09-09T01:46:40.987654321+00:00[UTC][u-ca=custom]", descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-auto.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-auto.js
new file mode 100644
index 0000000000..303faec688
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-auto.js
@@ -0,0 +1,23 @@
+// |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.zoneddatetime.prototype.tostring
+description: auto value for fractionalSecondDigits option
+features: [BigInt, Temporal]
+---*/
+
+const tests = [
+ [new Temporal.ZonedDateTime(192_258_181_000_000_000n, "UTC"), "1976-02-04T05:03:01+00:00[UTC]"],
+ [new Temporal.ZonedDateTime(0n, "UTC"), "1970-01-01T00:00:00+00:00[UTC]"],
+ [new Temporal.ZonedDateTime(30_000_000_000n, "UTC"), "1970-01-01T00:00:30+00:00[UTC]"],
+ [new Temporal.ZonedDateTime(30_123_400_000n, "UTC"), "1970-01-01T00:00:30.1234+00:00[UTC]"],
+];
+
+for (const [datetime, expected] of tests) {
+ assert.sameValue(datetime.toString(), expected, "default is to emit seconds and drop trailing zeroes");
+ assert.sameValue(datetime.toString({ fractionalSecondDigits: "auto" }), expected, "auto is the default");
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-invalid-string.js
new file mode 100644
index 0000000000..4562775585
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-invalid-string.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.zoneddatetime.prototype.tostring
+description: RangeError thrown when fractionalSecondDigits option not one of the allowed string values
+info: |
+ sec-getstringornumberoption step 4:
+ 4. If _stringValues_ is not *undefined* and _stringValues_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-tosecondsstringprecision step 9:
+ 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
+ sec-temporal.zoneddatetime.prototype.tostring step 4:
+ 4. Let _precision_ be ? ToSecondsStringPrecision(_options_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_650_000n, "UTC");
+
+for (const fractionalSecondDigits of ["other string", "AUTO", "not-auto", "autos", "auto\0"]) {
+ assert.throws(RangeError, () => datetime.toString({ fractionalSecondDigits }),
+ `"${fractionalSecondDigits}" is not a valid value for fractionalSecondDigits`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-nan.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-nan.js
new file mode 100644
index 0000000000..66d886ee90
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-nan.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.zoneddatetime.prototype.tostring
+description: RangeError thrown when fractionalSecondDigits option is NaN
+info: |
+ sec-getoption step 8.b:
+ b. If _value_ is *NaN*, throw a *RangeError* exception.
+ sec-getstringornumberoption step 2:
+ 2. Let _value_ be ? GetOption(_options_, _property_, « Number, String », *undefined*, _fallback_).
+ sec-temporal-tosecondsstringprecision step 9:
+ 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
+ sec-temporal.zoneddatetime.prototype.tostring step 4:
+ 4. Let _precision_ be ? ToSecondsStringPrecision(_options_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_650_000n, "UTC");
+assert.throws(RangeError, () => datetime.toString({ fractionalSecondDigits: NaN }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-non-integer.js
new file mode 100644
index 0000000000..19064a373e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-non-integer.js
@@ -0,0 +1,32 @@
+// |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.zoneddatetime.prototype.tostring
+description: Rounding for fractionalSecondDigits option
+info: |
+ sec-getstringornumberoption step 3.b:
+ b. Return floor(ℝ(_value_)).
+ sec-temporal-tosecondsstringprecision step 9:
+ 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
+ sec-temporal.zoneddatetime.prototype.tostring step 4:
+ 4. Let _precision_ be ? ToSecondsStringPrecision(_options_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_650_000n, "UTC");
+
+let string = datetime.toString({ fractionalSecondDigits: 2.5 });
+assert.sameValue(string, "2001-09-09T01:46:40.98+00:00[UTC]", "fractionalSecondDigits 2.5 floors to 2");
+
+string = datetime.toString({ fractionalSecondDigits: 9.7 });
+assert.sameValue(string, "2001-09-09T01:46:40.987650000+00:00[UTC]", "fractionalSecondDigits 9.7 floors to 9 and is not out of range");
+
+assert.throws(
+ RangeError,
+ () => datetime.toString({ fractionalSecondDigits: -0.6 }),
+ "fractionalSecondDigits -0.6 floors to -1 and is out of range"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-number.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-number.js
new file mode 100644
index 0000000000..445b498ed2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-number.js
@@ -0,0 +1,39 @@
+// |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.zoneddatetime.prototype.tostring
+description: Number for fractionalSecondDigits option
+features: [BigInt, Temporal]
+---*/
+
+const fewSeconds = new Temporal.ZonedDateTime(192_258_181_000_000_000n, "UTC");
+const zeroSeconds = new Temporal.ZonedDateTime(0n, "UTC");
+const wholeSeconds = new Temporal.ZonedDateTime(30_000_000_000n, "UTC");
+const subSeconds = new Temporal.ZonedDateTime(30_123_400_000n, "UTC");
+
+assert.sameValue(fewSeconds.toString({ fractionalSecondDigits: 0 }), "1976-02-04T05:03:01+00:00[UTC]",
+ "pads parts with 0");
+assert.sameValue(subSeconds.toString({ fractionalSecondDigits: 0 }), "1970-01-01T00:00:30+00:00[UTC]",
+ "truncates 4 decimal places to 0");
+assert.sameValue(zeroSeconds.toString({ fractionalSecondDigits: 2 }), "1970-01-01T00:00:00.00+00:00[UTC]",
+ "pads zero seconds to 2 decimal places");
+assert.sameValue(wholeSeconds.toString({ fractionalSecondDigits: 2 }), "1970-01-01T00:00:30.00+00:00[UTC]",
+ "pads whole seconds to 2 decimal places");
+assert.sameValue(subSeconds.toString({ fractionalSecondDigits: 2 }), "1970-01-01T00:00:30.12+00:00[UTC]",
+ "truncates 4 decimal places to 2");
+assert.sameValue(subSeconds.toString({ fractionalSecondDigits: 3 }), "1970-01-01T00:00:30.123+00:00[UTC]",
+ "truncates 4 decimal places to 3");
+assert.sameValue(subSeconds.toString({ fractionalSecondDigits: 6 }), "1970-01-01T00:00:30.123400+00:00[UTC]",
+ "pads 4 decimal places to 6");
+assert.sameValue(zeroSeconds.toString({ fractionalSecondDigits: 7 }), "1970-01-01T00:00:00.0000000+00:00[UTC]",
+ "pads zero seconds to 7 decimal places");
+assert.sameValue(wholeSeconds.toString({ fractionalSecondDigits: 7 }), "1970-01-01T00:00:30.0000000+00:00[UTC]",
+ "pads whole seconds to 7 decimal places");
+assert.sameValue(subSeconds.toString({ fractionalSecondDigits: 7 }), "1970-01-01T00:00:30.1234000+00:00[UTC]",
+ "pads 4 decimal places to 7");
+assert.sameValue(subSeconds.toString({ fractionalSecondDigits: 9 }), "1970-01-01T00:00:30.123400000+00:00[UTC]",
+ "pads 4 decimal places to 9");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-out-of-range.js
new file mode 100644
index 0000000000..8b134d7b87
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-out-of-range.js
@@ -0,0 +1,29 @@
+// |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.zoneddatetime.prototype.tostring
+description: RangeError thrown when fractionalSecondDigits option out of range
+info: |
+ sec-getstringornumberoption step 3.a:
+ a. If _value_ < _minimum_ or _value_ > _maximum_, throw a *RangeError* exception.
+ sec-temporal-tosecondsstringprecision step 9:
+ 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
+ sec-temporal.zoneddatetime.prototype.tostring step 4:
+ 4. Let _precision_ be ? ToSecondsStringPrecision(_options_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_650_000n, "UTC");
+
+assert.throws(RangeError, () => datetime.toString({ fractionalSecondDigits: -Infinity }),
+ "−∞ is out of range for fractionalSecondDigits");
+assert.throws(RangeError, () => datetime.toString({ fractionalSecondDigits: -1 }),
+ "−1 is out of range for fractionalSecondDigits");
+assert.throws(RangeError, () => datetime.toString({ fractionalSecondDigits: 10 }),
+ "10 is out of range for fractionalSecondDigits");
+assert.throws(RangeError, () => datetime.toString({ fractionalSecondDigits: Infinity }),
+ "∞ is out of range for fractionalSecondDigits");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-undefined.js
new file mode 100644
index 0000000000..0f89571cab
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-undefined.js
@@ -0,0 +1,38 @@
+// |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.zoneddatetime.prototype.tostring
+description: Fallback value for fractionalSecondDigits option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-getstringornumberoption step 2:
+ 2. Let _value_ be ? GetOption(_options_, _property_, « Number, String », *undefined*, _fallback_).
+ sec-temporal-tosecondsstringprecision step 9:
+ 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
+ sec-temporal.zoneddatetime.prototype.tostring step 4:
+ 4. Let _precision_ be ? ToSecondsStringPrecision(_options_).
+features: [Temporal]
+---*/
+
+const tests = [
+ [new Temporal.ZonedDateTime(192_258_181_000_000_000n, "UTC"), "1976-02-04T05:03:01+00:00[UTC]"],
+ [new Temporal.ZonedDateTime(0n, "UTC"), "1970-01-01T00:00:00+00:00[UTC]"],
+ [new Temporal.ZonedDateTime(30_000_000_000n, "UTC"), "1970-01-01T00:00:30+00:00[UTC]"],
+ [new Temporal.ZonedDateTime(30_123_400_000n, "UTC"), "1970-01-01T00:00:30.1234+00:00[UTC]"],
+];
+
+for (const [datetime, expected] of tests) {
+ const explicit = datetime.toString({ fractionalSecondDigits: undefined });
+ assert.sameValue(explicit, expected, "default fractionalSecondDigits is auto (property present but undefined)");
+
+ const implicit = datetime.toString({});
+ assert.sameValue(implicit, expected, "default fractionalSecondDigits is auto (property not present)");
+
+ const lambda = datetime.toString(() => {});
+ assert.sameValue(lambda, expected, "default fractionalSecondDigits is auto (property not present, function object)");
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-wrong-type.js
new file mode 100644
index 0000000000..bedea182ec
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-wrong-type.js
@@ -0,0 +1,50 @@
+// |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.zoneddatetime.prototype.tostring
+description: Type conversions for fractionalSecondDigits option
+info: |
+ sec-getoption steps 8–9:
+ 8. Else if _type_ is Number, then
+ a. Set _value_ to ? ToNumber(value).
+ b. ...
+ 9. Else,
+ a. Set _value_ to ? ToString(value).
+ sec-getstringornumberoption step 2:
+ 2. Let _value_ be ? GetOption(_options_, _property_, « Number, String », *undefined*, _fallback_).
+ sec-temporal-tosecondsstringprecision step 9:
+ 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
+ sec-temporal.zoneddatetime.prototype.tostring step 4:
+ 4. Let _precision_ be ? ToSecondsStringPrecision(_options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_650_000n, "UTC");
+
+assert.throws(RangeError, () => datetime.toString({ fractionalSecondDigits: null }),
+ "null is not a number and converts to the string 'null' which is not valid for fractionalSecondDigits");
+assert.throws(RangeError, () => datetime.toString({ fractionalSecondDigits: true }),
+ "true is not a number and converts to the string 'true' which is not valid for fractionalSecondDigits");
+assert.throws(RangeError, () => datetime.toString({ fractionalSecondDigits: false }),
+ "false is not a number and converts to the string 'false' which is not valid for fractionalSecondDigits");
+assert.throws(TypeError, () => datetime.toString({ fractionalSecondDigits: Symbol() }),
+ "symbols are not numbers and cannot convert to strings");
+assert.throws(RangeError, () => datetime.toString({ fractionalSecondDigits: 2n }),
+ "bigints are not numbers and convert to strings which are not valid for fractionalSecondDigits");
+assert.throws(RangeError, () => datetime.toString({ fractionalSecondDigits: {} }),
+ "plain objects are not numbers and convert to strings which are not valid for fractionalSecondDigits");
+
+const expected = [
+ "get fractionalSecondDigits.toString",
+ "call fractionalSecondDigits.toString",
+];
+const actual = [];
+const observer = TemporalHelpers.toPrimitiveObserver(actual, "auto", "fractionalSecondDigits");
+const result = datetime.toString({ fractionalSecondDigits: observer });
+assert.sameValue(result, "2001-09-09T01:46:40.98765+00:00[UTC]", "object with toString uses toString return value");
+assert.compareArray(actual, expected, "object with toString calls toString and not valueOf");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/length.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/length.js
new file mode 100644
index 0000000000..dffb76116e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/length.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.zoneddatetime.prototype.tostring
+description: Temporal.ZonedDateTime.prototype.toString.length is 0
+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.ZonedDateTime.prototype.toString, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/name.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/name.js
new file mode 100644
index 0000000000..63fe389903
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/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.zoneddatetime.prototype.tostring
+description: Temporal.ZonedDateTime.prototype.toString.name is "toString".
+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.ZonedDateTime.prototype.toString, "name", {
+ value: "toString",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/negative-epochnanoseconds.js
new file mode 100644
index 0000000000..255a46d256
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/negative-epochnanoseconds.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.zoneddatetime.prototype.tostring
+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]
+---*/
+
+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 result = datetime.toString();
+assert.sameValue(result, "1969-07-24T16:50:35.000000001+00:00[UTC]");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/not-a-constructor.js
new file mode 100644
index 0000000000..753e7f306e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/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.zoneddatetime.prototype.tostring
+description: >
+ Temporal.ZonedDateTime.prototype.toString 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.ZonedDateTime.prototype.toString();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.toString), false,
+ "isConstructor(Temporal.ZonedDateTime.prototype.toString)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/offset-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/offset-invalid-string.js
new file mode 100644
index 0000000000..44f8d760ab
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/offset-invalid-string.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.zoneddatetime.protoype.tostring
+description: RangeError thrown when offset option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-toshowoffsetoption step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"offset"*, « String », « *"auto"*, *"never"* », *"auto"*).
+ sec-temporal.zoneddatetime.protoype.tostring step 8:
+ 8. Let _showOffset_ be ? ToShowOffsetOption(_options_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_64_321n, "UTC");
+assert.throws(RangeError, () => datetime.toString({ offset: "other string" }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/offset-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/offset-undefined.js
new file mode 100644
index 0000000000..030f5181cb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/offset-undefined.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.zoneddatetime.protoype.tostring
+description: Fallback value for offset option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-toshowoffsetoption step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"offset"*, « String », « *"auto"*, *"never"* », *"auto"*).
+ sec-temporal.zoneddatetime.protoype.tostring step 8:
+ 8. Let _showOffset_ be ? ToShowOffsetOption(_options_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+
+const explicit = datetime.toString({ offset: undefined });
+assert.sameValue(explicit, "2001-09-09T01:46:40.987654321+00:00[UTC]", "default offset option is auto");
+
+// See options-undefined.js for {}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/offset-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/offset-wrong-type.js
new file mode 100644
index 0000000000..8896eb0e57
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/offset-wrong-type.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.zoneddatetime.protoype.tostring
+description: Type conversions for offset option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-toshowoffsetoption step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"offset"*, « String », « *"auto"*, *"never"* », *"auto"*).
+ sec-temporal.zoneddatetime.protoype.tostring step 8:
+ 8. Let _showOffset_ be ? ToShowOffsetOption(_options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+
+TemporalHelpers.checkStringOptionWrongType("offset", "auto",
+ (offset) => datetime.toString({ offset }),
+ (result, descr) => assert.sameValue(result, "2001-09-09T01:46:40.987654321+00:00[UTC]", descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/offset.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/offset.js
new file mode 100644
index 0000000000..39896f5760
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/offset.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.zoneddatetime.prototype.tostring
+description: The time zone offset part of the string serialization
+features: [BigInt, Temporal]
+---*/
+
+function test(timeZoneIdentifier, expected, description) {
+ const timeZone = new Temporal.TimeZone(timeZoneIdentifier);
+ const datetime = new Temporal.ZonedDateTime(0n, timeZone);
+ assert.sameValue(datetime.toString(), expected, description);
+}
+
+test("UTC", "1970-01-01T00:00:00+00:00[UTC]", "offset of UTC is +00:00");
+test("+01:00", "1970-01-01T01:00:00+01:00[+01:00]", "positive offset");
+test("-05:00", "1969-12-31T19:00:00-05:00[-05:00]", "negative offset");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/options-object.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/options-object.js
new file mode 100644
index 0000000000..d1ee3ac976
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/options-object.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.zoneddatetime.prototype.tostring
+description: Empty or a function object may be used as options
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(0n, "UTC");
+
+const result1 = instance.toString({});
+assert.sameValue(
+ result1, "1970-01-01T00:00:00+00:00[UTC]",
+ "options may be an empty plain object"
+);
+
+const result2 = instance.toString(() => {});
+assert.sameValue(
+ result2, "1970-01-01T00:00:00+00:00[UTC]",
+ "options may be a function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/options-undefined.js
new file mode 100644
index 0000000000..5a74aefd16
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/options-undefined.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.zoneddatetime.prototype.tostring
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const calendar = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "custom",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+const datetime1 = new Temporal.ZonedDateTime(957270896_987_650_000n, "UTC");
+const datetime2 = new Temporal.ZonedDateTime(957270896_987_650_000n, "UTC", calendar);
+
+[
+ [datetime1, "2000-05-02T12:34:56.98765+00:00[UTC]"],
+ [datetime2, "2000-05-02T12:34:56.98765+00:00[UTC][u-ca=custom]"],
+].forEach(([datetime, expected]) => {
+ const explicit = datetime.toString(undefined);
+ assert.sameValue(explicit, expected, "default show options are auto, precision is auto, and no rounding");
+
+ const propertyImplicit = datetime.toString({});
+ assert.sameValue(propertyImplicit, expected, "default show options are auto, precision is auto, and no rounding");
+
+ const implicit = datetime.toString();
+ assert.sameValue(implicit, expected, "default show options are auto, precision is auto, and no rounding");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/options-wrong-type.js
new file mode 100644
index 0000000000..6ecfd96475
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/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.zoneddatetime.prototype.tostring
+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.ZonedDateTime(0n, "UTC");
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.toString(value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/order-of-operations.js
new file mode 100644
index 0000000000..ebe60ab561
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/order-of-operations.js
@@ -0,0 +1,93 @@
+// |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.zoneddatetime.prototype.tostring
+description: Properties on objects passed to toString() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "get options.calendarName",
+ "get options.calendarName.toString",
+ "call options.calendarName.toString",
+ "get options.fractionalSecondDigits",
+ "get options.fractionalSecondDigits.toString",
+ "call options.fractionalSecondDigits.toString",
+ "get options.offset",
+ "get options.offset.toString",
+ "call options.offset.toString",
+ "get options.roundingMode",
+ "get options.roundingMode.toString",
+ "call options.roundingMode.toString",
+ "get options.smallestUnit",
+ "get options.smallestUnit.toString",
+ "call options.smallestUnit.toString",
+ "get options.timeZoneName",
+ "get options.timeZoneName.toString",
+ "call options.timeZoneName.toString",
+ "get this.timeZone.getOffsetNanosecondsFor",
+ "call this.timeZone.getOffsetNanosecondsFor",
+ "get this.timeZone.id",
+ "get this.calendar.id",
+];
+const actual = [];
+
+const timeZone = TemporalHelpers.timeZoneObserver(actual, "this.timeZone");
+const calendar = TemporalHelpers.calendarObserver(actual, "this.calendar");
+const instance = new Temporal.ZonedDateTime(0n, timeZone, calendar);
+// clear observable operations that occurred during the constructor call
+actual.splice(0);
+
+instance.toString(
+ TemporalHelpers.propertyBagObserver(actual, {
+ fractionalSecondDigits: "auto",
+ roundingMode: "halfExpand",
+ smallestUnit: "millisecond",
+ offset: "auto",
+ timeZoneName: "auto",
+ calendarName: "auto",
+ }, "options"),
+);
+assert.compareArray(actual, expected, "order of operations");
+actual.splice(0); // clear
+
+// Same as above but without accessing options.smallestUnit.toString
+const expectedForFractionalSecondDigits = [
+ "get options.calendarName",
+ "get options.calendarName.toString",
+ "call options.calendarName.toString",
+ "get options.fractionalSecondDigits",
+ "get options.fractionalSecondDigits.toString",
+ "call options.fractionalSecondDigits.toString",
+ "get options.offset",
+ "get options.offset.toString",
+ "call options.offset.toString",
+ "get options.roundingMode",
+ "get options.roundingMode.toString",
+ "call options.roundingMode.toString",
+ "get options.smallestUnit",
+ "get options.timeZoneName",
+ "get options.timeZoneName.toString",
+ "call options.timeZoneName.toString",
+ "get this.timeZone.getOffsetNanosecondsFor",
+ "call this.timeZone.getOffsetNanosecondsFor",
+ "get this.timeZone.id",
+ "get this.calendar.id",
+];
+
+instance.toString(
+ TemporalHelpers.propertyBagObserver(actual, {
+ fractionalSecondDigits: "auto",
+ roundingMode: "halfExpand",
+ smallestUnit: undefined,
+ offset: "auto",
+ timeZoneName: "auto",
+ calendarName: "auto",
+ }, "options"),
+);
+assert.compareArray(actual, expectedForFractionalSecondDigits, "order of operations with smallestUnit undefined");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/prop-desc.js
new file mode 100644
index 0000000000..6e52f25f2a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/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.zoneddatetime.prototype.tostring
+description: The "toString" property of Temporal.ZonedDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.ZonedDateTime.prototype.toString,
+ "function",
+ "`typeof ZonedDateTime.prototype.toString` is `function`"
+);
+
+verifyProperty(Temporal.ZonedDateTime.prototype, "toString", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/rounding-cross-midnight.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/rounding-cross-midnight.js
new file mode 100644
index 0000000000..63856a0be9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/rounding-cross-midnight.js
@@ -0,0 +1,16 @@
+// |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.zoneddatetime.prototype.tostring
+description: Rounding can cross midnight
+features: [Temporal]
+---*/
+
+const zonedDateTime = new Temporal.ZonedDateTime(946_684_799_999_999_999n, "UTC"); // one nanosecond before 2000-01-01T00:00:00
+for (const roundingMode of ["ceil", "halfExpand"]) {
+ assert.sameValue(zonedDateTime.toString({ fractionalSecondDigits: 8, roundingMode }), "2000-01-01T00:00:00.00000000+00:00[UTC]");
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/rounding-direction.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/rounding-direction.js
new file mode 100644
index 0000000000..80f3f01f07
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/rounding-direction.js
@@ -0,0 +1,33 @@
+// |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.zoneddatetime.prototype.tostring
+description: Rounding down is towards the Big Bang, not the epoch or 1 BCE
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(-65_261_246_399_500_000_000n, "UTC"); // -000099-12-15T12:00:00.5Z
+assert.sameValue(
+ instance.toString({ smallestUnit: "second", roundingMode: "floor" }),
+ "-000099-12-15T12:00:00+00:00[UTC]",
+ "Rounding down is towards the Big Bang, not the epoch or 1 BCE"
+);
+assert.sameValue(
+ instance.toString({ smallestUnit: "second", roundingMode: "trunc" }),
+ "-000099-12-15T12:00:00+00:00[UTC]",
+ "Rounding down is towards the Big Bang, not the epoch or 1 BCE (roundingMode trunc)"
+);
+assert.sameValue(
+ instance.toString({ smallestUnit: "second", roundingMode: "ceil" }),
+ "-000099-12-15T12:00:01+00:00[UTC]",
+ "Rounding up is away from the Big Bang, not the epoch or 1 BCE (roundingMode ceil)"
+);
+assert.sameValue(
+ instance.toString({ smallestUnit: "second", roundingMode: "halfExpand" }),
+ "-000099-12-15T12:00:01+00:00[UTC]",
+ "Rounding up is away from the Big Bang, not the epoch or 1 BCE (roundingMode halfExpand)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-ceil.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-ceil.js
new file mode 100644
index 0000000000..fe8a3e9544
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-ceil.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.zoneddatetime.prototype.tostring
+description: ceil value for roundingMode option
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_123_987_500n, "UTC");
+
+const result1 = datetime.toString({ smallestUnit: "microsecond", roundingMode: "ceil" });
+assert.sameValue(result1, "2001-09-09T01:46:40.123988+00:00[UTC]",
+ "roundingMode is ceil (with 6 digits from smallestUnit)");
+
+const result2 = datetime.toString({ fractionalSecondDigits: 6, roundingMode: "ceil" });
+assert.sameValue(result2, "2001-09-09T01:46:40.123988+00:00[UTC]",
+ "roundingMode is ceil (with 6 digits from fractionalSecondDigits)");
+
+const result3 = datetime.toString({ smallestUnit: "millisecond", roundingMode: "ceil" });
+assert.sameValue(result3, "2001-09-09T01:46:40.124+00:00[UTC]",
+ "roundingMode is ceil (with 3 digits from smallestUnit)");
+
+const result4 = datetime.toString({ fractionalSecondDigits: 3, roundingMode: "ceil" });
+assert.sameValue(result4, "2001-09-09T01:46:40.124+00:00[UTC]",
+ "roundingMode is ceil (with 3 digits from fractionalSecondDigits)");
+
+const result5 = datetime.toString({ smallestUnit: "second", roundingMode: "ceil" });
+assert.sameValue(result5, "2001-09-09T01:46:41+00:00[UTC]",
+ "roundingMode is ceil (with 0 digits from smallestUnit)");
+
+const result6 = datetime.toString({ fractionalSecondDigits: 0, roundingMode: "ceil" });
+assert.sameValue(result6, "2001-09-09T01:46:41+00:00[UTC]",
+ "roundingMode is ceil (with 0 digits from fractionalSecondDigits)");
+
+const result7 = datetime.toString({ smallestUnit: "minute", roundingMode: "ceil" });
+assert.sameValue(result7, "2001-09-09T01:47+00:00[UTC]", "roundingMode is ceil (round to minute)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-expand.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-expand.js
new file mode 100644
index 0000000000..7cb024373e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-expand.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.zoneddatetime.prototype.tostring
+description: expand value for roundingMode option
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_123_987_500n, "UTC");
+
+const result1 = datetime.toString({ smallestUnit: "microsecond", roundingMode: "expand" });
+assert.sameValue(result1, "2001-09-09T01:46:40.123988+00:00[UTC]",
+ "roundingMode is expand (with 6 digits from smallestUnit)");
+
+const result2 = datetime.toString({ fractionalSecondDigits: 6, roundingMode: "expand" });
+assert.sameValue(result2, "2001-09-09T01:46:40.123988+00:00[UTC]",
+ "roundingMode is expand (with 6 digits from fractionalSecondDigits)");
+
+const result3 = datetime.toString({ smallestUnit: "millisecond", roundingMode: "expand" });
+assert.sameValue(result3, "2001-09-09T01:46:40.124+00:00[UTC]",
+ "roundingMode is expand (with 3 digits from smallestUnit)");
+
+const result4 = datetime.toString({ fractionalSecondDigits: 3, roundingMode: "expand" });
+assert.sameValue(result4, "2001-09-09T01:46:40.124+00:00[UTC]",
+ "roundingMode is expand (with 3 digits from fractionalSecondDigits)");
+
+const result5 = datetime.toString({ smallestUnit: "second", roundingMode: "expand" });
+assert.sameValue(result5, "2001-09-09T01:46:41+00:00[UTC]",
+ "roundingMode is expand (with 0 digits from smallestUnit)");
+
+const result6 = datetime.toString({ fractionalSecondDigits: 0, roundingMode: "expand" });
+assert.sameValue(result6, "2001-09-09T01:46:41+00:00[UTC]",
+ "roundingMode is expand (with 0 digits from fractionalSecondDigits)");
+
+const result7 = datetime.toString({ smallestUnit: "minute", roundingMode: "expand" });
+assert.sameValue(result7, "2001-09-09T01:47+00:00[UTC]", "roundingMode is expand (round to minute)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-floor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-floor.js
new file mode 100644
index 0000000000..6b8ef9ae7a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-floor.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.zoneddatetime.prototype.tostring
+description: floor value for roundingMode option
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_123_987_500n, "UTC");
+
+const result1 = datetime.toString({ smallestUnit: "microsecond", roundingMode: "floor" });
+assert.sameValue(result1, "2001-09-09T01:46:40.123987+00:00[UTC]",
+ "roundingMode is floor (with 6 digits from smallestUnit)");
+
+const result2 = datetime.toString({ fractionalSecondDigits: 6, roundingMode: "floor" });
+assert.sameValue(result2, "2001-09-09T01:46:40.123987+00:00[UTC]",
+ "roundingMode is floor (with 6 digits from fractionalSecondDigits)");
+
+const result3 = datetime.toString({ smallestUnit: "millisecond", roundingMode: "floor" });
+assert.sameValue(result3, "2001-09-09T01:46:40.123+00:00[UTC]",
+ "roundingMode is floor (with 3 digits from smallestUnit)");
+
+const result4 = datetime.toString({ fractionalSecondDigits: 3, roundingMode: "floor" });
+assert.sameValue(result4, "2001-09-09T01:46:40.123+00:00[UTC]",
+ "roundingMode is floor (with 3 digits from fractionalSecondDigits)");
+
+const result5 = datetime.toString({ smallestUnit: "second", roundingMode: "floor" });
+assert.sameValue(result5, "2001-09-09T01:46:40+00:00[UTC]",
+ "roundingMode is floor (with 0 digits from smallestUnit)");
+
+const result6 = datetime.toString({ fractionalSecondDigits: 0, roundingMode: "floor" });
+assert.sameValue(result6, "2001-09-09T01:46:40+00:00[UTC]",
+ "roundingMode is floor (with 0 digits from fractionalSecondDigits)");
+
+const result7 = datetime.toString({ smallestUnit: "minute", roundingMode: "floor" });
+assert.sameValue(result7, "2001-09-09T01:46+00:00[UTC]", "roundingMode is floor (round to minute)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-halfCeil.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-halfCeil.js
new file mode 100644
index 0000000000..be443375bc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-halfCeil.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.zoneddatetime.prototype.tostring
+description: halfCeil value for roundingMode option
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_123_987_500n, "UTC");
+
+const result1 = datetime.toString({ smallestUnit: "microsecond", roundingMode: "halfCeil" });
+assert.sameValue(result1, "2001-09-09T01:46:40.123988+00:00[UTC]",
+ "roundingMode is halfCeil (with 6 digits from smallestUnit)");
+
+const result2 = datetime.toString({ fractionalSecondDigits: 6, roundingMode: "halfCeil" });
+assert.sameValue(result2, "2001-09-09T01:46:40.123988+00:00[UTC]",
+ "roundingMode is halfCeil (with 6 digits from fractionalSecondDigits)");
+
+const result3 = datetime.toString({ smallestUnit: "millisecond", roundingMode: "halfCeil" });
+assert.sameValue(result3, "2001-09-09T01:46:40.124+00:00[UTC]",
+ "roundingMode is halfCeil (with 3 digits from smallestUnit)");
+
+const result4 = datetime.toString({ fractionalSecondDigits: 3, roundingMode: "halfCeil" });
+assert.sameValue(result4, "2001-09-09T01:46:40.124+00:00[UTC]",
+ "roundingMode is halfCeil (with 3 digits from fractionalSecondDigits)");
+
+const result5 = datetime.toString({ smallestUnit: "second", roundingMode: "halfCeil" });
+assert.sameValue(result5, "2001-09-09T01:46:40+00:00[UTC]",
+ "roundingMode is halfCeil (with 0 digits from smallestUnit)");
+
+const result6 = datetime.toString({ fractionalSecondDigits: 0, roundingMode: "halfCeil" });
+assert.sameValue(result6, "2001-09-09T01:46:40+00:00[UTC]",
+ "roundingMode is halfCeil (with 0 digits from fractionalSecondDigits)");
+
+const result7 = datetime.toString({ smallestUnit: "minute", roundingMode: "halfCeil" });
+assert.sameValue(result7, "2001-09-09T01:47+00:00[UTC]", "roundingMode is halfCeil (round to minute)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-halfEven.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-halfEven.js
new file mode 100644
index 0000000000..086ce192d4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-halfEven.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.zoneddatetime.prototype.tostring
+description: halfEven value for roundingMode option
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_123_987_500n, "UTC");
+
+const result1 = datetime.toString({ smallestUnit: "microsecond", roundingMode: "halfEven" });
+assert.sameValue(result1, "2001-09-09T01:46:40.123988+00:00[UTC]",
+ "roundingMode is halfEven (with 6 digits from smallestUnit)");
+
+const result2 = datetime.toString({ fractionalSecondDigits: 6, roundingMode: "halfEven" });
+assert.sameValue(result2, "2001-09-09T01:46:40.123988+00:00[UTC]",
+ "roundingMode is halfEven (with 6 digits from fractionalSecondDigits)");
+
+const result3 = datetime.toString({ smallestUnit: "millisecond", roundingMode: "halfEven" });
+assert.sameValue(result3, "2001-09-09T01:46:40.124+00:00[UTC]",
+ "roundingMode is halfEven (with 3 digits from smallestUnit)");
+
+const result4 = datetime.toString({ fractionalSecondDigits: 3, roundingMode: "halfEven" });
+assert.sameValue(result4, "2001-09-09T01:46:40.124+00:00[UTC]",
+ "roundingMode is halfEven (with 3 digits from fractionalSecondDigits)");
+
+const result5 = datetime.toString({ smallestUnit: "second", roundingMode: "halfEven" });
+assert.sameValue(result5, "2001-09-09T01:46:40+00:00[UTC]",
+ "roundingMode is halfEven (with 0 digits from smallestUnit)");
+
+const result6 = datetime.toString({ fractionalSecondDigits: 0, roundingMode: "halfEven" });
+assert.sameValue(result6, "2001-09-09T01:46:40+00:00[UTC]",
+ "roundingMode is halfEven (with 0 digits from fractionalSecondDigits)");
+
+const result7 = datetime.toString({ smallestUnit: "minute", roundingMode: "halfEven" });
+assert.sameValue(result7, "2001-09-09T01:47+00:00[UTC]", "roundingMode is halfEven (round to minute)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-halfExpand.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-halfExpand.js
new file mode 100644
index 0000000000..66bd63e1d2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-halfExpand.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.zoneddatetime.prototype.tostring
+description: halfExpand value for roundingMode option
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_123_987_500n, "UTC");
+
+const result1 = datetime.toString({ smallestUnit: "microsecond", roundingMode: "halfExpand" });
+assert.sameValue(result1, "2001-09-09T01:46:40.123988+00:00[UTC]",
+ "roundingMode is halfExpand (with 6 digits from smallestUnit)");
+
+const result2 = datetime.toString({ fractionalSecondDigits: 6, roundingMode: "halfExpand" });
+assert.sameValue(result2, "2001-09-09T01:46:40.123988+00:00[UTC]",
+ "roundingMode is halfExpand (with 6 digits from fractionalSecondDigits)");
+
+const result3 = datetime.toString({ smallestUnit: "millisecond", roundingMode: "halfExpand" });
+assert.sameValue(result3, "2001-09-09T01:46:40.124+00:00[UTC]",
+ "roundingMode is halfExpand (with 3 digits from smallestUnit)");
+
+const result4 = datetime.toString({ fractionalSecondDigits: 3, roundingMode: "halfExpand" });
+assert.sameValue(result4, "2001-09-09T01:46:40.124+00:00[UTC]",
+ "roundingMode is halfExpand (with 3 digits from fractionalSecondDigits)");
+
+const result5 = datetime.toString({ smallestUnit: "second", roundingMode: "halfExpand" });
+assert.sameValue(result5, "2001-09-09T01:46:40+00:00[UTC]",
+ "roundingMode is halfExpand (with 0 digits from smallestUnit)");
+
+const result6 = datetime.toString({ fractionalSecondDigits: 0, roundingMode: "halfExpand" });
+assert.sameValue(result6, "2001-09-09T01:46:40+00:00[UTC]",
+ "roundingMode is halfExpand (with 0 digits from fractionalSecondDigits)");
+
+const result7 = datetime.toString({ smallestUnit: "minute", roundingMode: "halfExpand" });
+assert.sameValue(result7, "2001-09-09T01:47+00:00[UTC]", "roundingMode is halfExpand (round to minute)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-halfFloor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-halfFloor.js
new file mode 100644
index 0000000000..0a901fd446
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-halfFloor.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.zoneddatetime.prototype.tostring
+description: halfFloor value for roundingMode option
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_123_987_500n, "UTC");
+
+const result1 = datetime.toString({ smallestUnit: "microsecond", roundingMode: "halfFloor" });
+assert.sameValue(result1, "2001-09-09T01:46:40.123987+00:00[UTC]",
+ "roundingMode is halfFloor (with 6 digits from smallestUnit)");
+
+const result2 = datetime.toString({ fractionalSecondDigits: 6, roundingMode: "halfFloor" });
+assert.sameValue(result2, "2001-09-09T01:46:40.123987+00:00[UTC]",
+ "roundingMode is halfFloor (with 6 digits from fractionalSecondDigits)");
+
+const result3 = datetime.toString({ smallestUnit: "millisecond", roundingMode: "halfFloor" });
+assert.sameValue(result3, "2001-09-09T01:46:40.124+00:00[UTC]",
+ "roundingMode is halfFloor (with 3 digits from smallestUnit)");
+
+const result4 = datetime.toString({ fractionalSecondDigits: 3, roundingMode: "halfFloor" });
+assert.sameValue(result4, "2001-09-09T01:46:40.124+00:00[UTC]",
+ "roundingMode is halfFloor (with 3 digits from fractionalSecondDigits)");
+
+const result5 = datetime.toString({ smallestUnit: "second", roundingMode: "halfFloor" });
+assert.sameValue(result5, "2001-09-09T01:46:40+00:00[UTC]",
+ "roundingMode is halfFloor (with 0 digits from smallestUnit)");
+
+const result6 = datetime.toString({ fractionalSecondDigits: 0, roundingMode: "halfFloor" });
+assert.sameValue(result6, "2001-09-09T01:46:40+00:00[UTC]",
+ "roundingMode is halfFloor (with 0 digits from fractionalSecondDigits)");
+
+const result7 = datetime.toString({ smallestUnit: "minute", roundingMode: "halfFloor" });
+assert.sameValue(result7, "2001-09-09T01:47+00:00[UTC]", "roundingMode is halfFloor (round to minute)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-halfTrunc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-halfTrunc.js
new file mode 100644
index 0000000000..0ceac7ccce
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-halfTrunc.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.zoneddatetime.prototype.tostring
+description: halfTrunc value for roundingMode option
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_123_987_500n, "UTC");
+
+const result1 = datetime.toString({ smallestUnit: "microsecond", roundingMode: "halfTrunc" });
+assert.sameValue(result1, "2001-09-09T01:46:40.123987+00:00[UTC]",
+ "roundingMode is halfTrunc (with 6 digits from smallestUnit)");
+
+const result2 = datetime.toString({ fractionalSecondDigits: 6, roundingMode: "halfTrunc" });
+assert.sameValue(result2, "2001-09-09T01:46:40.123987+00:00[UTC]",
+ "roundingMode is halfTrunc (with 6 digits from fractionalSecondDigits)");
+
+const result3 = datetime.toString({ smallestUnit: "millisecond", roundingMode: "halfTrunc" });
+assert.sameValue(result3, "2001-09-09T01:46:40.124+00:00[UTC]",
+ "roundingMode is halfTrunc (with 3 digits from smallestUnit)");
+
+const result4 = datetime.toString({ fractionalSecondDigits: 3, roundingMode: "halfTrunc" });
+assert.sameValue(result4, "2001-09-09T01:46:40.124+00:00[UTC]",
+ "roundingMode is halfTrunc (with 3 digits from fractionalSecondDigits)");
+
+const result5 = datetime.toString({ smallestUnit: "second", roundingMode: "halfTrunc" });
+assert.sameValue(result5, "2001-09-09T01:46:40+00:00[UTC]",
+ "roundingMode is halfTrunc (with 0 digits from smallestUnit)");
+
+const result6 = datetime.toString({ fractionalSecondDigits: 0, roundingMode: "halfTrunc" });
+assert.sameValue(result6, "2001-09-09T01:46:40+00:00[UTC]",
+ "roundingMode is halfTrunc (with 0 digits from fractionalSecondDigits)");
+
+const result7 = datetime.toString({ smallestUnit: "minute", roundingMode: "halfTrunc" });
+assert.sameValue(result7, "2001-09-09T01:47+00:00[UTC]", "roundingMode is halfTrunc (round to minute)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-invalid-string.js
new file mode 100644
index 0000000000..566c1f9f70
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-invalid-string.js
@@ -0,0 +1,16 @@
+// |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.zoneddatetime.prototype.tostring
+description: RangeError thrown when roundingMode option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_123_987_500n, "UTC");
+for (const roundingMode of ["other string", "cile", "CEIL", "ce\u0131l", "auto", "halfexpand", "floor\0"]) {
+ assert.throws(RangeError, () => datetime.toString({ smallestUnit: "microsecond", roundingMode }));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-trunc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-trunc.js
new file mode 100644
index 0000000000..8e125466fc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-trunc.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.zoneddatetime.prototype.tostring
+description: trunc value for roundingMode option
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_123_987_500n, "UTC");
+
+const result1 = datetime.toString({ smallestUnit: "microsecond", roundingMode: "trunc" });
+assert.sameValue(result1, "2001-09-09T01:46:40.123987+00:00[UTC]",
+ "roundingMode is trunc (with 6 digits from smallestUnit)");
+
+const result2 = datetime.toString({ fractionalSecondDigits: 6, roundingMode: "trunc" });
+assert.sameValue(result2, "2001-09-09T01:46:40.123987+00:00[UTC]",
+ "roundingMode is trunc (with 6 digits from fractionalSecondDigits)");
+
+const result3 = datetime.toString({ smallestUnit: "millisecond", roundingMode: "trunc" });
+assert.sameValue(result3, "2001-09-09T01:46:40.123+00:00[UTC]",
+ "roundingMode is trunc (with 3 digits from smallestUnit)");
+
+const result4 = datetime.toString({ fractionalSecondDigits: 3, roundingMode: "trunc" });
+assert.sameValue(result4, "2001-09-09T01:46:40.123+00:00[UTC]",
+ "roundingMode is trunc (with 3 digits from fractionalSecondDigits)");
+
+const result5 = datetime.toString({ smallestUnit: "second", roundingMode: "trunc" });
+assert.sameValue(result5, "2001-09-09T01:46:40+00:00[UTC]",
+ "roundingMode is trunc (with 0 digits from smallestUnit)");
+
+const result6 = datetime.toString({ fractionalSecondDigits: 0, roundingMode: "trunc" });
+assert.sameValue(result6, "2001-09-09T01:46:40+00:00[UTC]",
+ "roundingMode is trunc (with 0 digits from fractionalSecondDigits)");
+
+const result7 = datetime.toString({ smallestUnit: "minute", roundingMode: "trunc" });
+assert.sameValue(result7, "2001-09-09T01:46+00:00[UTC]", "roundingMode is trunc (round to minute)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-undefined.js
new file mode 100644
index 0000000000..550ab18630
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-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.zoneddatetime.prototype.tostring
+description: Fallback value for roundingMode option
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_123_987_500n, "UTC");
+
+const explicit1 = datetime.toString({ smallestUnit: "microsecond", roundingMode: undefined });
+assert.sameValue(explicit1, "2001-09-09T01:46:40.123987+00:00[UTC]", "default roundingMode is trunc");
+const implicit1 = datetime.toString({ smallestUnit: "microsecond" });
+assert.sameValue(implicit1, "2001-09-09T01:46:40.123987+00:00[UTC]", "default roundingMode is trunc");
+
+const explicit2 = datetime.toString({ smallestUnit: "millisecond", roundingMode: undefined });
+assert.sameValue(explicit2, "2001-09-09T01:46:40.123+00:00[UTC]", "default roundingMode is trunc");
+const implicit2 = datetime.toString({ smallestUnit: "millisecond" });
+assert.sameValue(implicit2, "2001-09-09T01:46:40.123+00:00[UTC]", "default roundingMode is trunc");
+
+const explicit3 = datetime.toString({ smallestUnit: "second", roundingMode: undefined });
+assert.sameValue(explicit3, "2001-09-09T01:46:40+00:00[UTC]", "default roundingMode is trunc");
+const implicit3 = datetime.toString({ smallestUnit: "second" });
+assert.sameValue(implicit3, "2001-09-09T01:46:40+00:00[UTC]", "default roundingMode is trunc");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-wrong-type.js
new file mode 100644
index 0000000000..2f79532055
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-wrong-type.js
@@ -0,0 +1,18 @@
+// |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.zoneddatetime.prototype.tostring
+description: Type conversions for roundingMode option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_123_987_500n, "UTC");
+TemporalHelpers.checkStringOptionWrongType("roundingMode", "trunc",
+ (roundingMode) => datetime.toString({ smallestUnit: "microsecond", roundingMode }),
+ (result, descr) => assert.sameValue(result, "2001-09-09T01:46:40.123987+00:00[UTC]", descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/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/ZonedDateTime/prototype/toString/smallestunit-fractionalseconddigits.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/smallestunit-fractionalseconddigits.js
new file mode 100644
index 0000000000..6e8e3f2fef
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/smallestunit-fractionalseconddigits.js
@@ -0,0 +1,33 @@
+// |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.zoneddatetime.prototype.tostring
+description: fractionalSecondDigits option is not used with smallestUnit present
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(56_789_999_999n, "UTC");
+const tests = [
+ ["minute", "1970-01-01T00:00+00:00[UTC]"],
+ ["second", "1970-01-01T00:00:56+00:00[UTC]"],
+ ["millisecond", "1970-01-01T00:00:56.789+00:00[UTC]"],
+ ["microsecond", "1970-01-01T00:00:56.789999+00:00[UTC]"],
+ ["nanosecond", "1970-01-01T00:00:56.789999999+00:00[UTC]"],
+];
+
+for (const [smallestUnit, expected] of tests) {
+ const string = datetime.toString({
+ smallestUnit,
+ fractionalSecondDigits: 5,
+ });
+ assert.sameValue(string, expected, `smallestUnit: "${smallestUnit}" overrides fractionalSecondDigits`);
+}
+
+assert.throws(RangeError, () => datetime.toString({
+ smallestUnit: "hour",
+ fractionalSecondDigits: 5,
+}), "hour is an invalid smallestUnit but still overrides fractionalSecondDigits");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/smallestunit-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/smallestunit-invalid-string.js
new file mode 100644
index 0000000000..8a45ad4214
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/smallestunit-invalid-string.js
@@ -0,0 +1,40 @@
+// |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.zoneddatetime.prototype.tostring
+description: RangeError thrown when smallestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_123_987_500n, "UTC");
+const badValues = [
+ "era",
+ "eraYear",
+ "year",
+ "month",
+ "week",
+ "day",
+ "hour",
+ "millisecond\0",
+ "mill\u0131second",
+ "SECOND",
+ "eras",
+ "eraYears",
+ "years",
+ "months",
+ "weeks",
+ "days",
+ "hours",
+ "milliseconds\0",
+ "mill\u0131seconds",
+ "SECONDS",
+ "other string",
+];
+for (const smallestUnit of badValues) {
+ assert.throws(RangeError, () => datetime.toString({ smallestUnit }),
+ `"${smallestUnit}" is not a valid value for smallest unit`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/smallestunit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/smallestunit-plurals-accepted.js
new file mode 100644
index 0000000000..b67d82bd0e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/smallestunit-plurals-accepted.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.zoneddatetime.prototype.tostring
+description: Plural units are accepted as well for the smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_123_456_789n, "UTC");
+const validUnits = [
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+];
+TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => datetime.toString({ smallestUnit }), validUnits);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/smallestunit-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/smallestunit-undefined.js
new file mode 100644
index 0000000000..2a4ee1855e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/smallestunit-undefined.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.zoneddatetime.prototype.tostring
+description: Fallback value for smallestUnit option
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_123_987_500n, "UTC");
+
+const explicit1 = datetime.toString({ smallestUnit: undefined, fractionalSecondDigits: 6 });
+assert.sameValue(explicit1, "2001-09-09T01:46:40.123987+00:00[UTC]", "default smallestUnit defers to fractionalSecondDigits");
+const implicit1 = datetime.toString({ fractionalSecondDigits: 6 });
+assert.sameValue(implicit1, "2001-09-09T01:46:40.123987+00:00[UTC]", "default smallestUnit defers to fractionalSecondDigits");
+
+const explicit2 = datetime.toString({ smallestUnit: undefined, fractionalSecondDigits: 3 });
+assert.sameValue(explicit2, "2001-09-09T01:46:40.123+00:00[UTC]", "default smallestUnit defers to fractionalSecondDigits");
+const implicit2 = datetime.toString({ fractionalSecondDigits: 3 });
+assert.sameValue(implicit2, "2001-09-09T01:46:40.123+00:00[UTC]", "default smallestUnit defers to fractionalSecondDigits");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/smallestunit-valid-units.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/smallestunit-valid-units.js
new file mode 100644
index 0000000000..5058c1467e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/smallestunit-valid-units.js
@@ -0,0 +1,58 @@
+// |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.zoneddatetime.prototype.tostring
+description: Valid units for the smallestUnit option
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_123_456_789n, "UTC");
+
+function test(instance, expectations, description) {
+ for (const [smallestUnit, expectedResult] of expectations) {
+ assert.sameValue(instance.toString({ smallestUnit }), expectedResult,
+ `${description} with smallestUnit "${smallestUnit}"`);
+ }
+}
+
+test(
+ datetime,
+ [
+ ["minute", "2001-09-09T01:46+00:00[UTC]"],
+ ["second", "2001-09-09T01:46:40+00:00[UTC]"],
+ ["millisecond", "2001-09-09T01:46:40.123+00:00[UTC]"],
+ ["microsecond", "2001-09-09T01:46:40.123456+00:00[UTC]"],
+ ["nanosecond", "2001-09-09T01:46:40.123456789+00:00[UTC]"],
+ ],
+ "subseconds toString"
+);
+
+test(
+ new Temporal.ZonedDateTime(999_999_960_000_000_000n, "UTC"),
+ [
+ ["minute", "2001-09-09T01:46+00:00[UTC]"],
+ ["second", "2001-09-09T01:46:00+00:00[UTC]"],
+ ["millisecond", "2001-09-09T01:46:00.000+00:00[UTC]"],
+ ["microsecond", "2001-09-09T01:46:00.000000+00:00[UTC]"],
+ ["nanosecond", "2001-09-09T01:46:00.000000000+00:00[UTC]"],
+ ],
+ "whole minutes toString"
+);
+
+const notValid = [
+ "era",
+ "year",
+ "month",
+ "week",
+ "day",
+ "hour",
+];
+
+notValid.forEach((smallestUnit) => {
+ assert.throws(RangeError, () => datetime.toString({ smallestUnit }),
+ `"${smallestUnit}" is not a valid unit for the smallestUnit option`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/smallestunit-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/smallestunit-wrong-type.js
new file mode 100644
index 0000000000..cc085c40cc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/smallestunit-wrong-type.js
@@ -0,0 +1,18 @@
+// |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.zoneddatetime.prototype.tostring
+description: Type conversions for smallestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_123_987_500n, "UTC");
+TemporalHelpers.checkStringOptionWrongType("smallestUnit", "microsecond",
+ (smallestUnit) => datetime.toString({ smallestUnit }),
+ (result, descr) => assert.sameValue(result, "2001-09-09T01:46:40.123987+00:00[UTC]", descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..91c0ea1627
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,18 @@
+// |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.zoneddatetime.prototype.tostring
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.toString());
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..54bff8833d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezone-getoffsetnanosecondsfor-not-callable.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.zoneddatetime.prototype.tostring
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => datetime.toString(),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..7ae4e736d1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,18 @@
+// |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.zoneddatetime.prototype.tostring
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.toString());
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..dac816905c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezone-getoffsetnanosecondsfor-wrong-type.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.zoneddatetime.prototype.tostring
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.toString());
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezone-id-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezone-id-wrong-type.js
new file mode 100644
index 0000000000..68370146ff
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezone-id-wrong-type.js
@@ -0,0 +1,40 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Justin Grant. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.equals
+description: TypeError thrown if time zone reports an id that is not a String
+features: [Temporal]
+---*/
+
+class CustomTimeZone extends Temporal.TimeZone {
+ constructor(id) {
+ super("UTC");
+ this._id = id;
+ }
+ get id() {
+ return this._id;
+ }
+}
+
+[
+ undefined,
+ null,
+ true,
+ -1000,
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ {
+ valueOf() {
+ return 3600_000_000_000;
+ }
+ }
+].forEach((wrongId) => {
+ const timeZone = new CustomTimeZone(wrongId);
+ const zdt = Temporal.ZonedDateTime.from({ year: 1970, month: 1, day: 1, timeZone });
+ assert.throws(TypeError, () => zdt.toString());
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezonename-auto.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezonename-auto.js
new file mode 100644
index 0000000000..a743f25031
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezonename-auto.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.zoneddatetime.prototype.tostring
+description: If timeZoneName is "auto", the time zone ID should be included.
+features: [Temporal]
+---*/
+
+const tests = [
+ ["UTC", "1970-01-01T01:01:01.987654321+00:00[UTC]", "built-in UTC"],
+ ["+01:00", "1970-01-01T02:01:01.987654321+01:00[+01:00]", "built-in offset"],
+ [{
+ getOffsetNanosecondsFor() { return 0; },
+ getPossibleInstantsFor() { return []; },
+ id: "Etc/Custom",
+ }, "1970-01-01T01:01:01.987654321+00:00[Etc/Custom]", "custom"],
+];
+
+for (const [timeZone, expected, description] of tests) {
+ const date = new Temporal.ZonedDateTime(3661_987_654_321n, timeZone);
+ const result = date.toString({ timeZoneName: "auto" });
+ assert.sameValue(result, expected, `${description} time zone for timeZoneName = auto`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezonename-critical.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezonename-critical.js
new file mode 100644
index 0000000000..c4209394e9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezonename-critical.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.zoneddatetime.prototype.tostring
+description: >
+ If timeZoneName is "auto", the time zone ID should be included and prefixed
+ with "!"
+features: [Temporal]
+---*/
+
+const tests = [
+ ["UTC", "1970-01-01T01:01:01.987654321+00:00[!UTC]", "built-in UTC"],
+ ["+01:00", "1970-01-01T02:01:01.987654321+01:00[!+01:00]", "built-in offset"],
+ [{
+ getOffsetNanosecondsFor() { return 0; },
+ getPossibleInstantsFor() { return []; },
+ id: "Etc/Custom",
+ }, "1970-01-01T01:01:01.987654321+00:00[!Etc/Custom]", "custom"],
+];
+
+for (const [timeZone, expected, description] of tests) {
+ const date = new Temporal.ZonedDateTime(3661_987_654_321n, timeZone);
+ const result = date.toString({ timeZoneName: "critical" });
+ assert.sameValue(result, expected, `${description} time zone for timeZoneName = critical`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezonename-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezonename-invalid-string.js
new file mode 100644
index 0000000000..4e6f47c7c7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezonename-invalid-string.js
@@ -0,0 +1,29 @@
+// |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.zoneddatetime.protoype.tostring
+description: RangeError thrown when timeZoneName option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-toshowtimezonenameoption step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"timeZoneName"*, « *"string"* », « *"auto"*, *"never"*, *"critical"* », *"auto"*).
+ sec-temporal.zoneddatetime.protoype.tostring step 7:
+ 7. Let _showTimeZone_ be ? ToShowTimeZoneNameOption(_options_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_64_321n, "UTC");
+const invalidValues = ["NEVER", "sometimes", "other string", "auto\0"];
+
+for (const timeZoneName of invalidValues) {
+ assert.throws(
+ RangeError,
+ () => datetime.toString({ timeZoneName }),
+ `${timeZoneName} is an invalid value for timeZoneName option`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezonename-never.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezonename-never.js
new file mode 100644
index 0000000000..9fcb10d071
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezonename-never.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.zoneddatetime.prototype.tostring
+description: If timeZoneName is "never", the time zone ID should be omitted.
+features: [Temporal]
+---*/
+
+const tests = [
+ ["UTC", "1970-01-01T01:01:01.987654321+00:00", "built-in UTC"],
+ ["+01:00", "1970-01-01T02:01:01.987654321+01:00", "built-in offset"],
+ [{
+ getOffsetNanosecondsFor() { return 0; },
+ getPossibleInstantsFor() { return []; },
+ id: "Etc/Custom",
+ }, "1970-01-01T01:01:01.987654321+00:00", "custom"],
+];
+
+for (const [timeZone, expected, description] of tests) {
+ const date = new Temporal.ZonedDateTime(3661_987_654_321n, timeZone);
+ const result = date.toString({ timeZoneName: "never" });
+ assert.sameValue(result, expected, `${description} time zone for timeZoneName = never`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezonename-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezonename-undefined.js
new file mode 100644
index 0000000000..12a60c3d1b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezonename-undefined.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.zoneddatetime.protoype.tostring
+description: Fallback value for timeZoneName option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-toshowtimezonenameoption step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"timeZoneName"*, « *"string"* », « *"auto"*, *"never"*, *"critical"* », *"auto"*).
+ sec-temporal.zoneddatetime.protoype.tostring step 7:
+ 7. Let _showTimeZone_ be ? ToShowTimeZoneNameOption(_options_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+
+const explicit = datetime.toString({ timeZoneName: undefined });
+assert.sameValue(explicit, "2001-09-09T01:46:40.987654321+00:00[UTC]", "default timeZoneName option is auto");
+
+// See options-object.js for {} and options-undefined.js for absent
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezonename-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezonename-wrong-type.js
new file mode 100644
index 0000000000..672dc77901
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezonename-wrong-type.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.zoneddatetime.protoype.tostring
+description: Type conversions for timeZoneName option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-toshowtimezonenameoption step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"timeZoneName"*, « *"string"* », « *"auto"*, *"never"*, *"critical"* », *"auto"*).
+ sec-temporal.zoneddatetime.protoype.tostring step 7:
+ 7. Let _showTimeZone_ be ? ToShowTimeZoneNameOption(_options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+
+TemporalHelpers.checkStringOptionWrongType("timeZoneName", "auto",
+ (timeZoneName) => datetime.toString({ timeZoneName }),
+ (result, descr) => assert.sameValue(result, "2001-09-09T01:46:40.987654321+00:00[UTC]", descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/year-format.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/year-format.js
new file mode 100644
index 0000000000..f90960cf9f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/year-format.js
@@ -0,0 +1,58 @@
+// |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.zoneddatetime.prototype.tostring
+description: Verify that the year is appropriately formatted as 4 or 6 digits
+features: [Temporal]
+---*/
+
+function epochNsInYear(year) {
+ // Return an epoch nanoseconds value near the middle of the given year
+ const avgNsPerYear = 31_556_952_000_000_000n;
+ return (year - 1970n) * avgNsPerYear + (avgNsPerYear / 2n);
+}
+
+const utc = new Temporal.TimeZone("UTC");
+
+let instance = new Temporal.ZonedDateTime(epochNsInYear(-100000n), utc);
+assert.sameValue(instance.toString(), "-100000-07-01T21:30:36+00:00[UTC]", "large negative year formatted as 6-digit");
+
+instance = new Temporal.ZonedDateTime(epochNsInYear(-10000n), utc);
+assert.sameValue(instance.toString(), "-010000-07-01T21:30:36+00:00[UTC]", "smallest 5-digit negative year formatted as 6-digit");
+
+instance = new Temporal.ZonedDateTime(epochNsInYear(-9999n), utc);
+assert.sameValue(instance.toString(), "-009999-07-02T03:19:48+00:00[UTC]", "largest 4-digit negative year formatted as 6-digit");
+
+instance = new Temporal.ZonedDateTime(epochNsInYear(-1000n), utc);
+assert.sameValue(instance.toString(), "-001000-07-02T09:30:36+00:00[UTC]", "smallest 4-digit negative year formatted as 6-digit");
+
+instance = new Temporal.ZonedDateTime(epochNsInYear(-999n), utc);
+assert.sameValue(instance.toString(), "-000999-07-02T15:19:48+00:00[UTC]", "largest 3-digit negative year formatted as 6-digit");
+
+instance = new Temporal.ZonedDateTime(epochNsInYear(-1n), utc);
+assert.sameValue(instance.toString(), "-000001-07-02T15:41:24+00:00[UTC]", "year -1 formatted as 6-digit");
+
+instance = new Temporal.ZonedDateTime(epochNsInYear(0n), utc);
+assert.sameValue(instance.toString(), "0000-07-01T21:30:36+00:00[UTC]", "year 0 formatted as 4-digit");
+
+instance = new Temporal.ZonedDateTime(epochNsInYear(1n), utc);
+assert.sameValue(instance.toString(), "0001-07-02T03:19:48+00:00[UTC]", "year 1 formatted as 4-digit");
+
+instance = new Temporal.ZonedDateTime(epochNsInYear(999n), utc);
+assert.sameValue(instance.toString(), "0999-07-02T03:41:24+00:00[UTC]", "largest 3-digit positive year formatted as 4-digit");
+
+instance = new Temporal.ZonedDateTime(epochNsInYear(1000n), utc);
+assert.sameValue(instance.toString(), "1000-07-02T09:30:36+00:00[UTC]", "smallest 4-digit positive year formatted as 4-digit");
+
+instance = new Temporal.ZonedDateTime(epochNsInYear(9999n), utc);
+assert.sameValue(instance.toString(), "9999-07-02T15:41:24+00:00[UTC]", "largest 4-digit positive year formatted as 4-digit");
+
+instance = new Temporal.ZonedDateTime(epochNsInYear(10000n), utc);
+assert.sameValue(instance.toString(), "+010000-07-01T21:30:36+00:00[UTC]", "smallest 5-digit positive year formatted as 6-digit");
+
+instance = new Temporal.ZonedDateTime(epochNsInYear(100000n), utc);
+assert.sameValue(instance.toString(), "+100000-07-01T21:30:36+00:00[UTC]", "large positive year formatted as 6-digit");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toStringTag/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toStringTag/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toStringTag/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toStringTag/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toStringTag/prop-desc.js
new file mode 100644
index 0000000000..44b8978e72
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toStringTag/prop-desc.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.zoneddatetime.prototype-@@tostringtag
+description: The @@toStringTag property of Temporal.ZonedDateTime
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.ZonedDateTime.prototype, Symbol.toStringTag, {
+ value: "Temporal.ZonedDateTime",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toStringTag/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toStringTag/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toStringTag/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..2c1b07d2f0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-builtin-calendar-no-array-iteration.js
@@ -0,0 +1,25 @@
+// |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.zoneddatetime.prototype.until
+description: >
+ Calling the method with a property bag argument with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const timeZone = "UTC";
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+const arg = { year: 2000, month: 5, day: 2, hour: 21, minute: 43, second: 5, timeZone, calendar: "iso8601" };
+instance.until(arg);
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-ambiguous-wall-clock-time.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-ambiguous-wall-clock-time.js
new file mode 100644
index 0000000000..43819a54b9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-ambiguous-wall-clock-time.js
@@ -0,0 +1,93 @@
+// |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.zoneddatetime.prototype.until
+description: >
+ Correct time zone calls are made when converting a ZonedDateTime-like property
+ bag denoting an ambiguous wall-clock time
+includes: [temporalHelpers.js, compareArray.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+
+const dstTimeZone = TemporalHelpers.springForwardFallBackTimeZone();
+const dstTimeZoneObserver = TemporalHelpers.timeZoneObserver(actual, "timeZone", {
+ getOffsetNanosecondsFor: dstTimeZone.getOffsetNanosecondsFor.bind(dstTimeZone),
+ getPossibleInstantsFor: dstTimeZone.getPossibleInstantsFor.bind(dstTimeZone),
+});
+const calendar = TemporalHelpers.calendarObserver(actual, "calendar");
+
+const timeZone = "UTC";
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+
+let arg = { year: 2000, month: 4, day: 2, hour: 2, minute: 30, timeZone: dstTimeZoneObserver, calendar };
+instance.until(arg);
+
+const expected = [
+ // GetTemporalCalendarSlotValueWithISODefault
+ "has calendar.dateAdd",
+ "has calendar.dateFromFields",
+ "has calendar.dateUntil",
+ "has calendar.day",
+ "has calendar.dayOfWeek",
+ "has calendar.dayOfYear",
+ "has calendar.daysInMonth",
+ "has calendar.daysInWeek",
+ "has calendar.daysInYear",
+ "has calendar.fields",
+ "has calendar.id",
+ "has calendar.inLeapYear",
+ "has calendar.mergeFields",
+ "has calendar.month",
+ "has calendar.monthCode",
+ "has calendar.monthDayFromFields",
+ "has calendar.monthsInYear",
+ "has calendar.weekOfYear",
+ "has calendar.year",
+ "has calendar.yearMonthFromFields",
+ "has calendar.yearOfWeek",
+ // lookup in ToTemporalZonedDateTime
+ "get calendar.dateFromFields",
+ "get calendar.fields",
+ // CalendarFields
+ "call calendar.fields",
+ // ToTemporalTimeZoneSlotValue
+ "has timeZone.getOffsetNanosecondsFor",
+ "has timeZone.getPossibleInstantsFor",
+ "has timeZone.id",
+ // InterpretTemporalDateTimeFields
+ "call calendar.dateFromFields",
+ // lookup in ToTemporalZonedDateTime
+ "get timeZone.getOffsetNanosecondsFor",
+ "get timeZone.getPossibleInstantsFor",
+ // InterpretISODateTimeOffset
+ "call timeZone.getPossibleInstantsFor",
+];
+
+const expectedSpringForward = expected.concat([
+ // DisambiguatePossibleInstants
+ "call timeZone.getOffsetNanosecondsFor",
+ "call timeZone.getOffsetNanosecondsFor",
+ "call timeZone.getPossibleInstantsFor",
+]);
+assert.compareArray(
+ actual.slice(0, expectedSpringForward.length), // ignore operations after ToTemporalZonedDateTime
+ expectedSpringForward,
+ "order of operations converting property bag at skipped wall-clock time"
+);
+actual.splice(0); // clear
+
+arg = { year: 2000, month: 10, day: 29, hour: 1, minute: 30, timeZone: dstTimeZoneObserver, calendar };
+instance.until(arg);
+
+assert.compareArray(
+ actual.slice(0, expected.length), // ignore operations after ToTemporalZonedDateTime
+ expected,
+ "order of operations converting property bag at repeated wall-clock time"
+);
+actual.splice(0); // clear
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-calendar-case-insensitive.js
new file mode 100644
index 0000000000..961a1ee8db
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-calendar-case-insensitive.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.zoneddatetime.prototype.until
+description: The calendar name is case-insensitive
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+
+const calendar = "IsO8601";
+
+const arg = { year: 1970, monthCode: "M01", day: 1, timeZone, calendar };
+const result = instance.until(arg);
+TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-calendar-leap-second.js
new file mode 100644
index 0000000000..347b6c0c73
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-calendar-leap-second.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.zoneddatetime.prototype.until
+description: Leap second is a valid ISO string for a calendar in a property bag
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+
+const calendar = "2016-12-31T23:59:60+00:00[UTC]";
+
+const arg = { year: 1970, monthCode: "M01", day: 1, timeZone, calendar };
+const result = instance.until(arg);
+TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "leap second is a valid ISO string for calendar"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-calendar-number.js
new file mode 100644
index 0000000000..e8fa71f417
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-calendar-number.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.zoneddatetime.prototype.until
+description: A number as calendar in a property bag is not accepted
+features: [Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+
+const numbers = [
+ 1,
+ 19970327,
+ -19970327,
+ 1234567890,
+];
+
+for (const calendar of numbers) {
+ const arg = { year: 1970, monthCode: "M01", day: 1, timeZone, calendar };
+ assert.throws(
+ TypeError,
+ () => instance.until(arg),
+ "Numbers cannot be used as a calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-calendar-string.js
new file mode 100644
index 0000000000..379559e1ee
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-calendar-string.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.zoneddatetime.prototype.until
+description: A calendar ID is valid input for Calendar
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+
+const calendar = "iso8601";
+
+const arg = { year: 1970, monthCode: "M01", day: 1, timeZone, calendar };
+const result = instance.until(arg);
+TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, `Calendar created from string "${calendar}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-calendar-wrong-type.js
new file mode 100644
index 0000000000..7a69647935
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-calendar-wrong-type.js
@@ -0,0 +1,46 @@
+// |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.zoneddatetime.prototype.until
+description: >
+ Appropriate error thrown when a calendar property from a property bag cannot
+ be converted to a calendar object or string
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [calendar, description] of primitiveTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ typeof calendar === 'string' ? RangeError : TypeError,
+ () => instance.until(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+ [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields()
+];
+
+for (const [calendar, description] of typeErrorTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(TypeError, () => instance.until(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/ZonedDateTime/prototype/until/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-calendar-year-zero.js
new file mode 100644
index 0000000000..f0c915198e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-calendar-year-zero.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.zoneddatetime.prototype.until
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T17:45",
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+01:00",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.until(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-getpossibleinstantsfor-called-with-iso8601-calendar.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-getpossibleinstantsfor-called-with-iso8601-calendar.js
new file mode 100644
index 0000000000..02570557e5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-getpossibleinstantsfor-called-with-iso8601-calendar.js
@@ -0,0 +1,59 @@
+// |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.zoneddatetime.prototype.until
+description: >
+ Time zone's getPossibleInstantsFor is called with a PlainDateTime with the
+ built-in ISO 8601 calendar
+features: [Temporal]
+info: |
+ DisambiguatePossibleInstants:
+ 2. Let _n_ be _possibleInstants_'s length.
+ ...
+ 5. Assert: _n_ = 0.
+ ...
+ 19. If _disambiguation_ is *"earlier"*, then
+ ...
+ c. Let _earlierDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*).
+ d. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _earlierDateTime_).
+ ...
+ 20. Assert: _disambiguation_ is *"compatible"* or *"later"*.
+ ...
+ 23. Let _laterDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*).
+ 24. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _laterDateTime_).
+---*/
+
+class SkippedDateTime extends Temporal.TimeZone {
+ constructor() {
+ super("UTC");
+ this.calls = 0;
+ }
+
+ getPossibleInstantsFor(dateTime) {
+ // Calls occur in pairs. For the first one return no possible instants so
+ // that DisambiguatePossibleInstants will call it again
+ if (this.calls++ % 2 == 0) {
+ return [];
+ }
+
+ assert.sameValue(
+ dateTime.getISOFields().calendar,
+ "iso8601",
+ "getPossibleInstantsFor called with dateTime with built-in ISO 8601 calendar"
+ );
+ return super.getPossibleInstantsFor(dateTime);
+ }
+}
+
+const nonBuiltinISOCalendar = new Temporal.Calendar("iso8601");
+const timeZone = new SkippedDateTime();
+const arg = { year: 2000, month: 5, day: 2, timeZone, calendar: nonBuiltinISOCalendar };
+
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+instance.until(arg);
+
+assert.sameValue(timeZone.calls, 2, "getPossibleInstantsFor should have been called 2 times");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-invalid-offset-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-invalid-offset-string.js
new file mode 100644
index 0000000000..ef4bb27e4e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-invalid-offset-string.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.zoneddatetime.prototype.until
+description: Property bag with offset property is rejected if offset is in the wrong format
+features: [Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+
+const badOffsets = [
+ "00:00", // missing sign
+ "+0", // too short
+ "-000:00", // too long
+ 0, // must be a string
+ null, // must be a string
+ true, // must be a string
+ 1000n, // must be a string
+];
+badOffsets.forEach((offset) => {
+ const arg = { year: 2021, month: 10, day: 28, offset, timeZone };
+ assert.throws(
+ typeof(offset) === 'string' ? RangeError : TypeError,
+ () => instance.until(arg),
+ `"${offset} is not a valid offset string`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-offset-not-agreeing-with-timezone.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-offset-not-agreeing-with-timezone.js
new file mode 100644
index 0000000000..09ff638ad8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-offset-not-agreeing-with-timezone.js
@@ -0,0 +1,17 @@
+// |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.zoneddatetime.prototype.until
+description: Property bag with offset property is rejected if offset does not agree with time zone
+features: [Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("+01:00");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+
+const properties = { year: 2021, month: 10, day: 28, offset: "-07:00", timeZone };
+assert.throws(RangeError, () => instance.until(properties), "offset property not matching time zone is rejected");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..562b925709
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-getoffsetnanosecondsfor-non-integer.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.zoneddatetime.prototype.until
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+ const properties = { year: 2004, month: 11, day: 9, hour: 11, minute: 33, second: 20, timeZone };
+ timeZone.getPossibleInstantsFor = function () {
+ return [];
+ };
+ assert.throws(RangeError, () => datetime.until(properties, { largestUnit: "days" }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..b52d5f81e9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-getoffsetnanosecondsfor-not-callable.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.zoneddatetime.prototype.until
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+ const properties = { year: 2004, month: 11, day: 9, hour: 11, minute: 33, second: 20, timeZone };
+ timeZone.getPossibleInstantsFor = function () {
+ return [];
+ };
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => datetime.until(properties, { largestUnit: "days" }),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..6fb40a0e8e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.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.zoneddatetime.prototype.until
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+ const properties = { year: 2004, month: 11, day: 9, hour: 11, minute: 33, second: 20, timeZone };
+ timeZone.getPossibleInstantsFor = function () {
+ return [];
+ };
+ assert.throws(RangeError, () => datetime.until(properties, { largestUnit: "days" }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..d243987018
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,31 @@
+// |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.zoneddatetime.prototype.until
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+ const properties = { year: 2004, month: 11, day: 9, hour: 11, minute: 33, second: 20, timeZone };
+ timeZone.getPossibleInstantsFor = function () {
+ return [];
+ };
+ assert.throws(TypeError, () => datetime.until(properties, { largestUnit: "days" }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-id-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-id-wrong-type.js
new file mode 100644
index 0000000000..c11efec771
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-id-wrong-type.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Justin Grant. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.until
+description: TypeError thrown if time zone reports an id that is not a String
+features: [Temporal]
+---*/
+
+class CustomTimeZone extends Temporal.TimeZone {
+ constructor(id) {
+ super("UTC");
+ this._id = id;
+ }
+ get id() {
+ return this._id;
+ }
+}
+
+[
+ undefined,
+ null,
+ true,
+ -1000,
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ {
+ valueOf() {
+ return 3600_000_000_000;
+ }
+ }
+].forEach((wrongId) => {
+ const timeZone = new CustomTimeZone(wrongId);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+ const properties = { year: 2004, month: 11, day: 9, hour: 11, minute: 33, second: 20, timeZone };
+ assert.throws(TypeError, () => datetime.until(properties, { largestUnit: "days" }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-string-datetime.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-string-datetime.js
new file mode 100644
index 0000000000..94f4001ee4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-string-datetime.js
@@ -0,0 +1,69 @@
+// |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.zoneddatetime.prototype.until
+description: Conversion of ISO date-time strings to Temporal.TimeZone instances
+features: [Temporal]
+---*/
+
+let expectedTimeZone = "UTC";
+const instance1 = new Temporal.ZonedDateTime(0n, expectedTimeZone);
+
+let timeZone = "2021-08-19T17:30";
+assert.throws(RangeError, () => instance1.until({ year: 2020, month: 5, day: 2, timeZone }), "bare date-time string is not a time zone");
+
+[
+ "2021-08-19T17:30-07:00:01",
+ "2021-08-19T17:30-07:00:00",
+ "2021-08-19T17:30-07:00:00.1",
+ "2021-08-19T17:30-07:00:00.0",
+ "2021-08-19T17:30-07:00:00.01",
+ "2021-08-19T17:30-07:00:00.00",
+ "2021-08-19T17:30-07:00:00.001",
+ "2021-08-19T17:30-07:00:00.000",
+ "2021-08-19T17:30-07:00:00.0001",
+ "2021-08-19T17:30-07:00:00.0000",
+ "2021-08-19T17:30-07:00:00.00001",
+ "2021-08-19T17:30-07:00:00.00000",
+ "2021-08-19T17:30-07:00:00.000001",
+ "2021-08-19T17:30-07:00:00.000000",
+ "2021-08-19T17:30-07:00:00.0000001",
+ "2021-08-19T17:30-07:00:00.0000000",
+ "2021-08-19T17:30-07:00:00.00000001",
+ "2021-08-19T17:30-07:00:00.00000000",
+ "2021-08-19T17:30-07:00:00.000000001",
+ "2021-08-19T17:30-07:00:00.000000000",
+].forEach((timeZone) => {
+ assert.throws(
+ RangeError,
+ () => instance1.until({ year: 2020, month: 5, day: 2, timeZone }),
+ `ISO string ${timeZone} with a sub-minute offset is not a valid time zone`
+ );
+});
+
+// The following are all valid strings so should not throw. They should produce
+// expectedTimeZone, so additionally the operation will not throw due to the
+// time zones being different on the receiver and the argument.
+
+timeZone = "2021-08-19T17:30Z";
+instance1.until({ year: 2020, month: 5, day: 2, timeZone });
+
+expectedTimeZone = "-07:00";
+const instance2 = new Temporal.ZonedDateTime(0n, expectedTimeZone);
+timeZone = "2021-08-19T17:30-07:00";
+instance2.until({ year: 2020, month: 5, day: 2, timeZone });
+
+expectedTimeZone = "UTC";
+const instance3 = new Temporal.ZonedDateTime(0n, expectedTimeZone);
+timeZone = "2021-08-19T17:30[UTC]";
+instance3.until({ year: 2020, month: 5, day: 2, timeZone });
+
+timeZone = "2021-08-19T17:30Z[UTC]";
+instance3.until({ year: 2020, month: 5, day: 2, timeZone });
+
+timeZone = "2021-08-19T17:30-07:00[UTC]";
+instance3.until({ year: 2020, month: 5, day: 2, timeZone });
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-string-leap-second.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-string-leap-second.js
new file mode 100644
index 0000000000..9ba561195b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-string-leap-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.zoneddatetime.prototype.until
+description: Leap second is a valid ISO string for TimeZone
+features: [Temporal]
+---*/
+
+const expectedTimeZone = "UTC";
+const instance = new Temporal.ZonedDateTime(0n, expectedTimeZone);
+let timeZone = "2016-12-31T23:59:60+00:00[UTC]";
+
+// This operation should produce expectedTimeZone, so the following operation
+// should not throw due to the time zones being different on the receiver and
+// the argument.
+
+instance.until({ year: 2020, month: 5, day: 2, timeZone });
+
+timeZone = "2021-08-19T17:30:45.123456789+23:59[+23:59:60]";
+assert.throws(RangeError, () => instance.until({ year: 2020, month: 5, day: 2, timeZone }), "leap second in time zone name not valid");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-string-multiple-offsets.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-string-multiple-offsets.js
new file mode 100644
index 0000000000..2be403f13e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-string-multiple-offsets.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.zoneddatetime.prototype.until
+description: Time zone parsing from ISO strings uses the bracketed offset, not the ISO string offset
+features: [Temporal]
+---*/
+
+const expectedTimeZone = "+01:46";
+const instance = new Temporal.ZonedDateTime(0n, expectedTimeZone);
+const timeZone = "2021-08-19T17:30:45.123456789-12:12[+01:46]";
+
+// This operation should produce expectedTimeZone, so the following operation
+// should not throw due to the time zones being different on the receiver and
+// the argument.
+
+instance.until({ year: 2020, month: 5, day: 2, timeZone });
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-string-year-zero.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-string-year-zero.js
new file mode 100644
index 0000000000..0882be9c80
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-string-year-zero.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.zoneddatetime.prototype.until
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+const instance = new Temporal.ZonedDateTime(0n, new Temporal.TimeZone("UTC"));
+invalidStrings.forEach((timeZone) => {
+ assert.throws(
+ RangeError,
+ () => instance.until({ year: 2020, month: 5, day: 2, timeZone }),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-string.js
new file mode 100644
index 0000000000..d2c99517b2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-string.js
@@ -0,0 +1,38 @@
+// |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.zoneddatetime.prototype.until
+description: Time zone IDs are valid input for a time zone
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getPossibleInstantsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getPossibleInstantsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be looked up");
+ },
+});
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+
+const instance1 = new Temporal.ZonedDateTime(0n, new Temporal.TimeZone("UTC"));
+assert(instance1.until({ year: 1970, month: 1, day: 1, timeZone: "UTC" }).blank, "Time zone created from string 'UTC'");
+
+const instance2 = new Temporal.ZonedDateTime(0n, new Temporal.TimeZone("-01:30"));
+assert(instance2.until({ year: 1969, month: 12, day: 31, hour: 22, minute: 30, timeZone: "-01:30" }).blank, "Time zone created from string '-01:30'");
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", getPossibleInstantsForOriginal);
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-wrong-type.js
new file mode 100644
index 0000000000..cc9d5d6e7b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-wrong-type.js
@@ -0,0 +1,42 @@
+// |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.zoneddatetime.prototype.until
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or object for TimeZone
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(0n, new Temporal.TimeZone("UTC"));
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [19761118, "number that would convert to a valid ISO string in other contexts"],
+ [1n, "bigint"],
+];
+
+for (const [timeZone, description] of primitiveTests) {
+ assert.throws(
+ typeof timeZone === 'string' ? RangeError : TypeError,
+ () => instance.until({ year: 2020, month: 5, day: 2, timeZone }),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "object not implementing time zone protocol"],
+ [new Temporal.Calendar("iso8601"), "calendar instance"],
+];
+
+for (const [timeZone, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.until({ year: 2020, month: 5, day: 2, timeZone }), `${description} is not a valid object and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..3f50c229cf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-string-calendar-annotation.js
@@ -0,0 +1,31 @@
+// |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.zoneddatetime.prototype.until
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["1970-01-01T00:00[UTC][u-ca=iso8601]", "without !"],
+ ["1970-01-01T00:00[UTC][!u-ca=iso8601]", "with !"],
+ ["1970-01-01T00:00[UTC][u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.until(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/ZonedDateTime/prototype/until/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..79896e6656
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-string-critical-unknown-annotation.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.zoneddatetime.prototype.until
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01T00:00[UTC][!foo=bar]",
+ "1970-01-01T00:00[UTC][!foo=bar][u-ca=iso8601]",
+ "1970-01-01T00:00[UTC][u-ca=iso8601][!foo=bar]",
+ "1970-01-01T00:00[foo=bar][!_foo-bar0=Dont-Ignore-This-99999999999]",
+];
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.until(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..1949680722
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-string-date-with-utc-offset.js
@@ -0,0 +1,47 @@
+// |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.zoneddatetime.prototype.until
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+
+const validStrings = [
+ "1970-01-01T00Z[UTC]",
+ "1970-01-01T00Z[!UTC]",
+ "1970-01-01T00+00:00[UTC]",
+ "1970-01-01T00+00:00[!UTC]",
+];
+
+for (const arg of validStrings) {
+ const result = instance.until(arg);
+
+ TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ `"${arg}" is a valid UTC offset with time for ZonedDateTime`
+ );
+}
+
+const invalidStrings = [
+ "2022-09-15Z[UTC]",
+ "2022-09-15Z[Europe/Vienna]",
+ "2022-09-15+00:00[UTC]",
+ "2022-09-15-02:30[America/St_Johns]",
+];
+
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => instance.until(arg),
+ `"${arg}" UTC offset without time is not valid for ZonedDateTime`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..ee56d8c8d5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-string-multiple-calendar.js
@@ -0,0 +1,28 @@
+// |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.zoneddatetime.prototype.until
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01T00:00[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00[UTC][!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01T00:00[UTC][u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+];
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.until(arg),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..5204d2b29a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-string-multiple-time-zone.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.zoneddatetime.prototype.until
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "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 timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.until(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-string-time-separators.js
new file mode 100644
index 0000000000..c494617d6c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-string-time-separators.js
@@ -0,0 +1,31 @@
+// |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.zoneddatetime.prototype.until
+description: Time separator in string argument can vary
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["1970-01-01T00:00+00:00[UTC]", "uppercase T"],
+ ["1970-01-01t00:00+00:00[UTC]", "lowercase T"],
+ ["1970-01-01 00:00+00:00[UTC]", "space between date and time"],
+];
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.until(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/ZonedDateTime/prototype/until/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..ac9f4d59ed
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-string-time-zone-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.zoneddatetime.prototype.until
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["1970-01-01T00:00[UTC]", "named, with no offset"],
+ ["1970-01-01T00:00[!UTC]", "named, with ! and no offset"],
+ ["1970-01-01T00:00[+00:00]", "numeric, with no offset"],
+ ["1970-01-01T00:00[!+00:00]", "numeric, with ! and no offset"],
+ ["1970-01-01T00:00Z[UTC]", "named, with Z"],
+ ["1970-01-01T00:00Z[!UTC]", "named, with Z and !"],
+ ["1970-01-01T00:00Z[+00:00]", "numeric, with Z"],
+ ["1970-01-01T00:00Z[!+00:00]", "numeric, with Z and !"],
+ ["1970-01-01T00:00+00:00[UTC]", "named, with offset"],
+ ["1970-01-01T00:00+00:00[!UTC]", "named, with offset and !"],
+ ["1970-01-01T00:00+00:00[+00:00]", "numeric, with offset"],
+ ["1970-01-01T00:00+00:00[!+00:00]", "numeric, with offset and !"],
+];
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.until(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/ZonedDateTime/prototype/until/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..c39e2f6531
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-string-unknown-annotation.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.zoneddatetime.prototype.until
+description: Various forms of unknown annotation
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["1970-01-01T00:00[UTC][foo=bar]", "with time zone"],
+ ["1970-01-01T00:00[UTC][foo=bar][u-ca=iso8601]", "before calendar"],
+ ["1970-01-01T00:00[UTC][u-ca=iso8601][foo=bar]", "after calendar"],
+ ["1970-01-01T00:00[UTC][foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.until(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/ZonedDateTime/prototype/until/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-wrong-type.js
new file mode 100644
index 0000000000..974d14d117
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-wrong-type.js
@@ -0,0 +1,45 @@
+// |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.zoneddatetime.prototype.until
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for ZonedDateTime
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+
+const primitiveTests = [
+ [undefined, "undefined"],
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [19761118, "number that would convert to a valid ISO string in other contexts"],
+ [1n, "bigint"],
+];
+
+for (const [arg, description] of primitiveTests) {
+ assert.throws(
+ typeof arg === 'string' ? RangeError : TypeError,
+ () => instance.until(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.ZonedDateTime, "Temporal.ZonedDateTime, object"],
+ [Temporal.ZonedDateTime.prototype, "Temporal.ZonedDateTime.prototype, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.until(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/ZonedDateTime/prototype/until/balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/balance-negative-time-units.js
new file mode 100644
index 0000000000..f409f8fd2e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/balance-negative-time-units.js
@@ -0,0 +1,57 @@
+// |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.zoneddatetime.prototype.until
+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-differenceisodatetime step 2:
+ 2. Let _timeDifference_ be ? DifferenceTime(_h1_, _min1_, _s1_, _ms1_, _mus1_, _ns1_, _h2_, _min2_, _s2_, _ms2_, _mus2_, _ns2_).
+ sec-temporal-differencezoneddatetime step 7:
+ 7. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _calendar_, _largestUnit_, _options_).
+ sec-temporal.zoneddatetime.prototype.until step 15:
+ 15. Let _difference_ be ? DifferenceZonedDateTime(_zonedDateTime_.[[Nanoseconds]], _other_.[[Nanoseconds]], _zonedDateTime_.[[TimeZone]], _zonedDateTime_.[[Calendar]], _largestUnit_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const datetime = new Temporal.ZonedDateTime(830998861_001_001_001n, timeZone);
+const options = { largestUnit: "days" };
+
+const result1 = new Temporal.ZonedDateTime(830995200_000_000_002n, timeZone).until(datetime, options);
+TemporalHelpers.assertDuration(result1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 999, "nanoseconds balance");
+
+const result2 = new Temporal.ZonedDateTime(830995200_000_002_000n, timeZone).until(datetime, options);
+TemporalHelpers.assertDuration(result2, 0, 0, 0, 0, 1, 1, 1, 0, 999, 1, "microseconds balance");
+
+const result3 = new Temporal.ZonedDateTime(830995200_002_000_000n, timeZone).until(datetime, options);
+TemporalHelpers.assertDuration(result3, 0, 0, 0, 0, 1, 1, 0, 999, 1, 1, "milliseconds balance");
+
+const result4 = new Temporal.ZonedDateTime(830995202_000_000_000n, timeZone).until(datetime, options);
+TemporalHelpers.assertDuration(result4, 0, 0, 0, 0, 1, 0, 59, 1, 1, 1, "seconds balance");
+
+const result5 = new Temporal.ZonedDateTime(830995320_000_000_000n, timeZone).until(datetime, options);
+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 = new Temporal.ZonedDateTime(831002400_000_000_000n, timeZone).until(datetime, options);
+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/ZonedDateTime/prototype/until/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/branding.js
new file mode 100644
index 0000000000..12d8ab85d8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/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.zoneddatetime.prototype.until
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const until = Temporal.ZonedDateTime.prototype.until;
+
+assert.sameValue(typeof until, "function");
+
+const args = [new Temporal.ZonedDateTime(123456n, new Temporal.TimeZone("UTC"))];
+
+assert.throws(TypeError, () => until.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => until.apply(null, args), "null");
+assert.throws(TypeError, () => until.apply(true, args), "true");
+assert.throws(TypeError, () => until.apply("", args), "empty string");
+assert.throws(TypeError, () => until.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => until.apply(1, args), "1");
+assert.throws(TypeError, () => until.apply({}, args), "plain object");
+assert.throws(TypeError, () => until.apply(Temporal.ZonedDateTime, args), "Temporal.ZonedDateTime");
+assert.throws(TypeError, () => until.apply(Temporal.ZonedDateTime.prototype, args), "Temporal.ZonedDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..4967b2b786
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.zoneddatetime.prototype.until
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dateUntilOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dateUntil");
+Object.defineProperty(Temporal.Calendar.prototype, "dateUntil", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("dateUntil should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.until(new Temporal.ZonedDateTime(1_100_000_000_000_000_000n, "UTC"));
+
+Object.defineProperty(Temporal.Calendar.prototype, "dateUntil", dateUntilOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/builtin-timezone-no-observable-calls.js
new file mode 100644
index 0000000000..7686dbafc1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/builtin-timezone-no-observable-calls.js
@@ -0,0 +1,37 @@
+// |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.zoneddatetime.prototype.until
+description: >
+ Calling the method on an instance constructed with a builtin time zone causes
+ no observable lookups or calls to time zone methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+const getPossibleInstantsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getPossibleInstantsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.until(new Temporal.ZonedDateTime(1_100_000_000_000_000_000n, "UTC"));
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", getPossibleInstantsForOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/builtin.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/builtin.js
new file mode 100644
index 0000000000..7183c7345c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/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.zoneddatetime.prototype.until
+description: >
+ Tests that Temporal.ZonedDateTime.prototype.until
+ 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.ZonedDateTime.prototype.until),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.until),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.until),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.ZonedDateTime.prototype.until.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/calendar-dateadd-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/calendar-dateadd-called-with-options-undefined.js
new file mode 100644
index 0000000000..5710b9c2b4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/calendar-dateadd-called-with-options-undefined.js
@@ -0,0 +1,40 @@
+// |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.zoneddatetime.prototype.until
+description: >
+ BuiltinTimeZoneGetInstantFor calls Calendar.dateAdd with undefined as the
+ options value
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarDateAddUndefinedOptions();
+const timeZone = TemporalHelpers.oneShiftTimeZone(new Temporal.Instant(0n), 3600e9);
+const earlier = new Temporal.ZonedDateTime(0n, timeZone, calendar);
+
+// Basic difference with largestUnit larger than days.
+// The call comes from this path:
+// ZonedDateTime.until() -> DifferenceZonedDateTime -> AddZonedDateTime -> calendar.dateAdd()
+
+const later1 = new Temporal.ZonedDateTime(1_213_200_000_000_000n, timeZone, calendar);
+earlier.until(later1, { largestUnit: "weeks" });
+assert.sameValue(calendar.dateAddCallCount, 1, "basic difference with largestUnit >days");
+
+// Difference with rounding, with smallestUnit a calendar unit.
+// The calls come from these paths:
+// ZonedDateTime.until() ->
+// DifferenceZonedDateTime -> AddZonedDateTime -> calendar.dateAdd()
+// RoundDuration ->
+// MoveRelativeZonedDateTime -> AddZonedDateTime -> calendar.dateAdd()
+// MoveRelativeDate -> calendar.dateAdd()
+// BalanceDurationRelative -> MoveRelativeDate -> calendar.dateAdd()
+
+calendar.dateAddCallCount = 0;
+
+earlier.until(later1, { smallestUnit: "weeks" });
+assert.sameValue(calendar.dateAddCallCount, 4, "rounding difference with calendar smallestUnit");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/calendar-datefromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..a60c47d66f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/calendar-datefromfields-called-with-null-prototype-fields.js
@@ -0,0 +1,20 @@
+// |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.zoneddatetime.prototype.until
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+const arg = { year: 2000, month: 5, day: 2, timeZone, calendar };
+instance.until(arg);
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/calendar-dateuntil-called-with-copy-of-options.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/calendar-dateuntil-called-with-copy-of-options.js
new file mode 100644
index 0000000000..49eef7f158
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/calendar-dateuntil-called-with-copy-of-options.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.zoneddatetime.prototype.until
+description: The dateUntil() method on the calendar is called with a copy of the options bag
+features: [Temporal]
+---*/
+
+const originalOptions = {
+ largestUnit: "year",
+ shouldBeCopied: {},
+};
+let called = false;
+
+class Calendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+
+ dateUntil(d1, d2, options) {
+ called = true;
+ assert.notSameValue(options, originalOptions, "options bag should be a copy");
+ assert.sameValue(options.shouldBeCopied, originalOptions.shouldBeCopied, "options bag should be a shallow copy");
+ return new Temporal.Duration(1);
+ }
+}
+const calendar = new Calendar();
+const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar);
+// exactly one year later; avoids NanosecondsToDays path
+const later = new Temporal.ZonedDateTime(1_031_536_000_000_000_000n, "UTC", calendar);
+earlier.until(later, originalOptions);
+assert(called, "calendar.dateUntil must be called");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/calendar-dateuntil-called-with-null-prototype-options.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/calendar-dateuntil-called-with-null-prototype-options.js
new file mode 100644
index 0000000000..3cac430ecd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/calendar-dateuntil-called-with-null-prototype-options.js
@@ -0,0 +1,20 @@
+// |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.zoneddatetime.prototype.until
+description: >
+ Calendar.dateUntil method is called with a null-prototype object as the
+ options value when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckOptionsPrototypePollution();
+const instance = new Temporal.ZonedDateTime(0n, "UTC", calendar);
+const argument = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar);
+instance.until(argument, { largestUnit: "year" });
+assert.sameValue(calendar.dateUntilCallCount, 1, "dateUntil should have been called on the calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/calendar-dateuntil-called-with-singular-largestunit.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/calendar-dateuntil-called-with-singular-largestunit.js
new file mode 100644
index 0000000000..e283cf25c8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/calendar-dateuntil-called-with-singular-largestunit.js
@@ -0,0 +1,117 @@
+// |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.zoneddatetime.prototype.until
+description: The options object passed to calendar.dateUntil has a largestUnit property with its value in the singular form
+info: |
+ sec-temporal.zoneddatetime.prototype.until steps 13–17:
+ 13. If _largestUnit_ is not one of *"year"*, *"month"*, *"week"*, or *"day"*, then
+ ...
+ c. Return ...
+ 14. ...
+ 15. Let _difference_ be ? DifferenceZonedDateTime(_zonedDateTime_.[[Nanoseconds]], _other_.[[Nanoseconds]], _zonedDateTime_.[[TimeZone]], _zonedDateTime_.[[Calendar]], _largestUnit_).
+ 16. Let _roundResult_ be ? RoundDuration(_difference_.[[Years]], _difference_.[[Months]], _difference_.[[Weeks]], _difference_.[[Days]], _difference_.[[Hours]], _difference_.[[Minutes]], _difference_.[[Seconds]], _difference_.[[Milliseconds]], _difference_.[[Microseconds]], _difference_.[[Nanoseconds]], _roundingIncrement_, _smallestUnit_, _roundingMode_, _zonedDateTime_).
+ 17. Let _result_ be ? AdjustRoundedDurationDays(_roundResult_.[[Years]], _roundResult_.[[Months]], _roundResult_.[[Weeks]], _roundResult_.[[Days]], _roundResult_.[[Hours]], _roundResult_.[[Minutes]], _roundResult_.[[Seconds]], _roundResult_.[[Milliseconds]], _roundResult_.[[Microseconds]], _roundResult_.[[Nanoseconds]], _roundingIncrement_, _smallestUnit_, _roundingMode_, _zonedDateTime_).
+ sec-temporal-differencezoneddatetime steps 7 and 11:
+ 7. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _calendar_, _largestUnit_, _options_).
+ 11. Let _result_ be ? NanosecondsToDays(_timeRemainderNs_, _intermediate_).
+ sec-temporal-roundduration steps 5.d and 8.n–p:
+ 5. If _unit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*, then
+ ...
+ d. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _intermediate_).
+ ...
+ 8. If _unit_ is *"year"*, then
+ ...
+ n. Let _untilOptions_ be ! OrdinaryObjectCreate(*null*).
+ o. Perform ! CreateDataPropertyOrThrow(_untilOptions_, *"largestUnit"*, *"year"*).
+ p. Let _timePassed_ be ? CalendarDateUntil(_calendar_, _relativeTo_, _daysLater_, _untilOptions_)
+ sec-temporal-adjustroundeddurationdays steps 1 and 9:
+ 1. If _relativeTo_ does not have an [[InitializedTemporalZonedDateTime]] internal slot; or _unit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*; or _unit_ is *"nanosecond"* and _increment_ is 1, then
+ a. Return ...
+ ...
+ 9. Let _adjustedDateDuration_ be ? AddDuration(_years_, _months_, _weeks_, _days_, 0, 0, 0, 0, 0, 0, 0, 0, 0, _direction_, 0, 0, 0, 0, 0, 0, _relativeTo_).
+ sec-temporal-addduration step 7.a–g:
+ a. Assert: _relativeTo_ has an [[IntializedTemporalZonedDateTime]] internal slot.
+ ...
+ f. If _largestUnit_ is not one of *"year"*, *"month"*, *"week"*, or *"day"*, then
+ ...
+ g. Else,
+ i. Let _result_ be ? DifferenceZonedDateTime(_relativeTo_.[[Nanoseconds]], _endNs_, _timeZone_, _calendar_, _largestUnit_).
+ sec-temporal-nanosecondstodays step 11:
+ 11. 1. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _relativeTo_.[[Calendar]], *"day"*).
+ sec-temporal-differenceisodatetime steps 9–11:
+ 9. Let _dateLargestUnit_ be ! LargerOfTwoTemporalUnits(*"day"*, _largestUnit_).
+ 10. Let _untilOptions_ be ? MergeLargestUnitOption(_options_, _dateLargestUnit_).
+ 11. Let _dateDifference_ be ? CalendarDateUntil(_calendar_, _date1_, _date2_, _untilOptions_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkCalendarDateUntilLargestUnitSingular(
+ (calendar, largestUnit) => {
+ const earlier = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC", calendar);
+ const later = new Temporal.ZonedDateTime(1_086_403_661_988_655_322n, "UTC", calendar);
+ earlier.until(later, { largestUnit });
+ },
+ {
+ years: ["year"],
+ months: ["month"],
+ weeks: ["week"],
+ days: [],
+ hours: [],
+ minutes: [],
+ seconds: [],
+ milliseconds: [],
+ microseconds: [],
+ nanoseconds: []
+ }
+);
+
+// Additionally check the path that goes through AdjustRoundedDurationDays
+
+TemporalHelpers.checkCalendarDateUntilLargestUnitSingular(
+ (calendar, largestUnit) => {
+ const earlier = new Temporal.ZonedDateTime(-31536000_000_000_000n /* = -365 days */, "UTC", calendar);
+ const later = new Temporal.ZonedDateTime(86_399_999_999_999n, "UTC", calendar);
+ earlier.until(later, { largestUnit, roundingIncrement: 2, roundingMode: 'ceil' });
+ },
+ {
+ years: ["year", "year", "year"],
+ months: ["month", "month", "month"],
+ weeks: ["week", "week", "week"],
+ days: [],
+ hours: [],
+ minutes: [],
+ seconds: [],
+ milliseconds: [],
+ microseconds: [],
+ nanoseconds: []
+ }
+);
+
+// Also check the path that goes through RoundDuration when smallestUnit is
+// given
+
+TemporalHelpers.checkCalendarDateUntilLargestUnitSingular(
+ (calendar, smallestUnit) => {
+ const earlier = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC", calendar);
+ const later = new Temporal.ZonedDateTime(1_086_403_661_988_655_322n, "UTC", calendar);
+ earlier.until(later, { smallestUnit });
+ },
+ {
+ years: ["year", "year", "year"],
+ months: ["month", "month", "month"],
+ weeks: ["week", "week", "week"],
+ days: [],
+ hours: [],
+ minutes: [],
+ seconds: [],
+ milliseconds: [],
+ microseconds: [],
+ nanoseconds: []
+ }
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/calendar-fields-iterable.js
new file mode 100644
index 0000000000..11a2409c47
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/calendar-fields-iterable.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.zoneddatetime.prototype.until
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.zoneddatetime.prototype.until step 3:
+ 3. Set _other_ to ? ToTemporalZonedDateTime(_other_).
+ sec-temporal-totemporalzoneddatetime step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"hour"*, *"microsecond"*, *"millisecond"*, *"minute"*, *"month"*, *"monthCode"*, *"nanosecond"*, *"second"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar1);
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+datetime.until({ year: 2005, month: 6, day: 2, timeZone: "UTC", calendar: calendar2 });
+
+assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/calendar-temporal-object.js
new file mode 100644
index 0000000000..6fa9b6970b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/calendar-temporal-object.js
@@ -0,0 +1,29 @@
+// |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.zoneddatetime.prototype.until
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.zoneddatetime.prototype.until step 3:
+ 3. Set _other_ to ? ToTemporalZonedDateTime(_other_).
+ sec-temporal-totemporalzoneddatetime step 2.b:
+ b. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", temporalObject);
+ datetime.until({ year: 2005, month: 6, day: 2, timeZone: "UTC", calendar: temporalObject });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..22d5780862
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/constructor-in-calendar-fields.js
@@ -0,0 +1,18 @@
+// |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.zoneddatetime.prototype.until
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const timeZone = 'Europe/Paris'
+const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar, timeZone: timeZone };
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+
+assert.throws(RangeError, () => instance.until(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/date-and-time-durations-opposite-signs.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/date-and-time-durations-opposite-signs.js
new file mode 100644
index 0000000000..8facb0f633
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/date-and-time-durations-opposite-signs.js
@@ -0,0 +1,47 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2024 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.zoneddatetime.prototype.until
+description: >
+ Rounding calculation in difference method can result in duration date and
+ time components with opposite signs
+info: |
+ DifferenceTemporalZonedDateTime ( operation, zonedDateTime, other, options )
+ 17. If _roundingGranularityIsNoop_ is *false*, then
+ ...
+ e. Let _adjustResult_ be ? AdjustRoundedDurationDays(_roundResult_.[[Years]], _roundResult_.[[Months]],
+ _roundResult_.[[Weeks]], _days_, _daysResult_.[[NormalizedTime]], _settings_.[[RoundingIncrement]],
+ _settings_.[[SmallestUnit]], _settings_.[[RoundingMode]], _zonedDateTime_, _calendarRec_, _timeZoneRec_,
+ _precalculatedPlainDateTime_).
+ f. Let _balanceResult_ be ? BalanceDateDurationRelative(_adjustResult_.[[Years]], _adjustResult_.[[Months]],
+ _adjustResult_.[[Weeks]], _adjustResult_.[[Days]], _settings_.[[LargestUnit]], _settings_.[[SmallestUnit]],
+ _plainRelativeTo_, _calendarRec_).
+ g. Set _result_ to ? CombineDateAndNormalizedTimeDuration(_balanceResult_, _adjustResult_.[[NormalizedTime]]).
+features: [Temporal]
+---*/
+
+// Based on a test case by André Bargull
+
+const calendar = new class extends Temporal.Calendar {
+ #dateUntil = 0;
+
+ dateUntil(one, two, options) {
+ let result = super.dateUntil(one, two, options);
+ if (++this.#dateUntil === 2) {
+ result = result.negated();
+ }
+ return result;
+ }
+}("iso8601");
+
+const oneDay = 86400_000_000_000;
+const start = new Temporal.ZonedDateTime(0n, "UTC", calendar);
+const end = new Temporal.ZonedDateTime(BigInt(500.5 * oneDay), "UTC", calendar);
+
+assert.throws(RangeError, () => start.until(end, {
+ largestUnit: "years",
+ smallestUnit: "hours",
+}));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/duplicate-calendar-fields.js
new file mode 100644
index 0000000000..d0f6eb0bf2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/duplicate-calendar-fields.js
@@ -0,0 +1,20 @@
+// |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.zoneddatetime.prototype.until
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['day'], ['hour'], ['microsecond'], ['millisecond'], ['minute'], ['month'], ['monthCode'], ['nanosecond'], ['second'], ['year'], ['timeZone'], ['offset']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const timeZone = 'Europe/Paris'
+ const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar, timeZone: timeZone };
+ const instance = new Temporal.ZonedDateTime(0n, timeZone);
+
+ assert.throws(RangeError, () => instance.until(arg));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..9848b12866
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/infinity-throws-rangeerror.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.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.zoneddatetime.prototype.until
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+const base = { year: 2000, month: 5, day: 2, hour: 15, minute: 30, second: 45, millisecond: 987, microsecond: 654, nanosecond: 321, timeZone: "UTC" };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.until({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.until({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/largestunit-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/largestunit-invalid-string.js
new file mode 100644
index 0000000000..a55f710a75
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/largestunit-invalid-string.js
@@ -0,0 +1,31 @@
+// |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.zoneddatetime.prototype.until
+description: RangeError thrown when largestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+const later = new Temporal.ZonedDateTime(1_000_090_061_987_654_321n, "UTC");
+const badValues = [
+ "era",
+ "eraYear",
+ "millisecond\0",
+ "mill\u0131second",
+ "SECOND",
+ "eras",
+ "eraYears",
+ "milliseconds\0",
+ "mill\u0131seconds",
+ "SECONDS",
+ "other string"
+];
+for (const largestUnit of badValues) {
+ assert.throws(RangeError, () => earlier.until(later, { largestUnit }),
+ `"${largestUnit}" is not a valid value for largestUnit`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/largestunit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/largestunit-plurals-accepted.js
new file mode 100644
index 0000000000..acfa1f76b9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/largestunit-plurals-accepted.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.zoneddatetime.prototype.until
+description: Plural units are accepted as well for the largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+const later = new Temporal.ZonedDateTime(1_086_403_661_988_655_322n, "UTC");
+const validUnits = [
+ "year",
+ "month",
+ "week",
+ "day",
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+];
+TemporalHelpers.checkPluralUnitsAccepted((largestUnit) => earlier.until(later, { largestUnit }), validUnits);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/largestunit-smallestunit-mismatch.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/largestunit-smallestunit-mismatch.js
new file mode 100644
index 0000000000..08735ae401
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/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.zoneddatetime.prototype.until
+description: RangeError thrown when smallestUnit is larger than largestUnit
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+const later = new Temporal.ZonedDateTime(1_000_090_061_987_654_321n, "UTC");
+const units = ["years", "months", "weeks", "days", "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, () => earlier.until(later, { largestUnit, smallestUnit }));
+ }
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/largestunit-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/largestunit-undefined.js
new file mode 100644
index 0000000000..52b267a051
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/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.zoneddatetime.prototype.until
+description: Fallback value for largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+const later = new Temporal.ZonedDateTime(1_000_090_061_987_654_321n, "UTC");
+
+const explicit = earlier.until(later, { largestUnit: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 0, 25, 1, 1, 987, 654, 321, "default largestUnit is hour");
+const implicit = earlier.until(later, {});
+TemporalHelpers.assertDuration(implicit, 0, 0, 0, 0, 25, 1, 1, 987, 654, 321, "default largestUnit is hour");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/largestunit-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/largestunit-wrong-type.js
new file mode 100644
index 0000000000..ca09a1645c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/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.zoneddatetime.prototype.until
+description: Type conversions for largestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+const later = new Temporal.ZonedDateTime(1_000_090_061_987_654_321n, "UTC");
+TemporalHelpers.checkStringOptionWrongType("largestUnit", "year",
+ (largestUnit) => earlier.until(later, { largestUnit }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 1, 1, 1, 1, 987, 654, 321, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/leap-second.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/leap-second.js
new file mode 100644
index 0000000000..b581353e6a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/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.zoneddatetime.prototype.until
+description: Leap second is a valid ISO string for ZonedDateTime
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(1_483_228_799_000_000_000n, timeZone);
+
+let arg = "2016-12-31T23:59:60+00:00[UTC]";
+const result = instance.until(arg);
+TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "leap second is a valid ISO string for ZonedDateTime"
+);
+
+arg = "2000-05-02T12:34:56+23:59[+23:59:60]";
+assert.throws(
+ RangeError,
+ () => instance.until(arg),
+ "leap second in time zone name not valid"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/length.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/length.js
new file mode 100644
index 0000000000..f020c9be0f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/length.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.zoneddatetime.prototype.until
+description: Temporal.ZonedDateTime.prototype.until.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.ZonedDateTime.prototype.until, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/name.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/name.js
new file mode 100644
index 0000000000..df895120eb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/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.zoneddatetime.prototype.until
+description: Temporal.ZonedDateTime.prototype.until.name is "until".
+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.ZonedDateTime.prototype.until, "name", {
+ value: "until",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/negative-epochnanoseconds.js
new file mode 100644
index 0000000000..1d0145e333
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/negative-epochnanoseconds.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.zoneddatetime.prototype.until
+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 result = datetime.until(new Temporal.ZonedDateTime(0n, "UTC"), { largestUnit: "month" });
+TemporalHelpers.assertDuration(result, 0, 5, 0, 7, 7, 9, 24, 999, 999, 999);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/normalized-time-duration-to-days-loop-arbitrarily.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/normalized-time-duration-to-days-loop-arbitrarily.js
new file mode 100644
index 0000000000..f057d733d9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/normalized-time-duration-to-days-loop-arbitrarily.js
@@ -0,0 +1,78 @@
+// |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.zoneddatetime.prototype.until
+description: >
+ NormalizedTimeDurationToDays can loop arbitrarily up to max safe integer
+info: |
+ NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ , precalculatedPlainDatetime ] )
+ ...
+ 21. Repeat, while done is false,
+ a. Let oneDayFarther be ? AddDaysToZonedDateTime(relativeResult.[[Instant]],
+ relativeResult.[[DateTime]], timeZoneRec, zonedRelativeTo.[[Calendar]], sign).
+ b. Set dayLengthNs to NormalizedTimeDurationFromEpochNanosecondsDifference(oneDayFarther.[[EpochNanoseconds]],
+ relativeResult.[[EpochNanoseconds]]).
+ c. Let oneDayLess be ? SubtractNormalizedTimeDuration(norm, dayLengthNs).
+ c. If NormalizedTimeDurationSign(oneDayLess) × sign ≥ 0, then
+ i. Set norm to oneDayLess.
+ ii. Set relativeResult to oneDayFarther.
+ iii. Set days to days + sign.
+ d. Else,
+ i. Set done to true.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calls = [];
+const dayLengthNs = 86400000000000n;
+const other = new Temporal.ZonedDateTime(dayLengthNs, "UTC", "iso8601");
+
+function createRelativeTo(count) {
+ const dayInstant = new Temporal.Instant(dayLengthNs);
+ const substitutions = [];
+ const timeZone = new Temporal.TimeZone("UTC");
+ // Return constant value for first _count_ calls
+ TemporalHelpers.substituteMethod(
+ timeZone,
+ "getPossibleInstantsFor",
+ substitutions
+ );
+ substitutions.length = count;
+ let i = 0;
+ for (i = 0; i < substitutions.length; i++) {
+ // (this value)
+ substitutions[i] = [dayInstant];
+ }
+ // Record calls in calls[]
+ TemporalHelpers.observeMethod(calls, timeZone, "getPossibleInstantsFor");
+ return new Temporal.ZonedDateTime(0n, timeZone);
+}
+
+let zdt = createRelativeTo(50);
+calls.splice(0); // Reset calls list after ZonedDateTime construction
+zdt.until(other, {
+ largestUnit: "day",
+});
+assert.sameValue(
+ calls.length,
+ 50 + 1,
+ "Expected ZonedDateTime.until to call getPossibleInstantsFor correct number of times"
+);
+
+zdt = createRelativeTo(100);
+calls.splice(0); // Reset calls list after previous loop + ZonedDateTime construction
+zdt.until(other, {
+ largestUnit: "day",
+});
+assert.sameValue(
+ calls.length,
+ 100 + 1,
+ "Expected ZonedDateTime.until to call getPossibleInstantsFor correct number of times"
+);
+
+zdt = createRelativeTo(105);
+assert.throws(RangeError, () => zdt.until(other, { largestUnit: "day" }), "105 days > 2⁵³ ns");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/normalized-time-duration-to-days-range-errors.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/normalized-time-duration-to-days-range-errors.js
new file mode 100644
index 0000000000..178b1c981b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/normalized-time-duration-to-days-range-errors.js
@@ -0,0 +1,126 @@
+// |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.zoneddatetime.prototype.until
+description: >
+ Abstract operation NormalizedTimeDurationToDays can throw four different
+ RangeErrors.
+info: |
+ NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ , precalculatedPlainDateTime ] )
+ 22. If days < 0 and sign = 1, throw a RangeError exception.
+ 23. If days > 0 and sign = -1, throw a RangeError exception.
+ ...
+ 25. If NormalizedTimeDurationSign(_norm_) = 1 and sign = -1, throw a RangeError exception.
+ ...
+ 28. If dayLength ≥ 2⁵³, throw a RangeError exception.
+features: [Temporal, BigInt]
+includes: [temporalHelpers.js]
+---*/
+
+function timeZoneSubstituteValues(
+ getPossibleInstantsFor,
+ getOffsetNanosecondsFor
+) {
+ const tz = new Temporal.TimeZone("UTC");
+ TemporalHelpers.substituteMethod(
+ tz,
+ "getPossibleInstantsFor",
+ getPossibleInstantsFor
+ );
+ TemporalHelpers.substituteMethod(
+ tz,
+ "getOffsetNanosecondsFor",
+ getOffsetNanosecondsFor
+ );
+ return tz;
+}
+
+const dayNs = 86_400_000_000_000;
+const zeroZDT = new Temporal.ZonedDateTime(0n, "UTC");
+const oneZDT = new Temporal.ZonedDateTime(1n, "UTC");
+const epochInstant = new Temporal.Instant(0n);
+const options = { largestUnit: "days" };
+
+// Step 22: days < 0 and sign = 1
+let start = new Temporal.ZonedDateTime(
+ 0n, // Sets DifferenceZonedDateTime _ns1_
+ timeZoneSubstituteValues(
+ [[epochInstant]], // Returned in step 16, setting _relativeResult_
+ [
+ // Behave normally in 2 calls made prior to NormalizedTimeDurationToDays
+ TemporalHelpers.SUBSTITUTE_SKIP,
+ TemporalHelpers.SUBSTITUTE_SKIP,
+ dayNs - 1, // Returned in step 8, setting _startDateTime_
+ -dayNs + 1, // Returned in step 9, setting _endDateTime_
+ ]
+ )
+);
+assert.throws(RangeError, () =>
+ start.until(
+ oneZDT, // Sets DifferenceZonedDateTime _ns2_
+ options
+ )
+);
+
+// Step 23: days > 0 and sign = -1
+start = new Temporal.ZonedDateTime(
+ 1n, // Sets DifferenceZonedDateTime _ns1_
+ timeZoneSubstituteValues(
+ [[epochInstant]], // Returned in step 16, setting _relativeResult_
+ [
+ // Behave normally in 2 calls made prior to NormalizedTimeDurationToDays
+ TemporalHelpers.SUBSTITUTE_SKIP,
+ TemporalHelpers.SUBSTITUTE_SKIP,
+ -dayNs + 1, // Returned in step 8, setting _startDateTime_
+ dayNs - 1, // Returned in step 9, setting _endDateTime_
+ ]
+ )
+);
+assert.throws(RangeError, () =>
+ start.until(
+ zeroZDT, // Sets DifferenceZonedDateTime _ns2_
+ options
+ )
+);
+
+// Step 25: nanoseconds > 0 and sign = -1
+start = new Temporal.ZonedDateTime(
+ 1n, // Sets DifferenceZonedDateTime _ns1_
+ timeZoneSubstituteValues(
+ [[new Temporal.Instant(-1n)]], // Returned in step 16, setting _relativeResult_
+ [
+ // Behave normally in 2 calls made prior to NormalizedTimeDurationToDays
+ TemporalHelpers.SUBSTITUTE_SKIP,
+ TemporalHelpers.SUBSTITUTE_SKIP,
+ dayNs - 1, // Returned in step 8, setting _startDateTime_
+ -dayNs + 1, // Returned in step 9, setting _endDateTime_
+ ]
+ )
+);
+assert.throws(RangeError, () =>
+ start.until(
+ zeroZDT, // Sets DifferenceZonedDateTime _ns2_
+ options
+ )
+);
+
+// Step 28: day length is an unsafe integer
+start = new Temporal.ZonedDateTime(
+ 0n,
+ timeZoneSubstituteValues(
+ // Not called in step 16 because _days_ = 0
+ // Returned in step 21.a, making _oneDayFarther_ 2^53 ns later than _relativeResult_
+ [[new Temporal.Instant(2n ** 53n)]],
+ []
+ )
+);
+assert.throws(RangeError, () =>
+ start.until(
+ oneZDT,
+ options
+ ),
+ "Should throw RangeError when time zone calculates an outrageous day length"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/not-a-constructor.js
new file mode 100644
index 0000000000..e2d3e5f308
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/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.zoneddatetime.prototype.until
+description: >
+ Temporal.ZonedDateTime.prototype.until 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.ZonedDateTime.prototype.until();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.until), false,
+ "isConstructor(Temporal.ZonedDateTime.prototype.until)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/options-object.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/options-object.js
new file mode 100644
index 0000000000..e1de3222d7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/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.zoneddatetime.prototype.until
+description: Empty or a function object may be used as options
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(0n, "UTC");
+
+const result1 = instance.until(new Temporal.ZonedDateTime(3600_000_000_000n, "UTC"), {});
+TemporalHelpers.assertDuration(
+ result1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,
+ "options may be an empty plain object"
+);
+
+const result2 = instance.until(new Temporal.ZonedDateTime(3600_000_000_000n, "UTC"), () => {});
+TemporalHelpers.assertDuration(
+ result2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,
+ "options may be a function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/options-undefined.js
new file mode 100644
index 0000000000..c802ea499c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/options-undefined.js
@@ -0,0 +1,30 @@
+// |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.zoneddatetime.prototype.until
+description: Verify that undefined options are handled correctly.
+features: [BigInt, Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(957270896_987_654_321n, "UTC");
+const later = new Temporal.ZonedDateTime(959949296_987_654_322n, "UTC");
+
+const explicit = earlier.until(later, undefined);
+assert.sameValue(explicit.years, 0, "default largest unit is hours");
+assert.sameValue(explicit.months, 0, "default largest unit is hours");
+assert.sameValue(explicit.weeks, 0, "default largest unit is hours");
+assert.sameValue(explicit.days, 0, "default largest unit is hours");
+assert.sameValue(explicit.hours, 744, "default largest unit is hours");
+assert.sameValue(explicit.nanoseconds, 1, "default smallest unit is nanoseconds and no rounding");
+
+const implicit = earlier.until(later);
+assert.sameValue(implicit.years, 0, "default largest unit is hours");
+assert.sameValue(implicit.months, 0, "default largest unit is hours");
+assert.sameValue(implicit.weeks, 0, "default largest unit is hours");
+assert.sameValue(implicit.days, 0, "default largest unit is hours");
+assert.sameValue(implicit.hours, 744, "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/ZonedDateTime/prototype/until/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/options-wrong-type.js
new file mode 100644
index 0000000000..ce6b990e33
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/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.zoneddatetime.prototype.until
+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.ZonedDateTime(0n, "UTC");
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.until(new Temporal.ZonedDateTime(3600_000_000_000n, "UTC"), value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/order-of-operations.js
new file mode 100644
index 0000000000..6a233dc989
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/order-of-operations.js
@@ -0,0 +1,408 @@
+// |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.zoneddatetime.prototype.until
+description: Properties on objects passed to until() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ // ToTemporalZonedDateTime
+ "get other.calendar",
+ "has other.calendar.dateAdd",
+ "has other.calendar.dateFromFields",
+ "has other.calendar.dateUntil",
+ "has other.calendar.day",
+ "has other.calendar.dayOfWeek",
+ "has other.calendar.dayOfYear",
+ "has other.calendar.daysInMonth",
+ "has other.calendar.daysInWeek",
+ "has other.calendar.daysInYear",
+ "has other.calendar.fields",
+ "has other.calendar.id",
+ "has other.calendar.inLeapYear",
+ "has other.calendar.mergeFields",
+ "has other.calendar.month",
+ "has other.calendar.monthCode",
+ "has other.calendar.monthDayFromFields",
+ "has other.calendar.monthsInYear",
+ "has other.calendar.weekOfYear",
+ "has other.calendar.year",
+ "has other.calendar.yearMonthFromFields",
+ "has other.calendar.yearOfWeek",
+ "get other.calendar.dateFromFields",
+ "get other.calendar.fields",
+ "call other.calendar.fields",
+ "get other.day",
+ "get other.day.valueOf",
+ "call other.day.valueOf",
+ "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.month",
+ "get other.month.valueOf",
+ "call other.month.valueOf",
+ "get other.monthCode",
+ "get other.monthCode.toString",
+ "call other.monthCode.toString",
+ "get other.nanosecond",
+ "get other.nanosecond.valueOf",
+ "call other.nanosecond.valueOf",
+ "get other.offset",
+ "get other.offset.toString",
+ "call other.offset.toString",
+ "get other.second",
+ "get other.second.valueOf",
+ "call other.second.valueOf",
+ "get other.timeZone",
+ "get other.year",
+ "get other.year.valueOf",
+ "call other.year.valueOf",
+ "has other.timeZone.getOffsetNanosecondsFor",
+ "has other.timeZone.getPossibleInstantsFor",
+ "has other.timeZone.id",
+ "call other.calendar.dateFromFields",
+ "get other.timeZone.getOffsetNanosecondsFor",
+ "get other.timeZone.getPossibleInstantsFor",
+ "call other.timeZone.getPossibleInstantsFor",
+ "call other.timeZone.getOffsetNanosecondsFor",
+ // CalendarEquals
+ "get this.calendar.id",
+ "get other.calendar.id",
+ // 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 ownTimeZone = TemporalHelpers.timeZoneObserver(actual, "this.timeZone");
+const ownCalendar = TemporalHelpers.calendarObserver(actual, "this.calendar");
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, ownTimeZone, ownCalendar);
+
+const dstTimeZone = TemporalHelpers.springForwardFallBackTimeZone();
+const ownDstTimeZone = TemporalHelpers.timeZoneObserver(actual, "this.timeZone", {
+ getOffsetNanosecondsFor: dstTimeZone.getOffsetNanosecondsFor,
+ getPossibleInstantsFor: dstTimeZone.getPossibleInstantsFor,
+});
+const otherDstTimeZone = TemporalHelpers.timeZoneObserver(actual, "other.timeZone", {
+ getOffsetNanosecondsFor: dstTimeZone.getOffsetNanosecondsFor,
+ getPossibleInstantsFor: dstTimeZone.getPossibleInstantsFor,
+});
+/* 2000-10-29T01:30-07:00, in the middle of the first repeated hour: */
+const fallBackInstance = new Temporal.ZonedDateTime(972808200_000_000_000n, ownDstTimeZone, ownCalendar);
+
+const otherDateTimePropertyBag = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2004,
+ month: 5,
+ monthCode: "M05",
+ day: 12,
+ hour: 1,
+ minute: 46,
+ second: 40,
+ millisecond: 250,
+ microsecond: 500,
+ nanosecond: 750,
+ offset: "+00:00",
+ calendar: TemporalHelpers.calendarObserver(actual, "other.calendar"),
+ timeZone: TemporalHelpers.timeZoneObserver(actual, "other.timeZone"),
+}, "other");
+
+function createOptionsObserver({ smallestUnit = "nanoseconds", largestUnit = "auto", roundingMode = "halfExpand", roundingIncrement = 1 } = {}) {
+ return TemporalHelpers.propertyBagObserver(actual, {
+ // order is significant, due to iterating through properties in order to
+ // copy them to an internal null-prototype object:
+ roundingIncrement,
+ roundingMode,
+ largestUnit,
+ smallestUnit,
+ additional: "property",
+ }, "options");
+}
+
+// clear any observable things that happened while constructing the objects
+actual.splice(0);
+
+// basic order of observable operations, without rounding:
+instance.until(otherDateTimePropertyBag, createOptionsObserver());
+assert.compareArray(actual, expected, "order of operations");
+actual.splice(0); // clear
+
+// short-circuit for identical objects will still test TimeZoneEquals if
+// largestUnit is a calendar unit:
+const identicalPropertyBag = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2001,
+ month: 9,
+ monthCode: "M09",
+ day: 9,
+ hour: 1,
+ minute: 46,
+ second: 40,
+ millisecond: 0,
+ microsecond: 0,
+ nanosecond: 0,
+ offset: "+00:00",
+ calendar: TemporalHelpers.calendarObserver(actual, "other.calendar"),
+ timeZone: TemporalHelpers.timeZoneObserver(actual, "other.timeZone"),
+}, "other");
+
+instance.until(identicalPropertyBag, createOptionsObserver({ largestUnit: "years" }));
+assert.compareArray(actual, expected.concat([
+ "get this.timeZone.id",
+ "get other.timeZone.id",
+]), "order of operations with identical dates and largestUnit a calendar unit");
+actual.splice(0); // clear
+
+// two ZonedDateTimes that denote the same wall-clock time in the time zone can
+// avoid calling some calendar methods:
+const fallBackPropertyBag = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2000,
+ month: 10,
+ monthCode: "M10",
+ day: 29,
+ hour: 1,
+ minute: 30,
+ second: 0,
+ millisecond: 0,
+ microsecond: 0,
+ nanosecond: 0,
+ offset: "-08:00",
+ calendar: TemporalHelpers.calendarObserver(actual, "other.calendar"),
+ timeZone: otherDstTimeZone,
+}, "other");
+fallBackInstance.until(fallBackPropertyBag, createOptionsObserver({ largestUnit: "days" }));
+assert.compareArray(actual, [
+ // ToTemporalZonedDateTime
+ "get other.calendar",
+ "has other.calendar.dateAdd",
+ "has other.calendar.dateFromFields",
+ "has other.calendar.dateUntil",
+ "has other.calendar.day",
+ "has other.calendar.dayOfWeek",
+ "has other.calendar.dayOfYear",
+ "has other.calendar.daysInMonth",
+ "has other.calendar.daysInWeek",
+ "has other.calendar.daysInYear",
+ "has other.calendar.fields",
+ "has other.calendar.id",
+ "has other.calendar.inLeapYear",
+ "has other.calendar.mergeFields",
+ "has other.calendar.month",
+ "has other.calendar.monthCode",
+ "has other.calendar.monthDayFromFields",
+ "has other.calendar.monthsInYear",
+ "has other.calendar.weekOfYear",
+ "has other.calendar.year",
+ "has other.calendar.yearMonthFromFields",
+ "has other.calendar.yearOfWeek",
+ "get other.calendar.dateFromFields",
+ "get other.calendar.fields",
+ "call other.calendar.fields",
+ "get other.day",
+ "get other.day.valueOf",
+ "call other.day.valueOf",
+ "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.month",
+ "get other.month.valueOf",
+ "call other.month.valueOf",
+ "get other.monthCode",
+ "get other.monthCode.toString",
+ "call other.monthCode.toString",
+ "get other.nanosecond",
+ "get other.nanosecond.valueOf",
+ "call other.nanosecond.valueOf",
+ "get other.offset",
+ "get other.offset.toString",
+ "call other.offset.toString",
+ "get other.second",
+ "get other.second.valueOf",
+ "call other.second.valueOf",
+ "get other.timeZone",
+ "get other.year",
+ "get other.year.valueOf",
+ "call other.year.valueOf",
+ "has other.timeZone.getOffsetNanosecondsFor",
+ "has other.timeZone.getPossibleInstantsFor",
+ "has other.timeZone.id",
+ "call other.calendar.dateFromFields",
+ "get other.timeZone.getOffsetNanosecondsFor",
+ "get other.timeZone.getPossibleInstantsFor",
+ "call other.timeZone.getPossibleInstantsFor",
+ "call other.timeZone.getOffsetNanosecondsFor",
+ // NOTE: extra because of wall-clock time ambiguity:
+ "call other.timeZone.getOffsetNanosecondsFor",
+ // CalendarEquals
+ "get this.calendar.id",
+ "get other.calendar.id",
+ // 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",
+ // TimeZoneEquals
+ "get this.timeZone.id",
+ "get other.timeZone.id",
+ // lookup
+ "get this.timeZone.getOffsetNanosecondsFor",
+ "get this.timeZone.getPossibleInstantsFor",
+ "get this.calendar.dateAdd",
+ "get this.calendar.dateUntil",
+ // DifferenceZonedDateTime
+ "call this.timeZone.getOffsetNanosecondsFor",
+ "call this.timeZone.getOffsetNanosecondsFor",
+ // NanosecondsToDays
+ "call this.timeZone.getOffsetNanosecondsFor",
+ "call this.timeZone.getOffsetNanosecondsFor",
+ // NanosecondsToDays → AddDaysToZonedDateTime
+ "call this.timeZone.getPossibleInstantsFor",
+], "order of operations with identical wall-clock times and largestUnit a calendar unit");
+actual.splice(0); // clear
+
+// Making largestUnit a calendar unit adds the following observable operations:
+const expectedOpsForCalendarDifference = [
+ // TimeZoneEquals
+ "get this.timeZone.id",
+ "get other.timeZone.id",
+ // lookup
+ "get this.timeZone.getOffsetNanosecondsFor",
+ "get this.timeZone.getPossibleInstantsFor",
+ "get this.calendar.dateAdd",
+ "get this.calendar.dateUntil",
+ // precalculate PlainDateTime
+ "call this.timeZone.getOffsetNanosecondsFor",
+ // DifferenceZonedDateTime
+ "call this.timeZone.getOffsetNanosecondsFor",
+ // DifferenceISODateTime
+ "call this.calendar.dateUntil",
+ // AddZonedDateTime
+ "call this.calendar.dateAdd",
+ "call this.timeZone.getPossibleInstantsFor",
+ // NanosecondsToDays
+ "call this.timeZone.getOffsetNanosecondsFor",
+ "call this.timeZone.getOffsetNanosecondsFor",
+ // NanosecondsToDays → AddDaysToZonedDateTime
+ "call this.timeZone.getPossibleInstantsFor",
+ "call this.timeZone.getPossibleInstantsFor",
+];
+
+const expectedOpsForCalendarRounding = [
+ // RoundDuration → MoveRelativeZonedDateTime → AddZonedDateTime
+ "call this.calendar.dateAdd",
+ "call this.timeZone.getPossibleInstantsFor",
+ // RoundDuration → NanosecondsToDays
+ "call this.timeZone.getOffsetNanosecondsFor",
+ "call this.timeZone.getOffsetNanosecondsFor",
+ // RoundDuration → NanosecondsToDays → AddDaysToZonedDateTime
+ "call this.timeZone.getPossibleInstantsFor",
+];
+
+// code path that skips RoundDuration:
+instance.until(otherDateTimePropertyBag, createOptionsObserver({ largestUnit: "years", smallestUnit: "nanoseconds", roundingIncrement: 1 }));
+assert.compareArray(actual, expected.concat(expectedOpsForCalendarDifference), "order of operations with largestUnit years and no rounding");
+actual.splice(0); // clear
+
+// code path through RoundDuration that rounds to the nearest year:
+const expectedOpsForYearRounding = expected.concat(expectedOpsForCalendarDifference, expectedOpsForCalendarRounding, [
+ // RoundDuration
+ "call this.calendar.dateAdd", // 12.d
+ "call this.calendar.dateAdd", // 12.f
+ "call this.calendar.dateUntil", // 12.n
+ "call this.calendar.dateAdd", // 12.x MoveRelativeDate
+ // (12.r not called because other units can't add up to >1 year at this point)
+ // BalanceDateDurationRelative
+ "call this.calendar.dateAdd", // 9.c
+ "call this.calendar.dateUntil" // 9.d
+]);
+instance.until(otherDateTimePropertyBag, createOptionsObserver({ smallestUnit: "years" }));
+assert.compareArray(actual, expectedOpsForYearRounding, "order of operations with smallestUnit = years");
+actual.splice(0); // clear
+
+// code path through RoundDuration that rounds to the nearest month:
+const expectedOpsForMonthRounding = expected.concat(expectedOpsForCalendarDifference, expectedOpsForCalendarRounding, [
+ // RoundDuration
+ "call this.calendar.dateAdd", // 13.c
+ "call this.calendar.dateAdd", // 13.e
+ "call this.calendar.dateUntil", // 13.m
+ "call this.calendar.dateAdd", // 13.w MoveRelativeDate
+ // BalanceDateDurationRelative
+ "call this.calendar.dateAdd", // 10.d
+ "call this.calendar.dateUntil", // 10.e
+]);
+instance.until(otherDateTimePropertyBag, createOptionsObserver({ smallestUnit: "months" }));
+assert.compareArray(actual, expectedOpsForMonthRounding, "order of operations with smallestUnit = months");
+actual.splice(0); // clear
+
+// code path through RoundDuration that rounds to the nearest week:
+const expectedOpsForWeekRounding = expected.concat(expectedOpsForCalendarDifference, expectedOpsForCalendarRounding, [
+ // RoundDuration
+ "call this.calendar.dateUntil", // 14.f
+ "call this.calendar.dateAdd", // 14.p MoveRelativeDate
+ // BalanceDateDurationRelative
+ "call this.calendar.dateAdd", // 16
+ "call this.calendar.dateUntil", // 17
+]);
+instance.until(otherDateTimePropertyBag, createOptionsObserver({ smallestUnit: "weeks" }));
+assert.compareArray(actual, expectedOpsForWeekRounding, "order of operations with smallestUnit = weeks");
+actual.splice(0); // clear
+
+instance.until(otherDateTimePropertyBag, createOptionsObserver({ largestUnit: "hours" }));
+assert.compareArray(actual, expected, "order of operations with largestUnit being a time unit");
+actual.splice(0); // clear
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/prop-desc.js
new file mode 100644
index 0000000000..b2752f131d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/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.zoneddatetime.prototype.until
+description: The "until" property of Temporal.ZonedDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.ZonedDateTime.prototype.until,
+ "function",
+ "`typeof ZonedDateTime.prototype.until` is `function`"
+);
+
+verifyProperty(Temporal.ZonedDateTime.prototype, "until", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/proto-in-calendar-fields.js
new file mode 100644
index 0000000000..969bbcb955
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/proto-in-calendar-fields.js
@@ -0,0 +1,18 @@
+// |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.zoneddatetime.prototype.until
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const timeZone = 'Europe/Paris'
+const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar, timeZone: timeZone };
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+
+assert.throws(RangeError, () => instance.until(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/read-time-fields-before-datefromfields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/read-time-fields-before-datefromfields.js
new file mode 100644
index 0000000000..834a70f798
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/read-time-fields-before-datefromfields.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.zoneddatetime.prototype.until
+description: The time fields are read from the object before being passed to dateFromFields().
+info: |
+ sec-temporal.zoneddatetime.prototype.until step 3:
+ 3. Set _other_ to ? ToTemporalZonedDateTime(_other_).
+ sec-temporal-totemporalzoneddatetime step 2.e:
+ e. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+ sec-temporal-interprettemporaldatetimefields steps 1–2:
+ 1. Let _timeResult_ be ? ToTemporalTimeRecord(_fields_).
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarMakeInfinityTime();
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC", calendar);
+const duration = datetime.until({ year: 2001, month: 9, day: 9, timeZone: "UTC", calendar });
+
+TemporalHelpers.assertDuration(duration, 0, 0, 0, 0, -1, -46, -40, -987, -654, -321);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/round-cross-unit-boundary.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/round-cross-unit-boundary.js
new file mode 100644
index 0000000000..02b3c0cc07
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/round-cross-unit-boundary.js
@@ -0,0 +1,36 @@
+// |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.zoneddatetime.prototype.until
+description: Rounding can cross unit boundaries up to largestUnit
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+// Date units
+{
+ const earlier = new Temporal.ZonedDateTime(1640995200_000_000_000n /* = 2022-01-01T00 */, "UTC");
+ const later = new Temporal.ZonedDateTime(1703462400_000_000_000n /* = 2023-12-25T00 */, "UTC");
+ const duration = earlier.until(later, { largestUnit: "years", smallestUnit: "months", roundingMode: "expand" });
+ TemporalHelpers.assertDuration(duration, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, "1 year 11 months balances to 2 years");
+}
+
+// Time units
+{
+ const earlier = new Temporal.ZonedDateTime(0n, "UTC");
+ const later = new Temporal.ZonedDateTime(7199_000_000_000n, "UTC");
+ const duration = earlier.until(later, { largestUnit: "hours", smallestUnit: "minutes", roundingMode: "expand" });
+ TemporalHelpers.assertDuration(duration, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, "1:59 balances to 2 hours");
+}
+
+// Both
+{
+ const earlier = new Temporal.ZonedDateTime(0n, "UTC");
+ const later = new Temporal.ZonedDateTime(63071999_999_999_999n /* = 1971-12-31T23:59:59.999999999 */, "UTC");
+ const duration = earlier.until(later, { largestUnit: "years", smallestUnit: "microseconds", roundingMode: "expand" });
+ TemporalHelpers.assertDuration(duration, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, "rounding up 1 ns balances to 2 years");
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/rounding-zero-year-month-week-length.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/rounding-zero-year-month-week-length.js
new file mode 100644
index 0000000000..c8990eb9b3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/rounding-zero-year-month-week-length.js
@@ -0,0 +1,34 @@
+// |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.zoneddatetime.prototype.until
+description: >
+ A malicious calendar resulting in a year, month, or week length of zero is
+ handled correctly
+info: |
+ RoundDuration
+ 10.z. If _oneYearDays_ = 0, throw a *RangeError* exception.
+ ...
+ 11.z. If _oneMonthDays_ = 0, throw a *RangeError* exception.
+ ...
+ 12.s. If _oneWeekDays_ = 0, throw a *RangeError* exception.
+features: [Temporal]
+---*/
+
+const cal = new class extends Temporal.Calendar {
+ dateAdd(date, duration, options) {
+ // Called several times, last call sets oneYear/Month/WeekDays to 0
+ return new Temporal.PlainDate(1970, 1, 1);
+ }
+}("iso8601");
+
+const dt1 = new Temporal.ZonedDateTime(0n, "UTC", cal);
+const dt2 = new Temporal.ZonedDateTime(365n * 86400_000_000_000n + 1n, "UTC", cal);
+
+assert.throws(RangeError, () => dt1.until(dt2, { smallestUnit: "years" }), "zero year length handled correctly");
+assert.throws(RangeError, () => dt1.until(dt2, { smallestUnit: "months" }), "zero month length handled correctly");
+assert.throws(RangeError, () => dt1.until(dt2, { smallestUnit: "weeks" }), "zero week length handled correctly");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingincrement-nan.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingincrement-nan.js
new file mode 100644
index 0000000000..d5e6a9ffda
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/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.zoneddatetime.prototype.until
+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.zoneddatetime.prototype.until step 12:
+ 12. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*).
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+const later = new Temporal.ZonedDateTime(1_000_090_061_988_655_322n, "UTC");
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: NaN }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingincrement-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingincrement-non-integer.js
new file mode 100644
index 0000000000..c986890d69
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingincrement-non-integer.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.zoneddatetime.prototype.until
+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.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+const later = new Temporal.ZonedDateTime(1_000_000_000_000_000_005n, "UTC");
+const result = earlier.until(later, { roundingIncrement: 2.5, roundingMode: "trunc" });
+TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, "roundingIncrement 2.5 truncates to 2");
+const result2 = earlier.until(later, { smallestUnit: "days", roundingIncrement: 1e9 + 0.5, roundingMode: "expand" });
+TemporalHelpers.assertDuration(result2, 0, 0, 0, 1e9, 0, 0, 0, 0, 0, 0, "roundingIncrement 1e9 + 0.5 truncates to 1e9");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingincrement-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingincrement-out-of-range.js
new file mode 100644
index 0000000000..01b093aa0e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/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.zoneddatetime.prototype.until
+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.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+const later = new Temporal.ZonedDateTime(1_000_000_000_000_000_005n, "UTC");
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: -Infinity }));
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: -1 }));
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: 0 }));
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: 0.9 }));
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: 1e9 + 1 }));
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: Infinity }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingincrement-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingincrement-undefined.js
new file mode 100644
index 0000000000..fe1ee88506
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/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.zoneddatetime.prototype.until
+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.zoneddatetime.prototype.until step 12:
+ 12. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+const later = new Temporal.ZonedDateTime(1_000_090_061_988_655_322n, "UTC");
+
+const explicit = earlier.until(later, { roundingIncrement: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 0, 25, 1, 1, 1, 1, 1, "default roundingIncrement is 1");
+
+const implicit = earlier.until(later, {});
+TemporalHelpers.assertDuration(implicit, 0, 0, 0, 0, 25, 1, 1, 1, 1, 1, "default roundingIncrement is 1");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingincrement-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingincrement-wrong-type.js
new file mode 100644
index 0000000000..a481e25f28
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/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.zoneddatetime.prototype.until
+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.zoneddatetime.prototype.until step 12:
+ 12. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*).
+includes: [temporalHelpers.js, compareArray.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+const later = new Temporal.ZonedDateTime(1_000_090_061_988_655_322n, "UTC");
+
+TemporalHelpers.checkRoundingIncrementOptionWrongType(
+ (roundingIncrement) => earlier.until(later, { roundingIncrement }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 25, 1, 1, 1, 1, 1, descr),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 25, 1, 1, 1, 1, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-ceil.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-ceil.js
new file mode 100644
index 0000000000..5a3e5d1ac9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-ceil.js
@@ -0,0 +1,45 @@
+// |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.zoneddatetime.prototype.until
+description: Tests calculations with roundingMode "ceil".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1546935756_123_456_789n /* 2019-01-08T08:22:36.123456789+00:00 */, "UTC");
+const later = new Temporal.ZonedDateTime(1631018380_987_654_289n /* 2021-09-07T12:39:40.987654289+00:00 */, "UTC");
+
+const expected = [
+ ["years", [3], [-2]],
+ ["months", [0, 32], [0, -31]],
+ ["weeks", [0, 0, 140], [0, 0, -139]],
+ ["days", [0, 0, 0, 974], [0, 0, 0, -973]],
+ ["hours", [0, 0, 0, 0, 23357], [0, 0, 0, 0, -23356]],
+ ["minutes", [0, 0, 0, 0, 23356, 18], [0, 0, 0, 0, -23356, -17]],
+ ["seconds", [0, 0, 0, 0, 23356, 17, 5], [0, 0, 0, 0, -23356, -17, -4]],
+ ["milliseconds", [0, 0, 0, 0, 23356, 17, 4, 865], [0, 0, 0, 0, -23356, -17, -4, -864]],
+ ["microseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 198], [0, 0, 0, 0, -23356, -17, -4, -864, -197]],
+ ["nanoseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 197, 500], [0, 0, 0, 0, -23356, -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(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { 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/ZonedDateTime/prototype/until/roundingmode-expand.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-expand.js
new file mode 100644
index 0000000000..28c3a35162
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-expand.js
@@ -0,0 +1,45 @@
+// |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.zoneddatetime.prototype.until
+description: Tests calculations with roundingMode "expand".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1546935756_123_456_789n /* 2019-01-08T08:22:36.123456789+00:00 */, "UTC");
+const later = new Temporal.ZonedDateTime(1631018380_987_654_289n /* 2021-09-07T12:39:40.987654289+00:00 */, "UTC");
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [0, 32], [0, -32]],
+ ["weeks", [0, 0, 140], [0, 0, -140]],
+ ["days", [0, 0, 0, 974], [0, 0, 0, -974]],
+ ["hours", [0, 0, 0, 0, 23357], [0, 0, 0, 0, -23357]],
+ ["minutes", [0, 0, 0, 0, 23356, 18], [0, 0, 0, 0, -23356, -18]],
+ ["seconds", [0, 0, 0, 0, 23356, 17, 5], [0, 0, 0, 0, -23356, -17, -5]],
+ ["milliseconds", [0, 0, 0, 0, 23356, 17, 4, 865], [0, 0, 0, 0, -23356, -17, -4, -865]],
+ ["microseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 198], [0, 0, 0, 0, -23356, -17, -4, -864, -198]],
+ ["nanoseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 197, 500], [0, 0, 0, 0, -23356, -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(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { 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/ZonedDateTime/prototype/until/roundingmode-floor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-floor.js
new file mode 100644
index 0000000000..1abcf3b687
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-floor.js
@@ -0,0 +1,45 @@
+// |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.zoneddatetime.prototype.until
+description: Tests calculations with roundingMode "floor".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1546935756_123_456_789n /* 2019-01-08T08:22:36.123456789+00:00 */, "UTC");
+const later = new Temporal.ZonedDateTime(1631018380_987_654_289n /* 2021-09-07T12:39:40.987654289+00:00 */, "UTC");
+
+const expected = [
+ ["years", [2], [-3]],
+ ["months", [0, 31], [0, -32]],
+ ["weeks", [0, 0, 139], [0, 0, -140]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -974]],
+ ["hours", [0, 0, 0, 0, 23356], [0, 0, 0, 0, -23357]],
+ ["minutes", [0, 0, 0, 0, 23356, 17], [0, 0, 0, 0, -23356, -18]],
+ ["seconds", [0, 0, 0, 0, 23356, 17, 4], [0, 0, 0, 0, -23356, -17, -5]],
+ ["milliseconds", [0, 0, 0, 0, 23356, 17, 4, 864], [0, 0, 0, 0, -23356, -17, -4, -865]],
+ ["microseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 197], [0, 0, 0, 0, -23356, -17, -4, -864, -198]],
+ ["nanoseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 197, 500], [0, 0, 0, 0, -23356, -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(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { 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/ZonedDateTime/prototype/until/roundingmode-halfCeil.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-halfCeil.js
new file mode 100644
index 0000000000..e8a89b9dad
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-halfCeil.js
@@ -0,0 +1,45 @@
+// |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.zoneddatetime.prototype.until
+description: Tests calculations with roundingMode "halfCeil".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1546935756_123_456_789n /* 2019-01-08T08:22:36.123456789+00:00 */, "UTC");
+const later = new Temporal.ZonedDateTime(1631018380_987_654_289n /* 2021-09-07T12:39:40.987654289+00:00 */, "UTC");
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [0, 32], [0, -32]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+ ["hours", [0, 0, 0, 0, 23356], [0, 0, 0, 0, -23356]],
+ ["minutes", [0, 0, 0, 0, 23356, 17], [0, 0, 0, 0, -23356, -17]],
+ ["seconds", [0, 0, 0, 0, 23356, 17, 5], [0, 0, 0, 0, -23356, -17, -5]],
+ ["milliseconds", [0, 0, 0, 0, 23356, 17, 4, 864], [0, 0, 0, 0, -23356, -17, -4, -864]],
+ ["microseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 198], [0, 0, 0, 0, -23356, -17, -4, -864, -197]],
+ ["nanoseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 197, 500], [0, 0, 0, 0, -23356, -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(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { 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/ZonedDateTime/prototype/until/roundingmode-halfEven.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-halfEven.js
new file mode 100644
index 0000000000..f109364f85
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-halfEven.js
@@ -0,0 +1,45 @@
+// |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.zoneddatetime.prototype.until
+description: Tests calculations with roundingMode "halfEven".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1546935756_123_456_789n /* 2019-01-08T08:22:36.123456789+00:00 */, "UTC");
+const later = new Temporal.ZonedDateTime(1631018380_987_654_289n /* 2021-09-07T12:39:40.987654289+00:00 */, "UTC");
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [0, 32], [0, -32]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+ ["hours", [0, 0, 0, 0, 23356], [0, 0, 0, 0, -23356]],
+ ["minutes", [0, 0, 0, 0, 23356, 17], [0, 0, 0, 0, -23356, -17]],
+ ["seconds", [0, 0, 0, 0, 23356, 17, 5], [0, 0, 0, 0, -23356, -17, -5]],
+ ["milliseconds", [0, 0, 0, 0, 23356, 17, 4, 864], [0, 0, 0, 0, -23356, -17, -4, -864]],
+ ["microseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 198], [0, 0, 0, 0, -23356, -17, -4, -864, -198]],
+ ["nanoseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 197, 500], [0, 0, 0, 0, -23356, -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(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { 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/ZonedDateTime/prototype/until/roundingmode-halfExpand.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-halfExpand.js
new file mode 100644
index 0000000000..5036a3287c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-halfExpand.js
@@ -0,0 +1,45 @@
+// |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.zoneddatetime.prototype.until
+description: Tests calculations with roundingMode "halfExpand".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1546935756_123_456_789n /* 2019-01-08T08:22:36.123456789+00:00 */, "UTC");
+const later = new Temporal.ZonedDateTime(1631018380_987_654_289n /* 2021-09-07T12:39:40.987654289+00:00 */, "UTC");
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [0, 32], [0, -32]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+ ["hours", [0, 0, 0, 0, 23356], [0, 0, 0, 0, -23356]],
+ ["minutes", [0, 0, 0, 0, 23356, 17], [0, 0, 0, 0, -23356, -17]],
+ ["seconds", [0, 0, 0, 0, 23356, 17, 5], [0, 0, 0, 0, -23356, -17, -5]],
+ ["milliseconds", [0, 0, 0, 0, 23356, 17, 4, 864], [0, 0, 0, 0, -23356, -17, -4, -864]],
+ ["microseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 198], [0, 0, 0, 0, -23356, -17, -4, -864, -198]],
+ ["nanoseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 197, 500], [0, 0, 0, 0, -23356, -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(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { 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/ZonedDateTime/prototype/until/roundingmode-halfFloor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-halfFloor.js
new file mode 100644
index 0000000000..4bca263cd9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-halfFloor.js
@@ -0,0 +1,45 @@
+// |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.zoneddatetime.prototype.until
+description: Tests calculations with roundingMode "halfFloor".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1546935756_123_456_789n /* 2019-01-08T08:22:36.123456789+00:00 */, "UTC");
+const later = new Temporal.ZonedDateTime(1631018380_987_654_289n /* 2021-09-07T12:39:40.987654289+00:00 */, "UTC");
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [0, 32], [0, -32]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+ ["hours", [0, 0, 0, 0, 23356], [0, 0, 0, 0, -23356]],
+ ["minutes", [0, 0, 0, 0, 23356, 17], [0, 0, 0, 0, -23356, -17]],
+ ["seconds", [0, 0, 0, 0, 23356, 17, 5], [0, 0, 0, 0, -23356, -17, -5]],
+ ["milliseconds", [0, 0, 0, 0, 23356, 17, 4, 864], [0, 0, 0, 0, -23356, -17, -4, -864]],
+ ["microseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 197], [0, 0, 0, 0, -23356, -17, -4, -864, -198]],
+ ["nanoseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 197, 500], [0, 0, 0, 0, -23356, -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(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { 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/ZonedDateTime/prototype/until/roundingmode-halfTrunc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-halfTrunc.js
new file mode 100644
index 0000000000..6d489a21c9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-halfTrunc.js
@@ -0,0 +1,45 @@
+// |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.zoneddatetime.prototype.until
+description: Tests calculations with roundingMode "halfTrunc".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1546935756_123_456_789n /* 2019-01-08T08:22:36.123456789+00:00 */, "UTC");
+const later = new Temporal.ZonedDateTime(1631018380_987_654_289n /* 2021-09-07T12:39:40.987654289+00:00 */, "UTC");
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [0, 32], [0, -32]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+ ["hours", [0, 0, 0, 0, 23356], [0, 0, 0, 0, -23356]],
+ ["minutes", [0, 0, 0, 0, 23356, 17], [0, 0, 0, 0, -23356, -17]],
+ ["seconds", [0, 0, 0, 0, 23356, 17, 5], [0, 0, 0, 0, -23356, -17, -5]],
+ ["milliseconds", [0, 0, 0, 0, 23356, 17, 4, 864], [0, 0, 0, 0, -23356, -17, -4, -864]],
+ ["microseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 197], [0, 0, 0, 0, -23356, -17, -4, -864, -197]],
+ ["nanoseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 197, 500], [0, 0, 0, 0, -23356, -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(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { 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/ZonedDateTime/prototype/until/roundingmode-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-invalid-string.js
new file mode 100644
index 0000000000..82c969ab77
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/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.zoneddatetime.prototype.until
+description: RangeError thrown when roundingMode option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+const later = new Temporal.ZonedDateTime(1_000_090_061_123_987_500n, "UTC");
+for (const roundingMode of ["other string", "cile", "CEIL", "ce\u0131l", "auto", "halfexpand", "floor\0"]) {
+ assert.throws(RangeError, () => earlier.until(later, { smallestUnit: "microsecond", roundingMode }));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-trunc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-trunc.js
new file mode 100644
index 0000000000..10dd33e8db
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-trunc.js
@@ -0,0 +1,45 @@
+// |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.zoneddatetime.prototype.until
+description: Tests calculations with roundingMode "trunc".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1546935756_123_456_789n /* 2019-01-08T08:22:36.123456789+00:00 */, "UTC");
+const later = new Temporal.ZonedDateTime(1631018380_987_654_289n /* 2021-09-07T12:39:40.987654289+00:00 */, "UTC");
+
+const expected = [
+ ["years", [2], [-2]],
+ ["months", [0, 31], [0, -31]],
+ ["weeks", [0, 0, 139], [0, 0, -139]],
+ ["days", [0, 0, 0, 973], [0, 0, 0, -973]],
+ ["hours", [0, 0, 0, 0, 23356], [0, 0, 0, 0, -23356]],
+ ["minutes", [0, 0, 0, 0, 23356, 17], [0, 0, 0, 0, -23356, -17]],
+ ["seconds", [0, 0, 0, 0, 23356, 17, 4], [0, 0, 0, 0, -23356, -17, -4]],
+ ["milliseconds", [0, 0, 0, 0, 23356, 17, 4, 864], [0, 0, 0, 0, -23356, -17, -4, -864]],
+ ["microseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 197], [0, 0, 0, 0, -23356, -17, -4, -864, -197]],
+ ["nanoseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 197, 500], [0, 0, 0, 0, -23356, -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(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { 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/ZonedDateTime/prototype/until/roundingmode-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-undefined.js
new file mode 100644
index 0000000000..8c797f1cd4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-undefined.js
@@ -0,0 +1,30 @@
+// |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.zoneddatetime.prototype.until
+description: Fallback value for roundingMode option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+const later = new Temporal.ZonedDateTime(1_000_090_061_123_987_500n, "UTC");
+
+const explicit1 = earlier.until(later, { smallestUnit: "microsecond", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit1, 0, 0, 0, 0, 25, 1, 1, 123, 987, 0, "default roundingMode is trunc");
+const implicit1 = earlier.until(later, { smallestUnit: "microsecond" });
+TemporalHelpers.assertDuration(implicit1, 0, 0, 0, 0, 25, 1, 1, 123, 987, 0, "default roundingMode is trunc");
+
+const explicit2 = earlier.until(later, { smallestUnit: "millisecond", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit2, 0, 0, 0, 0, 25, 1, 1, 123, 0, 0, "default roundingMode is trunc");
+const implicit2 = earlier.until(later, { smallestUnit: "millisecond" });
+TemporalHelpers.assertDuration(implicit2, 0, 0, 0, 0, 25, 1, 1, 123, 0, 0, "default roundingMode is trunc");
+
+const explicit3 = earlier.until(later, { smallestUnit: "second", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit3, 0, 0, 0, 0, 25, 1, 1, 0, 0, 0, "default roundingMode is trunc");
+const implicit3 = earlier.until(later, { smallestUnit: "second" });
+TemporalHelpers.assertDuration(implicit3, 0, 0, 0, 0, 25, 1, 1, 0, 0, 0, "default roundingMode is trunc");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-wrong-type.js
new file mode 100644
index 0000000000..cc073f9165
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/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.zoneddatetime.prototype.until
+description: Type conversions for roundingMode option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+const later = new Temporal.ZonedDateTime(1_000_090_061_123_987_500n, "UTC");
+TemporalHelpers.checkStringOptionWrongType("roundingMode", "trunc",
+ (roundingMode) => earlier.until(later, { smallestUnit: "microsecond", roundingMode }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 25, 1, 1, 123, 987, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/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/ZonedDateTime/prototype/until/smallestunit-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/smallestunit-invalid-string.js
new file mode 100644
index 0000000000..c875186a93
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/smallestunit-invalid-string.js
@@ -0,0 +1,31 @@
+// |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.zoneddatetime.prototype.until
+description: RangeError thrown when smallestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+const later = new Temporal.ZonedDateTime(1_000_090_061_987_654_321n, "UTC");
+const badValues = [
+ "era",
+ "eraYear",
+ "millisecond\0",
+ "mill\u0131second",
+ "SECOND",
+ "eras",
+ "eraYears",
+ "milliseconds\0",
+ "mill\u0131seconds",
+ "SECONDS",
+ "other string",
+];
+for (const smallestUnit of badValues) {
+ assert.throws(RangeError, () => earlier.until(later, { smallestUnit }),
+ `"${smallestUnit}" is not a valid value for smallest unit`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/smallestunit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/smallestunit-plurals-accepted.js
new file mode 100644
index 0000000000..9ce243e4e7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/smallestunit-plurals-accepted.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.zoneddatetime.prototype.until
+description: Plural units are accepted as well for the smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+const later = new Temporal.ZonedDateTime(1_086_403_661_988_655_322n, "UTC");
+const validUnits = [
+ "year",
+ "month",
+ "week",
+ "day",
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+];
+TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => earlier.until(later, { smallestUnit }), validUnits);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/smallestunit-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/smallestunit-undefined.js
new file mode 100644
index 0000000000..86614c4af8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/smallestunit-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.zoneddatetime.prototype.until
+description: Fallback value for smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+const later = new Temporal.ZonedDateTime(1_000_090_061_987_654_321n, "UTC");
+
+const explicit = earlier.until(later, { smallestUnit: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 0, 25, 1, 1, 987, 654, 321, "default smallestUnit is nanosecond");
+const implicit = earlier.until(later, {});
+TemporalHelpers.assertDuration(implicit, 0, 0, 0, 0, 25, 1, 1, 987, 654, 321, "default smallestUnit is nanosecond");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/smallestunit-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/smallestunit-wrong-type.js
new file mode 100644
index 0000000000..20536d6f0c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/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.zoneddatetime.prototype.until
+description: Type conversions for smallestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+const later = new Temporal.ZonedDateTime(1_000_090_061_987_654_321n, "UTC");
+TemporalHelpers.checkStringOptionWrongType("smallestUnit", "microsecond",
+ (smallestUnit) => earlier.until(later, { smallestUnit }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 25, 1, 1, 987, 654, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..74c097a2df
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/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.zoneddatetime.prototype.until
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const other = new Temporal.ZonedDateTime(1_100_000_000_123_456_789n, timeZone);
+ assert.throws(RangeError, () => datetime.until(other, { largestUnit: "days" }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..8ce2f12932
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/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.zoneddatetime.prototype.until
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const other = new Temporal.ZonedDateTime(1_100_000_000_123_456_789n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => datetime.until(other, { largestUnit: "days" }),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..a1887854a4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/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.zoneddatetime.prototype.until
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const other = new Temporal.ZonedDateTime(1_100_000_000_123_456_789n, timeZone);
+ assert.throws(RangeError, () => datetime.until(other, { largestUnit: "days" }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..72e8b1ed59
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/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.zoneddatetime.prototype.until
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const other = new Temporal.ZonedDateTime(1_100_000_000_123_456_789n, timeZone);
+ assert.throws(TypeError, () => datetime.until(other, { largestUnit: "days" }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/timezone-getpossibleinstantsfor-iterable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/timezone-getpossibleinstantsfor-iterable.js
new file mode 100644
index 0000000000..ed8b71c90e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/timezone-getpossibleinstantsfor-iterable.js
@@ -0,0 +1,41 @@
+// |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.zoneddatetime.prototype.until
+description: An iterable returned from timeZone.getPossibleInstantsFor is consumed after each call
+info: |
+ sec-temporal.zoneddatetime.prototype.until step 3:
+ 3. Set _other_ to ? ToTemporalZonedDateTime(_other_).
+ sec-temporal-totemporalzoneddatetime step 7:
+ 7. Let _epochNanoseconds_ be ? InterpretISODateTimeOffset(_result_.[[Year]], [...], _result_.[[Nanosecond]], _offsetNanoseconds_, _timeZone_, _disambiguation_, _offset_).
+ sec-temporal-interpretisodatetimeoffset step 7:
+ 7. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_).
+ sec-temporal-getpossibleinstantsfor step 2:
+ 2. Let _list_ be ? IterableToList(_possibleInstants_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+// Not called on the instance's time zone
+
+const expected1 = [];
+
+TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => {
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, timeZone);
+ datetime.until({ year: 2005, month: 6, day: 2, timeZone: "UTC" });
+}, expected1);
+
+// Called on the argument's time zone
+
+const expected2 = [
+ "2005-06-02T00:00:00",
+];
+
+TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => {
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+ datetime.until({ year: 2005, month: 6, day: 2, timeZone });
+}, expected2);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/year-zero.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/year-zero.js
new file mode 100644
index 0000000000..a62bcbbfa0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/year-zero.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.zoneddatetime.prototype.until
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-0000000-01-01T00:02Z[UTC]",
+ "-0000000-01-01T00:02+00:00[UTC]",
+ "-0000000-01-01T00:02:00.000000000+00:00[UTC]",
+];
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.until(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/zoneddatetime-string-multiple-offsets.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/zoneddatetime-string-multiple-offsets.js
new file mode 100644
index 0000000000..135a441fac
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/zoneddatetime-string-multiple-offsets.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.zoneddatetime.prototype.until
+description: Sub-minute offset trailing zeroes allowed in ISO string but not in bracketed offset
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("+01:35");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+let str = "1970-01-01T01:35:30+01:35:00.000000000[+01:35]";
+
+const result = instance.until(str);
+TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 30, 0, 0, 0, "ISO offset, sub-minute offset trailing-zeroes");
+
+str = "1970-01-01T01:35:30+01:35:00.000000000[+01:35:00.000000000]";
+assert.throws(
+ RangeError,
+ () => instance.until(str),
+ "Trailing zeroes not allowed for sub-minute time zone identifiers"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/zoneddatetime-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/zoneddatetime-string.js
new file mode 100644
index 0000000000..59a371d40d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/zoneddatetime-string.js
@@ -0,0 +1,37 @@
+// |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.zoneddatetime.prototype.until
+description: Conversion of ISO date-time strings to Temporal.ZonedDateTime instances
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+
+let str = "1970-01-01T00:00";
+assert.throws(RangeError, () => instance.until(str), "bare date-time string is not a ZonedDateTime");
+str = "1970-01-01T00:00Z";
+assert.throws(RangeError, () => instance.until(str), "date-time + Z is not a ZonedDateTime");
+str = "1970-01-01T00:00+01:00";
+assert.throws(RangeError, () => instance.until(str), "date-time + offset is not a ZonedDateTime");
+
+str = "1970-01-01T00:00[+01:00]";
+const result1 = instance.until(str);
+TemporalHelpers.assertDuration(result1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, "date-time + IANA annotation preserves wall time in the time zone");
+
+str = "1970-01-01T00:00Z[+01:00]";
+const result2 = instance.until(str);
+TemporalHelpers.assertDuration(result2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "date-time + Z + IANA annotation preserves exact time in the time zone");
+
+str = "1970-01-01T00:00+01:00[+01:00]";
+const result3 = instance.until(str);
+TemporalHelpers.assertDuration(result3, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, "date-time + offset + IANA annotation ensures both exact and wall time match");
+
+str = "1970-01-01T00:00-04:15[+01:00]";
+assert.throws(RangeError, () => instance.until(str), "date-time + offset + IANA annotation throws if wall time and exact time mismatch");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/valueOf/basic.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/valueOf/basic.js
new file mode 100644
index 0000000000..2be86ffaa6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/valueOf/basic.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.zoneddatetime.prototype.valueof
+description: Basic tests for valueOf().
+features: [Temporal]
+---*/
+
+const zonedDateTime = new Temporal.ZonedDateTime(100n, "UTC");
+const zonedDateTime2 = new Temporal.ZonedDateTime(987654321n, "UTC");
+
+assert.throws(TypeError, () => zonedDateTime.valueOf(), "valueOf");
+assert.throws(TypeError, () => zonedDateTime < zonedDateTime, "<");
+assert.throws(TypeError, () => zonedDateTime <= zonedDateTime, "<=");
+assert.throws(TypeError, () => zonedDateTime > zonedDateTime, ">");
+assert.throws(TypeError, () => zonedDateTime >= zonedDateTime, ">=");
+assert.sameValue(zonedDateTime === zonedDateTime, true, "===");
+assert.sameValue(zonedDateTime === zonedDateTime2, false, "===");
+assert.sameValue(zonedDateTime !== zonedDateTime, false, "!==");
+assert.sameValue(zonedDateTime !== zonedDateTime2, true, "!==");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/valueOf/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/valueOf/branding.js
new file mode 100644
index 0000000000..432906a228
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/valueOf/branding.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.zoneddatetime.prototype.valueof
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const valueOf = Temporal.ZonedDateTime.prototype.valueOf;
+
+assert.sameValue(typeof valueOf, "function");
+
+assert.throws(TypeError, () => valueOf.call(undefined), "undefined");
+assert.throws(TypeError, () => valueOf.call(null), "null");
+assert.throws(TypeError, () => valueOf.call(true), "true");
+assert.throws(TypeError, () => valueOf.call(""), "empty string");
+assert.throws(TypeError, () => valueOf.call(Symbol()), "symbol");
+assert.throws(TypeError, () => valueOf.call(1), "1");
+assert.throws(TypeError, () => valueOf.call({}), "plain object");
+assert.throws(TypeError, () => valueOf.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime");
+assert.throws(TypeError, () => valueOf.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/valueOf/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/valueOf/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/valueOf/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/valueOf/builtin.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/valueOf/builtin.js
new file mode 100644
index 0000000000..41a92e9ba0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/valueOf/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.zoneddatetime.prototype.valueof
+description: >
+ Tests that Temporal.ZonedDateTime.prototype.valueOf
+ 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.ZonedDateTime.prototype.valueOf),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.valueOf),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.valueOf),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.ZonedDateTime.prototype.valueOf.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/valueOf/length.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/valueOf/length.js
new file mode 100644
index 0000000000..8a9e4da58e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/valueOf/length.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.zoneddatetime.prototype.valueof
+description: Temporal.ZonedDateTime.prototype.valueOf.length is 0
+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.ZonedDateTime.prototype.valueOf, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/valueOf/name.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/valueOf/name.js
new file mode 100644
index 0000000000..a3d0a305e0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/valueOf/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.zoneddatetime.prototype.valueof
+description: Temporal.ZonedDateTime.prototype.valueOf.name is "valueOf".
+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.ZonedDateTime.prototype.valueOf, "name", {
+ value: "valueOf",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/valueOf/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/valueOf/not-a-constructor.js
new file mode 100644
index 0000000000..8c48ecb23a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/valueOf/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.zoneddatetime.prototype.valueof
+description: >
+ Temporal.ZonedDateTime.prototype.valueOf 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.ZonedDateTime.prototype.valueOf();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.valueOf), false,
+ "isConstructor(Temporal.ZonedDateTime.prototype.valueOf)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/valueOf/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/valueOf/prop-desc.js
new file mode 100644
index 0000000000..01f09f4692
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/valueOf/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.zoneddatetime.prototype.valueof
+description: The "valueOf" property of Temporal.ZonedDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.ZonedDateTime.prototype.valueOf,
+ "function",
+ "`typeof ZonedDateTime.prototype.valueOf` is `function`"
+);
+
+verifyProperty(Temporal.ZonedDateTime.prototype, "valueOf", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/valueOf/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/valueOf/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/valueOf/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/ZonedDateTime/prototype/weekOfYear/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/branding.js
new file mode 100644
index 0000000000..3b78894aef
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/branding.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-get-temporal.zoneddatetime.prototype.weekofyear
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const weekOfYear = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "weekOfYear").get;
+
+assert.sameValue(typeof weekOfYear, "function");
+
+assert.throws(TypeError, () => weekOfYear.call(undefined), "undefined");
+assert.throws(TypeError, () => weekOfYear.call(null), "null");
+assert.throws(TypeError, () => weekOfYear.call(true), "true");
+assert.throws(TypeError, () => weekOfYear.call(""), "empty string");
+assert.throws(TypeError, () => weekOfYear.call(Symbol()), "symbol");
+assert.throws(TypeError, () => weekOfYear.call(1), "1");
+assert.throws(TypeError, () => weekOfYear.call({}), "plain object");
+assert.throws(TypeError, () => weekOfYear.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime");
+assert.throws(TypeError, () => weekOfYear.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..f8207ff213
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.zoneddatetime.prototype.weekofyear
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const weekOfYearOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "weekOfYear");
+Object.defineProperty(Temporal.Calendar.prototype, "weekOfYear", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("weekOfYear should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.weekOfYear;
+
+Object.defineProperty(Temporal.Calendar.prototype, "weekOfYear", weekOfYearOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/builtin-timezone-no-observable-calls.js
new file mode 100644
index 0000000000..f4c6165031
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/builtin-timezone-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.zoneddatetime.prototype.weekofyear
+description: >
+ Calling the method on an instance constructed with a builtin time zone causes
+ no observable lookups or calls to time zone methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.weekOfYear;
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/custom.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/custom.js
new file mode 100644
index 0000000000..20192188ed
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/custom.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-get-temporal.zoneddatetime.prototype.weekofyear
+description: Custom calendar tests for weekOfYear().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ weekOfYear(...args) {
+ ++calls;
+ assert.compareArray(args.map(String), [instance].map((arg) => arg.toPlainDateTime().toString()), "weekOfYear arguments");
+ return 7;
+ }
+}
+
+const calendar = new CustomCalendar();
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar);
+const result = instance.weekOfYear;
+assert.sameValue(result, 7, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/prop-desc.js
new file mode 100644
index 0000000000..524cf17210
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/prop-desc.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-get-temporal.zoneddatetime.prototype.weekofyear
+description: The "weekOfYear" property of Temporal.ZonedDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "weekOfYear");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..dc62383bce
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,18 @@
+// |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-get-temporal.zoneddatetime.prototype.weekofyear
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.weekOfYear);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..8c366c3818
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/timezone-getoffsetnanosecondsfor-not-callable.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-get-temporal.zoneddatetime.prototype.weekofyear
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => datetime.weekOfYear,
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..4f131781ec
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,18 @@
+// |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-get-temporal.zoneddatetime.prototype.weekofyear
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.weekOfYear);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..7b22299451
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/timezone-getoffsetnanosecondsfor-wrong-type.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-get-temporal.zoneddatetime.prototype.weekofyear
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.weekOfYear);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/validate-calendar-value.js
new file mode 100644
index 0000000000..8b18332ce4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/validate-calendar-value.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-get-temporal.zoneddatetime.prototype.weekofyear
+description: Validate result returned from calendar weekOfYear() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [null, TypeError],
+ [false, TypeError],
+ [Infinity, RangeError],
+ [-Infinity, RangeError],
+ [NaN, RangeError],
+ [-7, RangeError],
+ [-0.1, RangeError],
+ ["string", TypeError],
+ [Symbol("foo"), TypeError],
+ [7n, TypeError],
+ [{}, TypeError],
+ [true, TypeError],
+ [7.1, RangeError],
+ ["7", TypeError],
+ ["7.5", TypeError],
+ [{valueOf() { return 7; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ weekOfYear() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar);
+ assert.throws(error, () => instance.weekOfYear, `${typeof result} ${String(result)} not converted to positive integer`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/balance-negative-time-units.js
new file mode 100644
index 0000000000..e7935824fd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/balance-negative-time-units.js
@@ -0,0 +1,65 @@
+// |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.zoneddatetime.prototype.with
+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-addtime step 8:
+ 8. Return ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_).
+ sec-temporal-adddatetime step 1:
+ 1. Let _timeResult_ be ? AddTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_, _hours_, _minutes_, _seconds_, _milliseconds_, _microseconds_, _nanoseconds_).
+ sec-temporal-builtintimezonegetinstantfor step 13.a:
+ a. Let _earlier_ be ? AddDateTime(_dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], _dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], 0, 0, 0, 0, 0, 0, 0, 0, 0, −_nanoseconds_, *"constrain"*).
+ sec-temporal-interpretisodatetimeoffset steps 4–10:
+ 4. If _offsetNanoseconds_ is *null*, or _offset_ is *"ignore"*, then
+ a. Let _instant_ be ? BuiltinTimeZoneGetInstantFor(_timeZone_, _dateTime_, _disambiguation_).
+ ...
+ ...
+ 6. Assert: _offset_ is *"prefer"* or *"reject"*.
+ 7. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_).
+ ...
+ 9. If _offset_ is *"reject"*, throw a *RangeError* exception.
+ 10. Let _instant_ be ? DisambiguatePossibleInstants(_possibleInstants_, _timeZone_, _dateTime_, _disambiguation_).
+ sec-temporal.zoneddatetime.prototype.with step 26:
+ 26. Let _epochNanoseconds_ be ? InterpretISODateTimeOffset(_dateTimeResult_.[[Year]], _dateTimeResult_.[[Month]], _dateTimeResult_.[[Day]], _dateTimeResult_.[[Hour]], _dateTimeResult_.[[Minute]], _dateTimeResult_.[[Second]], _dateTimeResult_.[[Millisecond]], _dateTimeResult_.[[Microsecond]], _dateTimeResult_.[[Nanosecond]], _offsetNanoseconds_, _timeZone_, _disambiguation_, _offset_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const shiftInstant = new Temporal.Instant(3661_001_001_001n);
+const tz1 = TemporalHelpers.oneShiftTimeZone(shiftInstant, 2);
+const datetime1 = new Temporal.ZonedDateTime(3661_001_001_000n, tz1);
+
+// This code path is encountered if offset is `ignore` or `prefer`,
+// disambiguation is `earlier` and the shift is a spring-forward change
+datetime1.with({ nanosecond: 1 }, { offset: "ignore", disambiguation: "earlier" });
+
+const expected = [
+ "1970-01-01T01:01:01.001001001",
+ "1970-01-01T01:01:01.001000999",
+];
+assert.compareArray(tz1.getPossibleInstantsForCalledWith, expected);
+
+const tz2 = TemporalHelpers.oneShiftTimeZone(shiftInstant, 2);
+const datetime2 = new Temporal.ZonedDateTime(3661_001_001_000n, tz2);
+
+datetime2.with({ nanosecond: 1 }, { offset: "prefer", disambiguation: "earlier" });
+
+assert.compareArray(tz2.getPossibleInstantsForCalledWith, expected);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/branding.js
new file mode 100644
index 0000000000..089d357535
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/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.zoneddatetime.prototype.with
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const with_ = Temporal.ZonedDateTime.prototype.with;
+
+assert.sameValue(typeof with_, "function");
+
+const args = [{ year: 2022 }];
+
+assert.throws(TypeError, () => with_.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => with_.apply(null, args), "null");
+assert.throws(TypeError, () => with_.apply(true, args), "true");
+assert.throws(TypeError, () => with_.apply("", args), "empty string");
+assert.throws(TypeError, () => with_.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => with_.apply(1, args), "1");
+assert.throws(TypeError, () => with_.apply({}, args), "plain object");
+assert.throws(TypeError, () => with_.apply(Temporal.ZonedDateTime, args), "Temporal.ZonedDateTime");
+assert.throws(TypeError, () => with_.apply(Temporal.ZonedDateTime.prototype, args), "Temporal.ZonedDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..29b963ecf8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/builtin-calendar-no-array-iteration.js
@@ -0,0 +1,23 @@
+// |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.zoneddatetime.prototype.with
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.with({ day: 5 });
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..0d3f1edc2f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,46 @@
+// |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.zoneddatetime.prototype.with
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const fieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "fields");
+Object.defineProperty(Temporal.Calendar.prototype, "fields", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("fields should not be looked up");
+ },
+});
+const mergeFieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "mergeFields");
+Object.defineProperty(Temporal.Calendar.prototype, "mergeFields", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("mergeFields should not be looked up");
+ },
+});
+const dateFromFieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dateFromFields");
+Object.defineProperty(Temporal.Calendar.prototype, "dateFromFields", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("dateFromFields should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.with({ year: 2001 });
+
+Object.defineProperty(Temporal.Calendar.prototype, "fields", fieldsOriginal);
+Object.defineProperty(Temporal.Calendar.prototype, "mergeFields", mergeFieldsOriginal);
+Object.defineProperty(Temporal.Calendar.prototype, "dateFromFields", dateFromFieldsOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/builtin-timezone-no-observable-calls.js
new file mode 100644
index 0000000000..d40d49f7fb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/builtin-timezone-no-observable-calls.js
@@ -0,0 +1,37 @@
+// |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.zoneddatetime.prototype.with
+description: >
+ Calling the method on an instance constructed with a builtin time zone causes
+ no observable lookups or calls to time zone methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+const getPossibleInstantsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getPossibleInstantsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.with({ year: 2001 });
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", getPossibleInstantsForOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/builtin.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/builtin.js
new file mode 100644
index 0000000000..c18d00211d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/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.zoneddatetime.prototype.with
+description: >
+ Tests that Temporal.ZonedDateTime.prototype.with
+ 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.ZonedDateTime.prototype.with),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.with),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.with),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.ZonedDateTime.prototype.with.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/calendar-fields-iterable.js
new file mode 100644
index 0000000000..7993c23c19
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/calendar-fields-iterable.js
@@ -0,0 +1,32 @@
+// |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.zoneddatetime.prototype.with
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.zoneddatetime.prototype.with step 9:
+ 9. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar);
+datetime.with({ year: 2005 });
+
+assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/calendar-fromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/calendar-fromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..35d0c27f9f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/calendar-fromfields-called-with-null-prototype-fields.js
@@ -0,0 +1,18 @@
+// |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.zoneddatetime.prototype.with
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.ZonedDateTime(0n, "UTC", calendar);
+instance.with({ day: 24 });
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should have been called on the calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/calendar-merge-fields-returns-primitive.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/calendar-merge-fields-returns-primitive.js
new file mode 100644
index 0000000000..7f1e187cbf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/calendar-merge-fields-returns-primitive.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.zoneddatetime.prototype.with
+description: >
+ with() should throw a TypeError if mergeFields() returns a primitive,
+ without passing the value on to any other calendar methods
+includes: [compareArray.js, temporalHelpers.js]
+features: [BigInt, Symbol, Temporal]
+---*/
+
+[undefined, null, true, 3.14159, "bad value", Symbol("no"), 7n].forEach((primitive) => {
+ const calendar = TemporalHelpers.calendarMergeFieldsReturnsPrimitive(primitive);
+ const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar);
+ assert.throws(TypeError, () => instance.with({ year: 2005 }), "bad return from mergeFields() throws");
+ assert.sameValue(calendar.dateFromFieldsCallCount, 0, "dateFromFields() never called");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/calendar-mergefields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/calendar-mergefields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..6a75d5ce22
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/calendar-mergefields-called-with-null-prototype-fields.js
@@ -0,0 +1,18 @@
+// |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.zoneddatetime.prototype.with
+description: >
+ Calendar.mergeFields method is called with null-prototype fields objects
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckMergeFieldsPrototypePollution();
+const instance = new Temporal.ZonedDateTime(0n, "UTC", calendar);
+instance.with({ day: 24 });
+assert.sameValue(calendar.mergeFieldsCallCount, 1, "mergeFields should have been called on the calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/calendar-options.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/calendar-options.js
new file mode 100644
index 0000000000..2daa8e28ec
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/calendar-options.js
@@ -0,0 +1,35 @@
+// |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.zoneddatetime.prototype.with
+description: >
+ The options argument is copied and the copy is passed to
+ Calendar#dateFromFields.
+features: [Temporal]
+---*/
+
+const options = {
+ extra: "property",
+};
+let calledDateFromFields = 0;
+class Calendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ dateFromFields(fields, optionsArg) {
+ ++calledDateFromFields;
+ assert.notSameValue(optionsArg, options, "should pass copied options object");
+ assert.sameValue(optionsArg.extra, "property", "should copy all properties from options object");
+ assert.sameValue(Object.getPrototypeOf(optionsArg), null, "Copy has null prototype");
+ return super.dateFromFields(fields, optionsArg);
+ }
+};
+const calendar = new Calendar();
+const datetime = new Temporal.ZonedDateTime(0n, "UTC", calendar);
+const result = datetime.with({ year: 1971 }, options);
+assert.sameValue(result.epochNanoseconds, 365n * 86400_000_000_000n, "year changed from 1970 to 1971")
+assert.sameValue(calledDateFromFields, 1, "should have called overridden dateFromFields once");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..640cd8d689
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/constructor-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |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.zoneddatetime.prototype.with
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const zoneddatetime = new Temporal.ZonedDateTime(1682892000000000000n, 'Europe/Madrid', calendar);
+
+assert.throws(RangeError, () => zoneddatetime.with({hour: 12}));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/copies-merge-fields-object.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/copies-merge-fields-object.js
new file mode 100644
index 0000000000..dc159e00f5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/copies-merge-fields-object.js
@@ -0,0 +1,61 @@
+// |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.plaindatetime.prototype.with
+description: The object returned from mergeFields() is copied before being passed to dateFromFields().
+info: |
+ sec-temporal.plaindatetime.prototype.with steps 18–19 and 23:
+ 18. Set _fields_ to ? CalendarMergeFields(_calendar_, _fields_, _partialDate_).
+ 19. Set _fields_ to ? PrepareTemporalFields(_fields_, _fieldNames_, « *"timeZone"* »).
+ 23. Let _dateTimeResult_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+ sec-temporal-interprettemporaldatetimefields step 2:
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "get day",
+ "get day.valueOf",
+ "call day.valueOf",
+ "get hour",
+ "get hour.valueOf",
+ "call hour.valueOf",
+ "get microsecond",
+ "get microsecond.valueOf",
+ "call microsecond.valueOf",
+ "get millisecond",
+ "get millisecond.valueOf",
+ "call millisecond.valueOf",
+ "get minute",
+ "get minute.valueOf",
+ "call minute.valueOf",
+ "get month",
+ "get month.valueOf",
+ "call month.valueOf",
+ "get monthCode",
+ "get monthCode.toString",
+ "call monthCode.toString",
+ "get nanosecond",
+ "get nanosecond.valueOf",
+ "call nanosecond.valueOf",
+ "get offset",
+ "get offset.toString",
+ "call offset.toString",
+ "get second",
+ "get second.valueOf",
+ "call second.valueOf",
+ "get year",
+ "get year.valueOf",
+ "call year.valueOf",
+];
+
+const calendar = TemporalHelpers.calendarMergeFieldsGetters();
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar);
+datetime.with({ year: 2022 });
+
+assert.compareArray(calendar.mergeFieldsReturnOperations, expected, "getters called on mergeFields return");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/copy-properties-not-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/copy-properties-not-undefined.js
new file mode 100644
index 0000000000..6afaf10645
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/copy-properties-not-undefined.js
@@ -0,0 +1,52 @@
+// |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.zoneddatetime.prototype.with
+description: PreparePartialTemporalFields copies only defined properties of source object
+info: |
+ 4. For each value _property_ of _fieldNames_, do
+ a. Let _value_ be ? Get(_fields_, _property_).
+ b. If _value_ is not *undefined*, then
+ ...
+ iii. Perform ! CreateDataPropertyOrThrow(_result_, _property_, _value_).
+features: [Temporal]
+---*/
+
+const d1 = new Temporal.ZonedDateTime(1_000_000_000_000_000_789n, "UTC");
+
+const d2 = d1.with({ day: 1, hour: 10, year: undefined });
+
+assert.sameValue(d2.year, 2001,
+ "only the properties that are present and defined in the plain object are copied (year value)"
+);
+
+assert.sameValue(d2.month, 9,
+ "only the properties that are present and defined in the plain object are copied (month value)"
+);
+
+assert.sameValue(d2.day, 1,
+ "only the properties that are present and defined in the plain object are copied (day value)"
+);
+
+assert.sameValue(d2.hour, 10,
+ "only the properties that are present and defined in the plain object are copied (hour value)"
+);
+assert.sameValue(d2.minute, 46,
+ "only the properties that are present and defined in the plain object are copied (minute value)"
+);
+assert.sameValue(d2.second, 40,
+ "only the properties that are present and defined in the plain object are copied (second value)"
+);
+assert.sameValue(d2.millisecond, 0,
+ "only the properties that are present and defined in the plain object are copied (millisecond value)"
+);
+assert.sameValue(d2.microsecond, 0,
+ "only the properties that are present and defined in the plain object are copied (microsecond value)"
+);
+assert.sameValue(d2.nanosecond, 789,
+ "only the properties that are present and defined in the plain object are copied (nanosecond value)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/disambiguation-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/disambiguation-invalid-string.js
new file mode 100644
index 0000000000..8cf415e441
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/disambiguation-invalid-string.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.zoneddatetime.protoype.with
+description: RangeError thrown when disambiguation option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaldisambiguation step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"disambiguation"*, « String », « *"compatible"*, *"earlier"*, *"later"*, *"reject"* », *"compatible"*).
+ sec-temporal.zoneddatetime.protoype.with step 14:
+ 14. Let _disambiguation_ be ? ToTemporalDisambiguation(_options_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+assert.throws(RangeError, () => datetime.with({ hour: 2 }, { disambiguation: "other string" }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/disambiguation-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/disambiguation-undefined.js
new file mode 100644
index 0000000000..e81165eeb7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/disambiguation-undefined.js
@@ -0,0 +1,34 @@
+// |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.zoneddatetime.prototype.with
+description: Fallback value for disambiguation option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaldisambiguation step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"disambiguation"*, « String », « *"compatible"*, *"earlier"*, *"later"*, *"reject"* », *"compatible"*).
+ sec-temporal.zoneddatetime.protoype.with step 14:
+ 14. Let _disambiguation_ be ? ToTemporalDisambiguation(_options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const timeZone = TemporalHelpers.springForwardFallBackTimeZone();
+const springForwardDatetime = new Temporal.ZonedDateTime(954702001_000_000_000n, timeZone);
+const fallBackDatetime = new Temporal.ZonedDateTime(972849601_000_000_000n, timeZone);
+const offset = "ignore";
+
+[
+ [springForwardDatetime, { hour: 2, minute: 30 }, 954671401_000_000_000n],
+ [fallBackDatetime, { hour: 1, minute: 30 }, 972808201_000_000_000n],
+].forEach(([datetime, fields, expected]) => {
+ const explicit = datetime.with(fields, { offset, disambiguation: undefined });
+ assert.sameValue(explicit.epochNanoseconds, expected, "default disambiguation is compatible");
+ const implicit = datetime.with(fields, { offset });
+ assert.sameValue(implicit.epochNanoseconds, expected, "default disambiguation is compatible");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/disambiguation-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/disambiguation-wrong-type.js
new file mode 100644
index 0000000000..90358a2752
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/disambiguation-wrong-type.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.zoneddatetime.prototype.with
+description: Type conversions for disambiguation option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaldisambiguation step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"disambiguation"*, « String », « *"compatible"*, *"earlier"*, *"later"*, *"reject"* », *"compatible"*).
+ sec-temporal.zoneddatetime.protoype.with step 14:
+ 14. Let _disambiguation_ be ? ToTemporalDisambiguation(_options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+TemporalHelpers.checkStringOptionWrongType("disambiguation", "compatible",
+ (disambiguation) => datetime.with({ hour: 2 }, { disambiguation }),
+ (result, descr) => assert.sameValue(result.epochNanoseconds, 1_000_003_600_987_654_321n, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/duplicate-calendar-fields.js
new file mode 100644
index 0000000000..bcf2046f1c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/duplicate-calendar-fields.js
@@ -0,0 +1,18 @@
+// |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.zoneddatetime.prototype.with
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['monthCode'], ['day'], ['hour'], ['microsecond'], ['millisecond'], ['minute'], ['month'], ['nanosecond'], ['second'], ['year'], ['offset']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const zoneddatetime = new Temporal.ZonedDateTime(1682892000000000000n, 'Europe/Madrid', calendar);
+
+ assert.throws(RangeError, () => zoneddatetime.with({hour: 12}));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/getpossibleinstantsfor-called-with-iso8601-calendar.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/getpossibleinstantsfor-called-with-iso8601-calendar.js
new file mode 100644
index 0000000000..67a199f72f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/getpossibleinstantsfor-called-with-iso8601-calendar.js
@@ -0,0 +1,61 @@
+// |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.zoneddatetime.prototype.with
+description: >
+ Time zone's getPossibleInstantsFor is called with a PlainDateTime with the
+ built-in ISO 8601 calendar
+features: [Temporal]
+info: |
+ DisambiguatePossibleInstants:
+ 2. Let _n_ be _possibleInstants_'s length.
+ ...
+ 5. Assert: _n_ = 0.
+ ...
+ 19. If _disambiguation_ is *"earlier"*, then
+ ...
+ c. Let _earlierDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*).
+ d. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _earlierDateTime_).
+ ...
+ 20. Assert: _disambiguation_ is *"compatible"* or *"later"*.
+ ...
+ 23. Let _laterDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*).
+ 24. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _laterDateTime_).
+---*/
+
+class SkippedDateTime extends Temporal.TimeZone {
+ constructor() {
+ super("UTC");
+ this.calls = 0;
+ }
+
+ getPossibleInstantsFor(dateTime) {
+ // Calls occur in pairs. For the first one return no possible instants so
+ // that DisambiguatePossibleInstants will call it again
+ if (this.calls++ % 2 == 0) {
+ return [];
+ }
+
+ assert.sameValue(
+ dateTime.getISOFields().calendar,
+ "iso8601",
+ "getPossibleInstantsFor called with dateTime with built-in ISO 8601 calendar"
+ );
+ return super.getPossibleInstantsFor(dateTime);
+ }
+}
+
+const nonBuiltinISOCalendar = new Temporal.Calendar("iso8601");
+
+for (const disambiguation of ["earlier", "later", "compatible"]) {
+ const timeZone = new SkippedDateTime();
+ const instance = new Temporal.ZonedDateTime(0n, timeZone, nonBuiltinISOCalendar);
+
+ instance.with({ day: 1 }, { disambiguation });
+
+ assert.sameValue(timeZone.calls, 2, "getPossibleInstantsFor should have been called 2 times");
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..2dc6beff59
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/infinity-throws-rangeerror.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.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.zoneddatetime.prototype.with
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((prop) => {
+ ["constrain", "reject"].forEach((overflow) => {
+ assert.throws(RangeError, () => instance.with({ [prop]: inf }, { overflow }), `${prop} property cannot be ${inf} (overflow ${overflow}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.with({ [prop]: obj }, { overflow }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/length.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/length.js
new file mode 100644
index 0000000000..89ac918633
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/length.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.zoneddatetime.prototype.with
+description: Temporal.ZonedDateTime.prototype.with.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.ZonedDateTime.prototype.with, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/minimum-instant-with-one-hour-offset.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/minimum-instant-with-one-hour-offset.js
new file mode 100644
index 0000000000..09ebb9c90b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/minimum-instant-with-one-hour-offset.js
@@ -0,0 +1,33 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.with
+description: >
+ Throws a RangeError when ZonedDateTime at minimum instant and an explicit +1h offset.
+info: |
+ Temporal.ZonedDateTime.prototype.with ( temporalZonedDateTimeLike [ , options ] )
+ ...
+ 21. Let epochNanoseconds be ? InterpretISODateTimeOffset(dateTimeResult.[[Year]],
+ dateTimeResult.[[Month]], dateTimeResult.[[Day]], dateTimeResult.[[Hour]],
+ dateTimeResult.[[Minute]], dateTimeResult.[[Second]], dateTimeResult.[[Millisecond]],
+ dateTimeResult.[[Microsecond]], dateTimeResult.[[Nanosecond]], option, offsetNanoseconds,
+ timeZone, disambiguation, offset, match exactly).
+ ...
+features: [Temporal]
+---*/
+
+let zdt = new Temporal.ZonedDateTime(-86_40000_00000_00000_00000n, "UTC");
+
+let temporalZonedDateTimeLike = {
+ offset: "+01",
+};
+
+let options = {
+ offset: "use",
+};
+
+assert.throws(RangeError, () => zdt.with(temporalZonedDateTimeLike, options));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/name.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/name.js
new file mode 100644
index 0000000000..cfff0818fd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/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.zoneddatetime.prototype.with
+description: Temporal.ZonedDateTime.prototype.with.name is "with".
+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.ZonedDateTime.prototype.with, "name", {
+ value: "with",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/not-a-constructor.js
new file mode 100644
index 0000000000..6487ca2e44
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/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.zoneddatetime.prototype.with
+description: >
+ Temporal.ZonedDateTime.prototype.with 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.ZonedDateTime.prototype.with();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.with), false,
+ "isConstructor(Temporal.ZonedDateTime.prototype.with)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/offset-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/offset-invalid-string.js
new file mode 100644
index 0000000000..d099f8857a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/offset-invalid-string.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.zoneddatetime.protoype.with
+description: RangeError thrown when offset option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaloffset step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"offset"*, « String », « *"prefer"*, *"use"*, *"ignore"*, *"reject"* », _fallback_).
+ sec-temporal.zoneddatetime.protoype.with step 15:
+ 15. Let _offset_ be ? ToTemporalOffset(_options_, *"prefer"*).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+assert.throws(RangeError, () => datetime.with({ hour: 2 }, { offset: "other string" }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/offset-property-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/offset-property-invalid-string.js
new file mode 100644
index 0000000000..cc48282502
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/offset-property-invalid-string.js
@@ -0,0 +1,35 @@
+// |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.zoneddatetime.prototype.with
+description: Property bag with offset property is rejected if offset is in the wrong format
+features: [Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+
+const offsetOptions = ['use', 'prefer', 'ignore', 'reject'];
+
+const badOffsets = [
+ "00:00", // missing sign
+ "+0", // too short
+ "-000:00", // too long
+ 0, // must be a string
+ null, // must be a string
+ true, // must be a string
+ 1000n, // must be a string
+];
+offsetOptions.forEach((offsetOption) => {
+ badOffsets.forEach((offset) => {
+ assert.throws(
+ typeof(offset) === 'string' ? RangeError : TypeError,
+ () => instance.with({ offset }, { offset: offsetOption }),
+ `"${offset} is not a valid offset string (with ${offsetOption} offset option)`,
+ );
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/offset-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/offset-undefined.js
new file mode 100644
index 0000000000..f48b9c750e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/offset-undefined.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.zoneddatetime.prototype.with
+description: Fallback value for offset option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaloffset step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"offset"*, « String », « *"prefer"*, *"use"*, *"ignore"*, *"reject"* », _fallback_).
+ sec-temporal.zoneddatetime.protoype.with step 15:
+ 15. Let _offset_ be ? ToTemporalOffset(_options_, *"prefer"*).
+features: [Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("-03:30");
+
+const datetime = new Temporal.ZonedDateTime(1572757201_000_000_000n, timeZone);
+const explicit = datetime.with({ minute: 31 }, { offset: undefined });
+assert.sameValue(explicit.epochNanoseconds, 1572757261_000_000_000n, "default offset is prefer");
+const implicit = datetime.with({ minute: 31 }, {});
+assert.sameValue(implicit.epochNanoseconds, 1572757261_000_000_000n, "default offset is prefer");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/offset-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/offset-wrong-type.js
new file mode 100644
index 0000000000..af450155ca
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/offset-wrong-type.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.zoneddatetime.prototype.with
+description: Type conversions for offset option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaloffset step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"offset"*, « String », « *"prefer"*, *"use"*, *"ignore"*, *"reject"* », _fallback_).
+ sec-temporal.zoneddatetime.protoype.with step 15:
+ 15. Let _offset_ be ? ToTemporalOffset(_options_, *"prefer"*).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+TemporalHelpers.checkStringOptionWrongType("offset", "prefer",
+ (offset) => datetime.with({ hour: 2 }, { offset }),
+ (result, descr) => assert.sameValue(result.epochNanoseconds, 1_000_003_600_987_654_321n, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/options-object.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/options-object.js
new file mode 100644
index 0000000000..9674f868a4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/options-object.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.zoneddatetime.prototype.with
+description: Empty or a function object may be used as options
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(0n, "UTC");
+
+const result1 = instance.with({ day: 5 }, {});
+assert.sameValue(
+ result1.epochNanoseconds, 345600000000000n, "UTC",
+ "options may be an empty plain object"
+);
+
+const result2 = instance.with({ day: 5 }, () => {});
+assert.sameValue(
+ result2.epochNanoseconds, 345600000000000n, "UTC",
+ "options may be a function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/options-undefined.js
new file mode 100644
index 0000000000..e648410e0e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/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.zoneddatetime.prototype.with
+description: Verify that undefined options are handled correctly.
+features: [BigInt, Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(949494896_987_654_321n, "UTC");
+const fields = { day: 31 };
+
+const explicit = datetime.with(fields, undefined);
+assert.sameValue(explicit.month, 2, "default overflow is constrain");
+assert.sameValue(explicit.day, 29, "default overflow is constrain");
+
+const implicit = datetime.with(fields);
+assert.sameValue(implicit.month, 2, "default overflow is constrain");
+assert.sameValue(implicit.day, 29, "default overflow is constrain");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/options-wrong-type.js
new file mode 100644
index 0000000000..94ea786d19
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/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.zoneddatetime.prototype.with
+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.ZonedDateTime(0n, "UTC");
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.with({ day: 5 }, value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/order-of-operations.js
new file mode 100644
index 0000000000..18387ceb9a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/order-of-operations.js
@@ -0,0 +1,177 @@
+// |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.zoneddatetime.prototype.with
+description: Properties on objects passed to with() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ // RejectObjectWithCalendarOrTimeZone
+ "get fields.calendar",
+ "get fields.timeZone",
+ // CopyDataProperties
+ "ownKeys options",
+ "getOwnPropertyDescriptor options.overflow",
+ "get options.overflow",
+ "getOwnPropertyDescriptor options.disambiguation",
+ "get options.disambiguation",
+ "getOwnPropertyDescriptor options.offset",
+ "get options.offset",
+ "getOwnPropertyDescriptor options.extra",
+ "get options.extra",
+ // lookup
+ "get this.calendar.dateFromFields",
+ "get this.calendar.fields",
+ "get this.calendar.mergeFields",
+ // lookup
+ "get this.timeZone.getOffsetNanosecondsFor",
+ "get this.timeZone.getPossibleInstantsFor",
+ // GetOffsetNanosecondsFor on receiver
+ "call this.timeZone.getOffsetNanosecondsFor",
+ // CalendarFields
+ "call this.calendar.fields",
+ // PrepareTemporalFields on receiver
+ "get this.calendar.day",
+ "call this.calendar.day",
+ "get this.calendar.month",
+ "call this.calendar.month",
+ "get this.calendar.monthCode",
+ "call this.calendar.monthCode",
+ "get this.calendar.year",
+ "call this.calendar.year",
+ // PrepareTemporalFields on argument
+ "get fields.day",
+ "get fields.day.valueOf",
+ "call fields.day.valueOf",
+ "get fields.hour",
+ "get fields.hour.valueOf",
+ "call fields.hour.valueOf",
+ "get fields.microsecond",
+ "get fields.microsecond.valueOf",
+ "call fields.microsecond.valueOf",
+ "get fields.millisecond",
+ "get fields.millisecond.valueOf",
+ "call fields.millisecond.valueOf",
+ "get fields.minute",
+ "get fields.minute.valueOf",
+ "call fields.minute.valueOf",
+ "get fields.month",
+ "get fields.month.valueOf",
+ "call fields.month.valueOf",
+ "get fields.monthCode",
+ "get fields.monthCode.toString",
+ "call fields.monthCode.toString",
+ "get fields.nanosecond",
+ "get fields.nanosecond.valueOf",
+ "call fields.nanosecond.valueOf",
+ "get fields.offset",
+ "get fields.offset.toString",
+ "call fields.offset.toString",
+ "get fields.second",
+ "get fields.second.valueOf",
+ "call fields.second.valueOf",
+ "get fields.year",
+ "get fields.year.valueOf",
+ "call fields.year.valueOf",
+ // CalendarMergeFields
+ "call this.calendar.mergeFields",
+ // InterpretTemporalDateTimeFields
+ "get options.disambiguation.toString",
+ "call options.disambiguation.toString",
+ "get options.offset.toString",
+ "call options.offset.toString",
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+ "call this.calendar.dateFromFields",
+ // InterpretISODateTimeOffset
+ "call this.timeZone.getPossibleInstantsFor",
+ "call this.timeZone.getOffsetNanosecondsFor",
+];
+const actual = [];
+
+const timeZone = TemporalHelpers.timeZoneObserver(actual, "this.timeZone");
+const calendar = TemporalHelpers.calendarObserver(actual, "this.calendar");
+const instance = new Temporal.ZonedDateTime(0n, timeZone, calendar);
+// clear observable operations that occurred during the constructor call
+actual.splice(0);
+
+const fields = TemporalHelpers.propertyBagObserver(actual, {
+ year: 1.7,
+ month: 1.7,
+ monthCode: "M01",
+ day: 1.7,
+ hour: 1.7,
+ minute: 1.7,
+ second: 1.7,
+ millisecond: 1.7,
+ microsecond: 1.7,
+ nanosecond: 1.7,
+ offset: "+00:00",
+}, "fields");
+
+const options = TemporalHelpers.propertyBagObserver(actual, {
+ overflow: "constrain",
+ disambiguation: "compatible",
+ offset: "prefer",
+ extra: "property",
+}, "options");
+
+instance.with(fields, options);
+assert.compareArray(actual, expected, "order of operations");
+actual.splice(0); // clear
+
+const dstTimeZone = TemporalHelpers.springForwardFallBackTimeZone();
+const dstTimeZoneObserver = TemporalHelpers.timeZoneObserver(actual, "this.timeZone", {
+ getOffsetNanosecondsFor: dstTimeZone.getOffsetNanosecondsFor,
+ getPossibleInstantsFor: dstTimeZone.getPossibleInstantsFor,
+});
+
+const dstInstance = new Temporal.ZonedDateTime(37800_000_000_000n /* 1970-01-01T02:30-08:00 */, dstTimeZoneObserver, calendar);
+actual.splice(0); // clear calls that happened in constructor
+
+const fallBackFields = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2000,
+ month: 10,
+ monthCode: "M10",
+ day: 29,
+ hour: 1,
+ minute: 30,
+ second: 0,
+ millisecond: 0,
+ microsecond: 0,
+ nanosecond: 0,
+ offset: "+00:00", // ignored
+}, "fields");
+dstInstance.with(fallBackFields, options);
+assert.compareArray(actual, expected.concat([
+ // extra call in InterpretISODateTimeOffset
+ "call this.timeZone.getOffsetNanosecondsFor",
+]), "order of operations at repeated wall-clock time");
+actual.splice(0); // clear
+
+const springForwardFields = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2000,
+ month: 4,
+ monthCode: "M04",
+ day: 2,
+ hour: 2,
+ minute: 30,
+ second: 0,
+ millisecond: 0,
+ microsecond: 0,
+ nanosecond: 0,
+ offset: "+00:00", // ignored
+}, "fields");
+dstInstance.with(springForwardFields, options);
+assert.compareArray(actual, expected.concat([
+ // DisambiguatePossibleInstants
+ "call this.timeZone.getOffsetNanosecondsFor",
+ "call this.timeZone.getPossibleInstantsFor",
+]), "order of operations at skipped wall-clock time");
+actual.splice(0); // clear
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/overflow-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/overflow-invalid-string.js
new file mode 100644
index 0000000000..8e2be9a65f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/overflow-invalid-string.js
@@ -0,0 +1,31 @@
+// |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.zoneddatetime.prototype.with
+description: RangeError thrown when overflow option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-interprettemporaldatetimefields steps 2–3:
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+ 3. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.zoneddatetime.prototype.with step 24:
+ 24. Let _dateTimeResult_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+const badOverflows = ["", "CONSTRAIN", "balance", "other string", "constra\u0131n", "reject\0"];
+for (const overflow of badOverflows) {
+ assert.throws(
+ RangeError,
+ () => datetime.with({ minute: 45 }, { overflow }),
+ `invalid overflow ("${overflow}")`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/overflow-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/overflow-undefined.js
new file mode 100644
index 0000000000..17e7dd007f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/overflow-undefined.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.zoneddatetime.prototype.with
+description: Fallback value for overflow option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-interprettemporaldatetimefields steps 2–3:
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+ 3. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.zoneddatetime.prototype.with step 24:
+ 24. Let _dateTimeResult_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+const explicit = datetime.with({ second: 67 }, { overflow: undefined });
+assert.sameValue(explicit.epochNanoseconds, 1_000_000_019_987_654_321n, "default overflow is constrain");
+const implicit = datetime.with({ second: 67 }, {});
+assert.sameValue(implicit.epochNanoseconds, 1_000_000_019_987_654_321n, "default overflow is constrain");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/overflow-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/overflow-wrong-type.js
new file mode 100644
index 0000000000..1ce7632251
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/overflow-wrong-type.js
@@ -0,0 +1,48 @@
+// |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.plaindatetime.prototype.with
+description: Type conversions for overflow option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-interprettemporaldatetimefields steps 2–3:
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+ 3. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.zoneddatetime.prototype.with step 24:
+ 24. Let _dateTimeResult_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+
+// See TemporalHelpers.checkStringOptionWrongType(); this code path has
+// different expectations for observable calls
+
+assert.throws(RangeError, () => datetime.with({ second: 41 }, { overflow: null }), "null");
+assert.throws(RangeError, () => datetime.with({ second: 41 }, { overflow: true }), "true");
+assert.throws(RangeError, () => datetime.with({ second: 41 }, { overflow: false }), "false");
+assert.throws(TypeError, () => datetime.with({ second: 41 }, { overflow: Symbol() }), "symbol");
+assert.throws(RangeError, () => datetime.with({ second: 41 }, { overflow: 2 }), "number");
+assert.throws(RangeError, () => datetime.with({ second: 41 }, { overflow: 2n }), "bigint");
+assert.throws(RangeError, () => datetime.with({ second: 41 }, { overflow: {} }), "plain object");
+
+// toString property should only be read and converted to a string once, because
+// a copied object with the resulting string on it is passed to
+// Calendar.dateFromFields().
+const expected = [
+ "get overflow.toString",
+ "call overflow.toString",
+];
+const actual = [];
+const observer = TemporalHelpers.toPrimitiveObserver(actual, "constrain", "overflow");
+const result = datetime.with({ second: 41 }, { overflow: observer });
+assert.sameValue(result.epochNanoseconds, 1_000_000_001_987_654_321n, "object with toString");
+assert.compareArray(actual, expected, "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/prop-desc.js
new file mode 100644
index 0000000000..510a4ed819
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/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.zoneddatetime.prototype.with
+description: The "with" property of Temporal.ZonedDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.ZonedDateTime.prototype.with,
+ "function",
+ "`typeof ZonedDateTime.prototype.with` is `function`"
+);
+
+verifyProperty(Temporal.ZonedDateTime.prototype, "with", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/proto-in-calendar-fields.js
new file mode 100644
index 0000000000..e7eca2b136
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/proto-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |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.zoneddatetime.prototype.with
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const zoneddatetime = new Temporal.ZonedDateTime(1682892000000000000n, 'Europe/Madrid', calendar);
+
+assert.throws(RangeError, () => zoneddatetime.with({hour: 12}));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/read-time-fields-before-datefromfields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/read-time-fields-before-datefromfields.js
new file mode 100644
index 0000000000..0977701e40
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/read-time-fields-before-datefromfields.js
@@ -0,0 +1,29 @@
+// |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.zoneddatetime.prototype.with
+description: The time fields are read from the object before being passed to dateFromFields().
+info: |
+ sec-temporal.zoneddatetime.prototype.with step 23:
+ 23. Let _dateTimeResult_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+ sec-temporal-interprettemporaldatetimefields steps 1–2:
+ 1. Let _timeResult_ be ? ToTemporalTimeRecord(_fields_).
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarMakeInfinityTime();
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC", calendar);
+const newDatetime = datetime.with({ year: 2022 });
+
+assert.sameValue(newDatetime.hour, 1, "hour value");
+assert.sameValue(newDatetime.minute, 46, "minute value");
+assert.sameValue(newDatetime.second, 40, "second value");
+assert.sameValue(newDatetime.millisecond, 987, "millisecond value");
+assert.sameValue(newDatetime.microsecond, 654, "microsecond value");
+assert.sameValue(newDatetime.nanosecond, 321, "nanosecond value");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/receiver-offset-broken.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/receiver-offset-broken.js
new file mode 100644
index 0000000000..c47d2634eb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/receiver-offset-broken.js
@@ -0,0 +1,59 @@
+// |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.zoneddatetime.protoype.with
+description: >
+ TypeError thrown when the offset field of the argument or the object returned
+ from mergeFields is broken
+info: |
+ 7. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ 8. Append *"hour"*, *"microsecond"*, *"millisecond"*, *"minute"*, *"nanosecond"*, *"offset"*, and *"second"* to _fieldNames_.
+ 9. Let _fields_ be ? PrepareTemporalFields(_zonedDateTime_, _fieldNames_, « *"offset"* »).
+ 10. Let _partialZonedDateTime_ be ? PrepareTemporalFields(_temporalZonedDateTimeLike_, _fieldNames_, ~partial~).
+ 11. Set _fields_ to ? CalendarMergeFields(_calendar_, _fields_, _partialZonedDateTime_).
+ 12. Set _fields_ to ? PrepareTemporalFields(_fields_, _fieldNames_, « *"offset"* »).
+features: [Temporal]
+---*/
+
+class ObservedCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ this.resetCalls();
+ }
+
+ toString() {
+ return "observed-calendar";
+ }
+
+ mergeFields(original, additional) {
+ this.mergeFieldsCalled++;
+ const result = super.mergeFields(original, additional);
+ result.offset = Symbol("can't convert to string");
+ return result;
+ }
+
+ resetCalls() {
+ this.mergeFieldsCalled = 0;
+ }
+}
+
+const calendar = new ObservedCalendar();
+const dateTime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar);
+
+// Test throw in step 10
+
+assert.throws(TypeError, () => dateTime.with({ offset: Symbol("can't convert to string") }), "conversion failure on ZonedDateTime-like");
+assert.sameValue(calendar.mergeFieldsCalled, 0, "calendar.mergeFields should not be called");
+
+calendar.resetCalls();
+
+// Test throw in step 12 (before sabotaging the ZonedDateTime instance)
+
+assert.throws(TypeError, () => dateTime.with({ year: 2002 }), "conversion failure on sabotaged return value from mergeFields");
+assert.sameValue(calendar.mergeFieldsCalled, 1, "calendar.mergeFields was called once");
+
+calendar.resetCalls();
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/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/ZonedDateTime/prototype/with/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/subclassing-ignored.js
new file mode 100644
index 0000000000..7435848f8d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/subclassing-ignored.js
@@ -0,0 +1,31 @@
+// |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.zoneddatetime.prototype.with
+description: Objects of a subclass are never created as return values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.ZonedDateTime,
+ [10n, "UTC"],
+ "with",
+ [{ year: 2000 }],
+ (result) => {
+ assert.sameValue(result.epochNanoseconds, 946684800_000_000_010n, "epochNanoseconds result");
+ assert.sameValue(result.year, 2000, "year result");
+ assert.sameValue(result.month, 1, "month result");
+ assert.sameValue(result.day, 1, "day result");
+ assert.sameValue(result.hour, 0, "hour result");
+ assert.sameValue(result.minute, 0, "minute result");
+ assert.sameValue(result.second, 0, "second result");
+ assert.sameValue(result.millisecond, 0, "millisecond result");
+ assert.sameValue(result.microsecond, 0, "microsecond result");
+ assert.sameValue(result.nanosecond, 10, "nanosecond result");
+ },
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..abcf246b03
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,18 @@
+// |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.zoneddatetime.prototype.with
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.with({ day: 27 }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..49db5085a5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/timezone-getoffsetnanosecondsfor-not-callable.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.zoneddatetime.prototype.with
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => datetime.with({ day: 27 }, { offset: "prefer" }),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError (in offset=prefer and no disambiguation case)`
+ );
+
+ const badTimeZone = {
+ id: "Etc/Bad",
+ getPossibleInstantsFor() { return []; },
+ getOffsetNanosecondsFor: notCallable,
+ };
+ const badDateTime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, badTimeZone);
+ assert.throws(
+ TypeError,
+ () => badDateTime.with({ day: 27 }, { offset: "ignore" }),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError (in offset=ignore and no possible instants case)`
+ );
+ assert.throws(
+ TypeError,
+ () => badDateTime.with({ day: 27 }, { offset: "prefer" }),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError (in offset=prefer and no possible instants case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..cec9c2b6e5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,18 @@
+// |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.zoneddatetime.prototype.with
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.with({ day: 27 }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..7d1d89673b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/timezone-getoffsetnanosecondsfor-wrong-type.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.zoneddatetime.prototype.with
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.with({ day: 27 }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/timezone-getpossibleinstantsfor-iterable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/timezone-getpossibleinstantsfor-iterable.js
new file mode 100644
index 0000000000..c24bad7373
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/timezone-getpossibleinstantsfor-iterable.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.zoneddatetime.prototype.with
+description: An iterable returned from timeZone.getPossibleInstantsFor is consumed after each call
+info: |
+ sec-temporal.zoneddatetime.prototype.with step 24:
+ 24. Let _epochNanoseconds_ be ? InterpretISODateTimeOffset(_dateTimeResult_.[[Year]], [...], _dateTimeResult_.[[Nanosecond]], _offsetNanoseconds_, _timeZone_, _disambiguation_, _offset_).
+ sec-temporal-interpretisodatetimeoffset step 7:
+ 7. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_).
+ sec-temporal-getpossibleinstantsfor step 2:
+ 2. Let _list_ be ? IterableToList(_possibleInstants_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "2005-09-09T01:46:40",
+];
+
+TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => {
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, timeZone);
+ datetime.with({ year: 2005 });
+}, expected);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/zoned-datetime-like-at-minimum-date-time-with-offset.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/zoned-datetime-like-at-minimum-date-time-with-offset.js
new file mode 100644
index 0000000000..f4fbeff05b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/zoned-datetime-like-at-minimum-date-time-with-offset.js
@@ -0,0 +1,42 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.with
+description: >
+ Throws a RangeError for the minimum date/value with UTC offset and an explicit offset.
+info: |
+ Temporal.ZonedDateTime.prototype.with ( temporalZonedDateTimeLike [ , options ] )
+ ...
+ 21. Let epochNanoseconds be ? InterpretISODateTimeOffset(dateTimeResult.[[Year]],
+ dateTimeResult.[[Month]], dateTimeResult.[[Day]], dateTimeResult.[[Hour]],
+ dateTimeResult.[[Minute]], dateTimeResult.[[Second]], dateTimeResult.[[Millisecond]],
+ dateTimeResult.[[Microsecond]], dateTimeResult.[[Nanosecond]], option, offsetNanoseconds,
+ timeZone, disambiguation, offset, match exactly).
+ ...
+features: [Temporal]
+---*/
+
+let zdt = new Temporal.ZonedDateTime(0n, "UTC");
+
+let temporalZonedDateTimeLike = {
+ year: -271821,
+ month: 4,
+ day: 19,
+ hour: 1,
+ minute: 0,
+ second: 0,
+ millisecond: 0,
+ microsecond: 0,
+ nanosecond: 0,
+ offset: "+00",
+};
+
+let options = {
+ offset: "use",
+};
+
+assert.throws(RangeError, () => zdt.with(temporalZonedDateTimeLike, options));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/zoned-datetime-like-at-minimum-date-time.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/zoned-datetime-like-at-minimum-date-time.js
new file mode 100644
index 0000000000..fe7a12d195
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/zoned-datetime-like-at-minimum-date-time.js
@@ -0,0 +1,37 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.with
+description: >
+ Throws a RangeError for the minimum date/value with UTC offset.
+info: |
+ Temporal.ZonedDateTime.prototype.with ( temporalZonedDateTimeLike [ , options ] )
+ ...
+ 21. Let epochNanoseconds be ? InterpretISODateTimeOffset(dateTimeResult.[[Year]],
+ dateTimeResult.[[Month]], dateTimeResult.[[Day]], dateTimeResult.[[Hour]],
+ dateTimeResult.[[Minute]], dateTimeResult.[[Second]], dateTimeResult.[[Millisecond]],
+ dateTimeResult.[[Microsecond]], dateTimeResult.[[Nanosecond]], option, offsetNanoseconds,
+ timeZone, disambiguation, offset, match exactly).
+ ...
+features: [Temporal]
+---*/
+
+let zdt = new Temporal.ZonedDateTime(0n, "UTC");
+
+let temporalZonedDateTimeLike = {
+ year: -271821,
+ month: 4,
+ day: 19,
+ hour: 1,
+ minute: 0,
+ second: 0,
+ millisecond: 0,
+ microsecond: 0,
+ nanosecond: 0,
+};
+
+assert.throws(RangeError, () => zdt.with(temporalZonedDateTimeLike));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/branding.js
new file mode 100644
index 0000000000..b8fbe1f637
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/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.zoneddatetime.prototype.withcalendar
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const withCalendar = Temporal.ZonedDateTime.prototype.withCalendar;
+
+assert.sameValue(typeof withCalendar, "function");
+
+const args = [new Temporal.Calendar("iso8601")];
+
+assert.throws(TypeError, () => withCalendar.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => withCalendar.apply(null, args), "null");
+assert.throws(TypeError, () => withCalendar.apply(true, args), "true");
+assert.throws(TypeError, () => withCalendar.apply("", args), "empty string");
+assert.throws(TypeError, () => withCalendar.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => withCalendar.apply(1, args), "1");
+assert.throws(TypeError, () => withCalendar.apply({}, args), "plain object");
+assert.throws(TypeError, () => withCalendar.apply(Temporal.ZonedDateTime, args), "Temporal.ZonedDateTime");
+assert.throws(TypeError, () => withCalendar.apply(Temporal.ZonedDateTime.prototype, args), "Temporal.ZonedDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..8c5ba81c9d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.zoneddatetime.prototype.withcalendar
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const idOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "id");
+Object.defineProperty(Temporal.Calendar.prototype, "id", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("id should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.withCalendar("iso8601");
+
+Object.defineProperty(Temporal.Calendar.prototype, "id", idOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/builtin.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/builtin.js
new file mode 100644
index 0000000000..373ccd5e4a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/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.zoneddatetime.prototype.withcalendar
+description: >
+ Tests that Temporal.ZonedDateTime.prototype.withCalendar
+ 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.ZonedDateTime.prototype.withCalendar),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.withCalendar),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.withCalendar),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.ZonedDateTime.prototype.withCalendar.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/calendar-case-insensitive.js
new file mode 100644
index 0000000000..34ca85e532
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/calendar-case-insensitive.js
@@ -0,0 +1,39 @@
+// |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.zoneddatetime.prototype.withcalendar
+description: Calendar names are case-insensitive
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "replace-me",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+});
+
+const arg = "iSo8601";
+const result = instance.withCalendar(arg);
+assert.sameValue(result.calendarId, "iso8601", "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/calendar-number.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/calendar-number.js
new file mode 100644
index 0000000000..ba5a6653a1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/calendar-number.js
@@ -0,0 +1,50 @@
+// |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.zoneddatetime.prototype.withcalendar
+description: A number is not allowed to be a calendar
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "replace-me",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+});
+
+const numbers = [
+ 1,
+ -19761118,
+ 19761118,
+ 1234567890,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => instance.withCalendar(arg),
+ "A number is not a valid ISO string for Calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/calendar-string-leap-second.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/calendar-string-leap-second.js
new file mode 100644
index 0000000000..d1b40c2f7a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/calendar-string-leap-second.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.zoneddatetime.prototype.withcalendar
+description: Leap second is a valid ISO string for Calendar
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "replace-me",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+});
+
+const arg = "2016-12-31T23:59:60";
+const result = instance.withCalendar(arg);
+assert.sameValue(
+ result.calendarId,
+ "iso8601",
+ "leap second is a valid ISO string for Calendar"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/calendar-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/calendar-string.js
new file mode 100644
index 0000000000..8acaca9dec
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/calendar-string.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.zoneddatetime.prototype.withcalendar
+description: A calendar ID is valid input for Calendar
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "replace-me",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+});
+
+const arg = "iso8601";
+
+const result = instance.withCalendar(arg);
+assert.sameValue(result.getISOFields().calendar, "iso8601", `Calendar created from string "${arg}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/calendar-temporal-object.js
new file mode 100644
index 0000000000..452864682f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/calendar-temporal-object.js
@@ -0,0 +1,64 @@
+// |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.zoneddatetime.prototype.withcalendar
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal-totemporalcalendar step 1.b:
+ b. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const plainDate = new Temporal.PlainDate(2000, 5, 2);
+const plainDateTime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const plainMonthDay = new Temporal.PlainMonthDay(5, 2);
+const plainYearMonth = new Temporal.PlainYearMonth(2000, 5);
+const zonedDateTime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+
+[plainDate, plainDateTime, plainMonthDay, plainYearMonth, zonedDateTime].forEach((arg) => {
+ const actual = [];
+ const expected = [];
+
+ const calendar = arg.getISOFields().calendar;
+
+ Object.defineProperty(arg, "calendar", {
+ get() {
+ actual.push("get calendar");
+ return calendar;
+ },
+ });
+
+ const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "replace-me",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+});
+ const result = instance.withCalendar(arg);
+ assert.sameValue(result.getISOFields().calendar, calendar, "Temporal object coerced to calendar");
+
+ assert.compareArray(actual, expected, "calendar getter not called");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/calendar-wrong-type.js
new file mode 100644
index 0000000000..34ff322aea
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/calendar-wrong-type.js
@@ -0,0 +1,64 @@
+// |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.zoneddatetime.prototype.withcalendar
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or object for Calendar
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "replace-me",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+});
+
+const primitiveTests = [
+ [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.withCalendar(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.withCalendar(arg), `${description} is not a valid object and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/length.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/length.js
new file mode 100644
index 0000000000..46193d5f90
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/length.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.zoneddatetime.prototype.withcalendar
+description: Temporal.ZonedDateTime.prototype.withCalendar.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.ZonedDateTime.prototype.withCalendar, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/missing-argument.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/missing-argument.js
new file mode 100644
index 0000000000..c59dfc0273
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/missing-argument.js
@@ -0,0 +1,15 @@
+// |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.zoneddatetime.prototype.withcalendar
+description: TypeError thrown when calendar argument not given
+features: [Temporal]
+---*/
+
+const zonedDateTime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+assert.throws(TypeError, () => zonedDateTime.withCalendar(), "missing argument");
+assert.throws(TypeError, () => zonedDateTime.withCalendar(undefined), "undefined argument");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/name.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/name.js
new file mode 100644
index 0000000000..e5e77eef78
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/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.zoneddatetime.prototype.withcalendar
+description: Temporal.ZonedDateTime.prototype.withCalendar.name is "withCalendar".
+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.ZonedDateTime.prototype.withCalendar, "name", {
+ value: "withCalendar",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/not-a-constructor.js
new file mode 100644
index 0000000000..68de93d8ca
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/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.zoneddatetime.prototype.withcalendar
+description: >
+ Temporal.ZonedDateTime.prototype.withCalendar 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.ZonedDateTime.prototype.withCalendar();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.withCalendar), false,
+ "isConstructor(Temporal.ZonedDateTime.prototype.withCalendar)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/prop-desc.js
new file mode 100644
index 0000000000..4afd544193
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/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.zoneddatetime.prototype.withcalendar
+description: The "withCalendar" property of Temporal.ZonedDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.ZonedDateTime.prototype.withCalendar,
+ "function",
+ "`typeof ZonedDateTime.prototype.withCalendar` is `function`"
+);
+
+verifyProperty(Temporal.ZonedDateTime.prototype, "withCalendar", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/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/ZonedDateTime/prototype/withCalendar/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/subclassing-ignored.js
new file mode 100644
index 0000000000..88167a59f0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/subclassing-ignored.js
@@ -0,0 +1,57 @@
+// |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.zoneddatetime.prototype.withcalendar
+description: Objects of a subclass are never created as return values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const customCalendar = {
+ year() { return 1900; },
+ month() { return 2; },
+ day() { return 5; },
+ toString() { return "custom-calendar"; },
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "custom-calendar",
+ inLeapYear() {},
+ mergeFields() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.ZonedDateTime,
+ [10n, "UTC"],
+ "withCalendar",
+ [customCalendar],
+ (result) => {
+ assert.sameValue(result.epochNanoseconds, 10n, "epochNanoseconds result");
+ assert.sameValue(result.year, 1900, "year result");
+ assert.sameValue(result.month, 2, "month result");
+ assert.sameValue(result.day, 5, "day result");
+ assert.sameValue(result.hour, 0, "hour result");
+ assert.sameValue(result.minute, 0, "minute result");
+ assert.sameValue(result.second, 0, "second result");
+ assert.sameValue(result.millisecond, 0, "millisecond result");
+ assert.sameValue(result.microsecond, 0, "microsecond result");
+ assert.sameValue(result.nanosecond, 10, "nanosecond result");
+ assert.sameValue(result.getCalendar(), customCalendar, "calendar result");
+ },
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..a5b009fd98
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-builtin-calendar-no-array-iteration.js
@@ -0,0 +1,24 @@
+// |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.zoneddatetime.prototype.withplaindate
+description: >
+ Calling the method with a property bag argument with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+const arg = { year: 2000, month: 5, day: 2, calendar: "iso8601" };
+instance.withPlainDate(arg);
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-calendar-datefromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..a91d766133
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-calendar-datefromfields-called-with-null-prototype-fields.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.zoneddatetime.prototype.withplaindate
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+const arg = { year: 2000, month: 5, day: 2, calendar };
+instance.withPlainDate(arg);
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..cc96585bc9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-constructor-in-calendar-fields.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.zoneddatetime.prototype.withplaindate
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+
+assert.throws(RangeError, () => instance.withPlainDate(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-duplicate-calendar-fields.js
new file mode 100644
index 0000000000..4ef2d3ba44
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-duplicate-calendar-fields.js
@@ -0,0 +1,19 @@
+// |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.zoneddatetime.prototype.withplaindate
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['day'], ['month'], ['monthCode'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar };
+ const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+
+ assert.throws(RangeError, () => instance.withPlainDate(arg));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-leap-second.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-leap-second.js
new file mode 100644
index 0000000000..8f0b291f86
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-leap-second.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.zoneddatetime.prototype.withplaindate
+description: Leap second is a valid ISO string for PlainDate
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+
+let arg = "2016-12-31T23:59:60";
+const result1 = instance.withPlainDate(arg);
+assert.sameValue(
+ result1.epochNanoseconds,
+ 1_483_148_800_000_000_000n,
+ "leap second is a valid ISO string for PlainDate"
+);
+
+arg = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+const result2 = instance.withPlainDate(arg);
+assert.sameValue(
+ result2.epochNanoseconds,
+ 1_483_148_800_000_000_000n,
+ "second: 60 is ignored in property bag for PlainDate"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-number.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-number.js
new file mode 100644
index 0000000000..107e93afd1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/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.zoneddatetime.prototype.withplaindate
+description: A number cannot be used in place of a Temporal.PlainDate
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+
+const numbers = [
+ 1,
+ 19761118,
+ -19761118,
+ 1234567890,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => instance.withPlainDate(arg),
+ 'Numbers cannot be used in place of an ISO string for PlainDate'
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-plaindatetime.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-plaindatetime.js
new file mode 100644
index 0000000000..e94e741220
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-plaindatetime.js
@@ -0,0 +1,32 @@
+// |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.zoneddatetime.withplaindate
+description: Fast path for converting Temporal.PlainDateTime to Temporal.PlainDate by reading internal slots
+info: |
+ sec-temporal.zoneddatetime.prototype.withplaindate step 3:
+ 3. Let _plainDate_ be ? ToTemporalDate(_plainDateLike_).
+ sec-temporal-totemporaldate step 2.b:
+ b. If _item_ has an [[InitializedTemporalDateTime]] internal slot, then
+ i. Return ! CreateTemporalDate(_item_.[[ISOYear]], _item_.[[ISOMonth]], _item_.[[ISODay]], _item_.[[Calendar]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkPlainDateTimeConversionFastPath((datetime) => {
+ const receiver = new Temporal.ZonedDateTime(1_000_000_000_123_456_789n, "UTC");
+ const result = receiver.withPlainDate(datetime);
+ assert.sameValue(result.year, 2000, "year result");
+ assert.sameValue(result.month, 5, "month result");
+ assert.sameValue(result.day, 2, "day result");
+ assert.sameValue(result.hour, 1, "hour result");
+ assert.sameValue(result.minute, 46, "minute result");
+ assert.sameValue(result.second, 40, "second result");
+ assert.sameValue(result.millisecond, 123, "millisecond result");
+ assert.sameValue(result.microsecond, 456, "microsecond result");
+ assert.sameValue(result.nanosecond, 789, "nanosecond result");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-propertybag-calendar-case-insensitive.js
new file mode 100644
index 0000000000..6b91d2f8fd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-propertybag-calendar-case-insensitive.js
@@ -0,0 +1,20 @@
+// |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.zoneddatetime.prototype.withplaindate
+description: The calendar name is case-insensitive
+features: [Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, timeZone);
+
+const calendar = "IsO8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.withPlainDate(arg);
+assert.sameValue(result.epochNanoseconds, 217_129_600_000_000_000n, "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-propertybag-calendar-leap-second.js
new file mode 100644
index 0000000000..28f8d33d32
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-propertybag-calendar-leap-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.zoneddatetime.prototype.withplaindate
+description: Leap second is a valid ISO string for a calendar in a property bag
+features: [Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, timeZone);
+
+const calendar = "2016-12-31T23:59:60+00:00[UTC]";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.withPlainDate(arg);
+assert.sameValue(
+ result.epochNanoseconds,
+ 217_129_600_000_000_000n,
+ "leap second is a valid ISO string for calendar"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-propertybag-calendar-number.js
new file mode 100644
index 0000000000..c3fb370434
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-propertybag-calendar-number.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.zoneddatetime.prototype.withplaindate
+description: A number as calendar in a property bag is not accepted
+features: [Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, timeZone);
+
+const numbers = [
+ 1,
+ 19970327,
+ -19970327,
+ 1234567890,
+];
+
+for (const calendar of numbers) {
+ const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+ assert.throws(
+ TypeError,
+ () => instance.withPlainDate(arg),
+ "Numbers cannot be used as a calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-propertybag-calendar-string.js
new file mode 100644
index 0000000000..cef382bdea
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-propertybag-calendar-string.js
@@ -0,0 +1,20 @@
+// |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.zoneddatetime.prototype.withplaindate
+description: A calendar ID is valid input for Calendar
+features: [Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, timeZone);
+
+const calendar = "iso8601";
+
+const arg = { year: 1976, monthCode: "M11", day: 18, calendar };
+const result = instance.withPlainDate(arg);
+assert.sameValue(result.epochNanoseconds, 217_129_600_000_000_000n, `Calendar created from string "${calendar}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-propertybag-calendar-wrong-type.js
new file mode 100644
index 0000000000..da5c268ad2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-propertybag-calendar-wrong-type.js
@@ -0,0 +1,46 @@
+// |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.zoneddatetime.prototype.withplaindate
+description: >
+ Appropriate error thrown when a calendar property from a property bag cannot
+ be converted to a calendar object or string
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, timeZone);
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [calendar, description] of primitiveTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ typeof calendar === 'string' ? RangeError : TypeError,
+ () => instance.withPlainDate(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+ [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields()
+];
+
+for (const [calendar, description] of typeErrorTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(TypeError, () => instance.withPlainDate(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/ZonedDateTime/prototype/withPlainDate/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-propertybag-calendar-year-zero.js
new file mode 100644
index 0000000000..3d7d95215e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-propertybag-calendar-year-zero.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.zoneddatetime.prototype.withplaindate
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T17:45",
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+01:00",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, timeZone);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.withPlainDate(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-proto-in-calendar-fields.js
new file mode 100644
index 0000000000..89ee626a0c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-proto-in-calendar-fields.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.zoneddatetime.prototype.withplaindate
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+
+assert.throws(RangeError, () => instance.withPlainDate(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..23736baf20
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-calendar-annotation.js
@@ -0,0 +1,34 @@
+// |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.zoneddatetime.prototype.withplaindate
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02[u-ca=iso8601]", "without time or time zone"],
+ ["2000-05-02[UTC][u-ca=iso8601]", "with time zone and no time"],
+ ["2000-05-02T15:23[u-ca=iso8601]", "without time zone"],
+ ["2000-05-02T15:23[UTC][u-ca=iso8601]", "with time zone"],
+ ["2000-05-02T15:23[!u-ca=iso8601]", "with ! and no time zone"],
+ ["2000-05-02T15:23[UTC][!u-ca=iso8601]", "with ! and time zone"],
+ ["2000-05-02T15:23[u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.withPlainDate(arg);
+
+ assert.sameValue(
+ result.epochNanoseconds,
+ 957_225_600_000_000_000n,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..e74fc7011d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/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.zoneddatetime.prototype.withplaindate
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[!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 timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.withPlainDate(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..238789510e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-date-with-utc-offset.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.zoneddatetime.prototype.withplaindate
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+
+const validStrings = [
+ "2000-05-02T00+00:00",
+ "2000-05-02T00+00:00[UTC]",
+ "2000-05-02T00+00:00[!UTC]",
+ "2000-05-02T00-02:30[America/St_Johns]",
+];
+
+for (const arg of validStrings) {
+ const result = instance.withPlainDate(arg);
+
+ assert.sameValue(
+ result.epochNanoseconds,
+ 957_225_600_000_000_000n,
+ `"${arg}" is a valid UTC offset with time for PlainDate`
+ );
+}
+
+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.withPlainDate(arg),
+ `"${arg}" UTC offset without time is not valid for PlainDate`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-invalid.js
new file mode 100644
index 0000000000..64bac8afce
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-invalid.js
@@ -0,0 +1,64 @@
+// |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.zoneddatetime.prototype.withplaindate
+description: >
+ RangeError thrown if an invalid ISO string (or syntactically valid ISO string
+ that is not supported) is used as a PlainDate
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ // invalid ISO strings:
+ "",
+ "invalid iso8601",
+ "2020-01-00",
+ "2020-01-32",
+ "2020-02-30",
+ "2021-02-29",
+ "2020-00-01",
+ "2020-13-01",
+ "2020-01-01T",
+ "2020-01-01T25:00:00",
+ "2020-01-01T01:60:00",
+ "2020-01-01T01:60:61",
+ "2020-01-01junk",
+ "2020-01-01T00:00:00junk",
+ "2020-01-01T00:00:00+00:00junk",
+ "2020-01-01T00:00:00+00:00[UTC]junk",
+ "2020-01-01T00:00:00+00:00[UTC][u-ca=iso8601]junk",
+ "02020-01-01",
+ "2020-001-01",
+ "2020-01-001",
+ "2020-01-01T001",
+ "2020-01-01T01:001",
+ "2020-01-01T01:01:001",
+ // valid, but forms not supported in Temporal:
+ "2020-W01-1",
+ "2020-001",
+ "+0002020-01-01",
+ // valid, but this calendar must not exist:
+ "2020-01-01[u-ca=notexist]",
+ // may be valid in other contexts, but insufficient information for PlainDate:
+ "2020-01",
+ "+002020-01",
+ "01-01",
+ "2020-W01",
+ "P1Y",
+ "-P12Y",
+ // valid, but outside the supported range:
+ "-999999-01-01",
+ "+999999-01-01",
+];
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => instance.withPlainDate(arg),
+ `"${arg}" should not be a valid ISO string for a PlainDate`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..b255b8f07d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-multiple-calendar.js
@@ -0,0 +1,33 @@
+// |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.zoneddatetime.prototype.withplaindate
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[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 timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.withPlainDate(arg),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..e79719e26c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/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.zoneddatetime.prototype.withplaindate
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[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 timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.withPlainDate(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-time-separators.js
new file mode 100644
index 0000000000..fef493b58b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-time-separators.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.zoneddatetime.prototype.withplaindate
+description: Time separator in string argument can vary
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02T15:23", "uppercase T"],
+ ["2000-05-02t15:23", "lowercase T"],
+ ["2000-05-02 15:23", "space between date and time"],
+];
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.withPlainDate(arg);
+
+ assert.sameValue(
+ result.epochNanoseconds,
+ 957_225_600_000_000_000n,
+ `variant time separators (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..831730e461
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-time-zone-annotation.js
@@ -0,0 +1,39 @@
+// |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.zoneddatetime.prototype.withplaindate
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02[Asia/Kolkata]", "named, with no time"],
+ ["2000-05-02[!Europe/Vienna]", "named, with ! and no time"],
+ ["2000-05-02[+00:00]", "numeric, with no time"],
+ ["2000-05-02[!-02:30]", "numeric, with ! and no time"],
+ ["2000-05-02T15:23[America/Sao_Paulo]", "named, with no offset"],
+ ["2000-05-02T15:23[!Asia/Tokyo]", "named, with ! and no offset"],
+ ["2000-05-02T15:23[-02:30]", "numeric, with no offset"],
+ ["2000-05-02T15:23[!+00:00]", "numeric, with ! and no offset"],
+ ["2000-05-02T15:23+00:00[America/New_York]", "named, with offset"],
+ ["2000-05-02T15:23+00:00[!UTC]", "named, with offset and !"],
+ ["2000-05-02T15:23+00:00[+01:00]", "numeric, with offset"],
+ ["2000-05-02T15:23+00:00[!-08:00]", "numeric, with offset and !"],
+];
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.withPlainDate(arg);
+
+ assert.sameValue(
+ result.epochNanoseconds,
+ 957_225_600_000_000_000n,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..b7447b455c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-unknown-annotation.js
@@ -0,0 +1,33 @@
+// |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.zoneddatetime.prototype.withplaindate
+description: Various forms of unknown annotation
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2000-05-02[foo=bar]", "without time"],
+ ["2000-05-02T15:23[foo=bar]", "alone"],
+ ["2000-05-02T15:23[UTC][foo=bar]", "with time zone"],
+ ["2000-05-02T15:23[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["2000-05-02T15:23[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["2000-05-02T15:23[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.withPlainDate(arg);
+
+ assert.sameValue(
+ result.epochNanoseconds,
+ 957_225_600_000_000_000n,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..74071fa90e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-with-utc-designator.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.zoneddatetime.prototype.withplaindate
+description: RangeError thrown if a string with UTC designator is used as a PlainDate
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "2019-10-01T09:00:00Z",
+ "2019-10-01T09:00:00Z[UTC]",
+];
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.withPlainDate(arg),
+ "String with UTC designator should not be valid as a PlainDate"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-wrong-type.js
new file mode 100644
index 0000000000..8432e76956
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/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.zoneddatetime.prototype.withplaindate
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainDate
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+
+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.withPlainDate(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.PlainDate, "Temporal.PlainDate, object"],
+ [Temporal.PlainDate.prototype, "Temporal.PlainDate.prototype, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.withPlainDate(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/ZonedDateTime/prototype/withPlainDate/argument-zoneddatetime-convert.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-zoneddatetime-convert.js
new file mode 100644
index 0000000000..c4ea148318
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-zoneddatetime-convert.js
@@ -0,0 +1,22 @@
+// |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.zoneddatetime.prototype.withplaindate
+description: An exception from TimeZone#getOffsetNanosecondsFor() is propagated.
+features: [Temporal]
+---*/
+
+class TZ extends Temporal.TimeZone {
+ constructor() { super("UTC") }
+ getOffsetNanosecondsFor() { throw new Test262Error() }
+}
+
+const tz = new TZ();
+const arg = new Temporal.ZonedDateTime(0n, tz);
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+
+assert.throws(Test262Error, () => instance.withPlainDate(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-zoneddatetime-slots.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-zoneddatetime-slots.js
new file mode 100644
index 0000000000..2a50ebb455
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-zoneddatetime-slots.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.zoneddatetime.prototype.withplaindate
+description: Getters are not called when converting a ZonedDateTime to a PlainDate.
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.ZonedDateTime.prototype);
+const getters = ["year", "month", "monthCode", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond", "calendar"];
+
+for (const property of getters) {
+ Object.defineProperty(Temporal.ZonedDateTime.prototype, property, {
+ get() {
+ actual.push(`get ${property}`);
+ const value = prototypeDescrs[property].get.call(this);
+ return {
+ toString() {
+ actual.push(`toString ${property}`);
+ return value.toString();
+ },
+ valueOf() {
+ actual.push(`valueOf ${property}`);
+ return value;
+ },
+ };
+ },
+ });
+}
+
+const arg = new Temporal.ZonedDateTime(0n, "UTC");
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+instance.withPlainDate(arg);
+assert.compareArray(actual, []);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..59f7ea3c1c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/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.zoneddatetime.prototype.withplaindate
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+ const other = new Temporal.ZonedDateTime(1_100_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.withPlainDate(other));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..f3661242e1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/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.zoneddatetime.prototype.withplaindate
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+ const other = new Temporal.ZonedDateTime(1_100_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => datetime.withPlainDate(other),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..5f1207a538
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/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.zoneddatetime.prototype.withplaindate
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+ const other = new Temporal.ZonedDateTime(1_100_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.withPlainDate(other));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..b53e9bc76c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/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.zoneddatetime.prototype.withplaindate
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+ const other = new Temporal.ZonedDateTime(1_100_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.withPlainDate(other));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/branding.js
new file mode 100644
index 0000000000..b6f48f3385
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/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.zoneddatetime.prototype.withplaindate
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const withPlainDate = Temporal.ZonedDateTime.prototype.withPlainDate;
+
+assert.sameValue(typeof withPlainDate, "function");
+
+const args = [new Temporal.PlainDate(2022, 6, 22)];
+
+assert.throws(TypeError, () => withPlainDate.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => withPlainDate.apply(null, args), "null");
+assert.throws(TypeError, () => withPlainDate.apply(true, args), "true");
+assert.throws(TypeError, () => withPlainDate.apply("", args), "empty string");
+assert.throws(TypeError, () => withPlainDate.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => withPlainDate.apply(1, args), "1");
+assert.throws(TypeError, () => withPlainDate.apply({}, args), "plain object");
+assert.throws(TypeError, () => withPlainDate.apply(Temporal.ZonedDateTime, args), "Temporal.ZonedDateTime");
+assert.throws(TypeError, () => withPlainDate.apply(Temporal.ZonedDateTime.prototype, args), "Temporal.ZonedDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..324f89b063
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.zoneddatetime.prototype.withplaindate
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const idOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "id");
+Object.defineProperty(Temporal.Calendar.prototype, "id", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("id should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.withPlainDate(new Temporal.PlainDate(2001, 6, 13));
+
+Object.defineProperty(Temporal.Calendar.prototype, "id", idOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/builtin-timezone-no-observable-calls.js
new file mode 100644
index 0000000000..0d002912dc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/builtin-timezone-no-observable-calls.js
@@ -0,0 +1,37 @@
+// |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.zoneddatetime.prototype.withplaindate
+description: >
+ Calling the method on an instance constructed with a builtin time zone causes
+ no observable lookups or calls to time zone methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+const getPossibleInstantsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getPossibleInstantsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.withPlainDate(new Temporal.PlainDate(2001, 6, 13));
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", getPossibleInstantsForOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/builtin.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/builtin.js
new file mode 100644
index 0000000000..fb6037f777
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/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.zoneddatetime.prototype.withplaindate
+description: >
+ Tests that Temporal.ZonedDateTime.prototype.withPlainDate
+ 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.ZonedDateTime.prototype.withPlainDate),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.withPlainDate),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.withPlainDate),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.ZonedDateTime.prototype.withPlainDate.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/calendar-datefromfields-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/calendar-datefromfields-called-with-options-undefined.js
new file mode 100644
index 0000000000..8c7a033e9b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/calendar-datefromfields-called-with-options-undefined.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.zoneddatetime.prototype.withplaindate
+description: >
+ Calendar.dateFromFields method is called with undefined as the options value
+ when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar);
+instance.withPlainDate({ year: 2000, month: 5, day: 3, calendar });
+assert.sameValue(calendar.dateFromFieldsCallCount, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/calendar-fields-iterable.js
new file mode 100644
index 0000000000..397a0b4f57
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/calendar-fields-iterable.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.zoneddatetime.prototype.withplaindate
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.zoneddatetime.prototype.withplaindate step 3:
+ 3. Let _plainDate_ be ? ToTemporalDate(_plainDateLike_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar1);
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+datetime.withPlainDate({ year: 2001, month: 6, day: 4, calendar: calendar2 });
+
+assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/calendar-temporal-object.js
new file mode 100644
index 0000000000..a3d487d9ae
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/calendar-temporal-object.js
@@ -0,0 +1,31 @@
+// |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.zoneddatetime.prototype.withplaindate
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.zoneddatetime.prototype.withplaindate step 3:
+ 3. Let _plainDate_ be ? ToTemporalDate(_plainDateLike_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject, calendar) => {
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+ // the PlainDate's calendar will override the ZonedDateTime's ISO calendar
+ const result = datetime.withPlainDate({ year: 2001, month: 6, day: 4, calendar: temporalObject });
+ assert.sameValue(result.getCalendar(), calendar, "Temporal object coerced to calendar");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/getpossibleinstantsfor-called-with-iso8601-calendar.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/getpossibleinstantsfor-called-with-iso8601-calendar.js
new file mode 100644
index 0000000000..9776d8ad73
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/getpossibleinstantsfor-called-with-iso8601-calendar.js
@@ -0,0 +1,58 @@
+// |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.zoneddatetime.prototype.withplaindate
+description: >
+ Time zone's getPossibleInstantsFor is called with a PlainDateTime with the
+ built-in ISO 8601 calendar
+features: [Temporal]
+info: |
+ DisambiguatePossibleInstants:
+ 2. Let _n_ be _possibleInstants_'s length.
+ ...
+ 5. Assert: _n_ = 0.
+ ...
+ 19. If _disambiguation_ is *"earlier"*, then
+ ...
+ c. Let _earlierDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*).
+ d. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _earlierDateTime_).
+ ...
+ 20. Assert: _disambiguation_ is *"compatible"* or *"later"*.
+ ...
+ 23. Let _laterDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*).
+ 24. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _laterDateTime_).
+---*/
+
+class SkippedDateTime extends Temporal.TimeZone {
+ constructor() {
+ super("UTC");
+ this.calls = 0;
+ }
+
+ getPossibleInstantsFor(dateTime) {
+ // Calls occur in pairs. For the first one return no possible instants so
+ // that DisambiguatePossibleInstants will call it again
+ if (this.calls++ % 2 == 0) {
+ return [];
+ }
+
+ assert.sameValue(
+ dateTime.getISOFields().calendar,
+ "iso8601",
+ "getPossibleInstantsFor called with dateTime with built-in ISO 8601 calendar"
+ );
+ return super.getPossibleInstantsFor(dateTime);
+ }
+}
+
+const nonBuiltinISOCalendar = new Temporal.Calendar("iso8601");
+const timeZone = new SkippedDateTime();
+
+const instance = new Temporal.ZonedDateTime(0n, timeZone, nonBuiltinISOCalendar);
+instance.withPlainDate(new Temporal.PlainDate(2000, 5, 2, nonBuiltinISOCalendar));
+
+assert.sameValue(timeZone.calls, 2, "getPossibleInstantsFor should have been called 2 times");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..894cd7ff7a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/infinity-throws-rangeerror.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.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.zoneddatetime.prototype.withplaindate
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+const base = { year: 2000, month: 5, day: 2 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.withPlainDate({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.withPlainDate({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/length.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/length.js
new file mode 100644
index 0000000000..8ff6734457
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/length.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.zoneddatetime.prototype.withplaindate
+description: Temporal.ZonedDateTime.prototype.withPlainDate.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.ZonedDateTime.prototype.withPlainDate, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/name.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/name.js
new file mode 100644
index 0000000000..ba8bfba184
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/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.zoneddatetime.prototype.withplaindate
+description: Temporal.ZonedDateTime.prototype.withPlainDate.name is "withPlainDate".
+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.ZonedDateTime.prototype.withPlainDate, "name", {
+ value: "withPlainDate",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/negative-epochnanoseconds.js
new file mode 100644
index 0000000000..e76365257d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/negative-epochnanoseconds.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.zoneddatetime.prototype.withplaindate
+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]
+---*/
+
+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 result = datetime.withPlainDate(new Temporal.PlainDate(2000, 5, 2));
+assert.sameValue(result.epochNanoseconds, 957286235_000_000_001n);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/not-a-constructor.js
new file mode 100644
index 0000000000..9cb6dd4ed6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/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.zoneddatetime.prototype.withplaindate
+description: >
+ Temporal.ZonedDateTime.prototype.withPlainDate 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.ZonedDateTime.prototype.withPlainDate();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.withPlainDate), false,
+ "isConstructor(Temporal.ZonedDateTime.prototype.withPlainDate)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/order-of-operations.js
new file mode 100644
index 0000000000..d26b5e8e1b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/order-of-operations.js
@@ -0,0 +1,114 @@
+// |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-get-temporal.zoneddatetime.prototype.withplaindate
+description: User code calls happen in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const expected = [
+ // ToTemporalDate
+ "get plainDateLike.calendar",
+ "has plainDateLike.calendar.dateAdd",
+ "has plainDateLike.calendar.dateFromFields",
+ "has plainDateLike.calendar.dateUntil",
+ "has plainDateLike.calendar.day",
+ "has plainDateLike.calendar.dayOfWeek",
+ "has plainDateLike.calendar.dayOfYear",
+ "has plainDateLike.calendar.daysInMonth",
+ "has plainDateLike.calendar.daysInWeek",
+ "has plainDateLike.calendar.daysInYear",
+ "has plainDateLike.calendar.fields",
+ "has plainDateLike.calendar.id",
+ "has plainDateLike.calendar.inLeapYear",
+ "has plainDateLike.calendar.mergeFields",
+ "has plainDateLike.calendar.month",
+ "has plainDateLike.calendar.monthCode",
+ "has plainDateLike.calendar.monthDayFromFields",
+ "has plainDateLike.calendar.monthsInYear",
+ "has plainDateLike.calendar.weekOfYear",
+ "has plainDateLike.calendar.year",
+ "has plainDateLike.calendar.yearMonthFromFields",
+ "has plainDateLike.calendar.yearOfWeek",
+ "get plainDateLike.calendar.dateFromFields",
+ "get plainDateLike.calendar.fields",
+ "call plainDateLike.calendar.fields",
+ "get plainDateLike.day",
+ "get plainDateLike.day.valueOf",
+ "call plainDateLike.day.valueOf",
+ "get plainDateLike.month",
+ "get plainDateLike.month.valueOf",
+ "call plainDateLike.month.valueOf",
+ "get plainDateLike.monthCode",
+ "get plainDateLike.monthCode.toString",
+ "call plainDateLike.monthCode.toString",
+ "get plainDateLike.year",
+ "get plainDateLike.year.valueOf",
+ "call plainDateLike.year.valueOf",
+ "call plainDateLike.calendar.dateFromFields",
+ // lookup
+ "get this.timeZone.getOffsetNanosecondsFor",
+ "get this.timeZone.getPossibleInstantsFor",
+ // GetPlainDateTimeFor
+ "call this.timeZone.getOffsetNanosecondsFor",
+ // ConsolidateCalendars
+ "get this.calendar.id",
+ "get plainDateLike.calendar.id",
+ // GetInstantFor
+ "call this.timeZone.getPossibleInstantsFor",
+];
+
+const calendar = TemporalHelpers.calendarObserver(actual, "this.calendar");
+const plainDateCalendar = TemporalHelpers.calendarObserver(actual, "plainDateLike.calendar");
+const dstTimeZone = TemporalHelpers.springForwardFallBackTimeZone();
+const timeZone = TemporalHelpers.timeZoneObserver(actual, "this.timeZone", {
+ getOffsetNanosecondsFor: dstTimeZone.getOffsetNanosecondsFor,
+ getPossibleInstantsFor: dstTimeZone.getPossibleInstantsFor,
+});
+
+const instance = new Temporal.ZonedDateTime(37800_000_000_000n /* 1970-01-01T02:30-08:00 */, timeZone, calendar);
+const fallBackInstance = new Temporal.ZonedDateTime(34200_000_000_000n /* 1970-01-01T01:30-08:00 */, timeZone, calendar);
+actual.splice(0); // clear calls that happened in constructor
+
+const plainDateLike = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2000,
+ month: 1,
+ monthCode: "M01",
+ day: 1,
+ calendar: plainDateCalendar,
+}, "plainDateLike");
+instance.withPlainDate(plainDateLike);
+assert.compareArray(actual, expected, "order of operations at normal wall-clock time");
+actual.splice(0); // clear
+
+const fallBackPlainDateLike = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2000,
+ month: 10,
+ monthCode: "M10",
+ day: 29,
+ calendar: plainDateCalendar,
+}, "plainDateLike");
+fallBackInstance.withPlainDate(fallBackPlainDateLike);
+assert.compareArray(actual, expected, "order of operations at repeated wall-clock time");
+actual.splice(0); // clear
+
+const springForwardPlainDateLike = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2000,
+ month: 4,
+ monthCode: "M04",
+ day: 2,
+ calendar: plainDateCalendar,
+}, "plainDateLike");
+instance.withPlainDate(springForwardPlainDateLike);
+assert.compareArray(actual, expected.concat([
+ "call this.timeZone.getOffsetNanosecondsFor",
+ "call this.timeZone.getOffsetNanosecondsFor",
+ "call this.timeZone.getPossibleInstantsFor",
+]), "order of operations at skipped wall-clock time");
+actual.splice(0); // clear
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/prop-desc.js
new file mode 100644
index 0000000000..c41fbee16b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/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.zoneddatetime.prototype.withplaindate
+description: The "withPlainDate" property of Temporal.ZonedDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.ZonedDateTime.prototype.withPlainDate,
+ "function",
+ "`typeof ZonedDateTime.prototype.withPlainDate` is `function`"
+);
+
+verifyProperty(Temporal.ZonedDateTime.prototype, "withPlainDate", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/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/ZonedDateTime/prototype/withPlainDate/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/subclassing-ignored.js
new file mode 100644
index 0000000000..311c25b2f5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/subclassing-ignored.js
@@ -0,0 +1,31 @@
+// |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.zoneddatetime.prototype.withplaindate
+description: Objects of a subclass are never created as return values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.ZonedDateTime,
+ [10n, "UTC"],
+ "withPlainDate",
+ ["2000-01-01"],
+ (result) => {
+ assert.sameValue(result.epochNanoseconds, 946684800_000_000_010n, "epochNanoseconds result");
+ assert.sameValue(result.year, 2000, "year result");
+ assert.sameValue(result.month, 1, "month result");
+ assert.sameValue(result.day, 1, "day result");
+ assert.sameValue(result.hour, 0, "hour result");
+ assert.sameValue(result.minute, 0, "minute result");
+ assert.sameValue(result.second, 0, "second result");
+ assert.sameValue(result.millisecond, 0, "millisecond result");
+ assert.sameValue(result.microsecond, 0, "microsecond result");
+ assert.sameValue(result.nanosecond, 10, "nanosecond result");
+ },
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..7d65f11f94
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/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.zoneddatetime.prototype.withplaindate
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ assert.throws(RangeError, () => datetime.withPlainDate(date));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..b33ef6da20
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/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.zoneddatetime.prototype.withplaindate
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => datetime.withPlainDate(date),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..541796174f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/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.zoneddatetime.prototype.withplaindate
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ assert.throws(RangeError, () => datetime.withPlainDate(date));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..15e28fdf51
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/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.zoneddatetime.prototype.withplaindate
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ assert.throws(TypeError, () => datetime.withPlainDate(date));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/year-zero.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/year-zero.js
new file mode 100644
index 0000000000..933590f526
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/year-zero.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.zoneddatetime.prototype.withplaindate
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T00:45",
+ "-000000-10-31T00:45+01:00",
+ "-000000-10-31T00:45+00:00[UTC]",
+];
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.withPlainDate(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-number.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-number.js
new file mode 100644
index 0000000000..3c363713a3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/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.zoneddatetime.prototype.withplaintime
+description: A number is invalid in place of an ISO string for Temporal.PlainTime
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+
+const numbers = [
+ 1,
+ -123456.987654321,
+ 1234567,
+ 123456.9876543219,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => instance.withPlainTime(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/ZonedDateTime/prototype/withPlainTime/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..a052bf77e8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/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.zoneddatetime.prototype.withplaintime
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+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 timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.withPlainTime(arg);
+
+ assert.sameValue(
+ result.epochNanoseconds,
+ 45_296_987_654_321n,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..231cb49b0b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-critical-unknown-annotation.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.zoneddatetime.prototype.withplaintime
+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 timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.withPlainTime(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..b59e9c1079
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/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.zoneddatetime.prototype.withplaintime
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+
+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.withPlainTime(arg);
+
+ assert.sameValue(
+ result.epochNanoseconds,
+ 45_296_987_654_321n,
+ `"${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.withPlainTime(arg),
+ `"${arg}" UTC offset without time is not valid for PlainTime`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..7997635a90
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-multiple-calendar.js
@@ -0,0 +1,33 @@
+// |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.zoneddatetime.prototype.withplaintime
+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 timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.withPlainTime(arg),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..731ce895a2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-multiple-time-zone.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.zoneddatetime.prototype.withplaintime
+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 timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.withPlainTime(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-no-implicit-midnight.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-no-implicit-midnight.js
new file mode 100644
index 0000000000..4f2f77be0a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/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.zoneddatetime.prototype.withplaintime
+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.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+assert.throws(
+ RangeError,
+ () => instance.withPlainTime(arg),
+ "Date-only string throws, does not implicitly convert to midnight"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-time-designator-required-for-disambiguation.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-time-designator-required-for-disambiguation.js
new file mode 100644
index 0000000000..51e897a340
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/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.zoneddatetime.prototype.withplaintime
+description: ISO 8601 time designator "T" required in cases of ambiguity
+includes: [temporalHelpers.js]
+features: [Temporal, arrow-function]
+---*/
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+
+TemporalHelpers.ISO.plainTimeStringsAmbiguous().forEach((string) => {
+ let arg = string;
+ assert.throws(
+ RangeError,
+ () => instance.withPlainTime(arg),
+ `'${arg}' is ambiguous and requires T prefix`
+ );
+ // The same string with a T prefix should not throw:
+ arg = `T${string}`;
+ instance.withPlainTime(arg);
+
+ arg = ` ${string}`;
+ assert.throws(
+ RangeError,
+ () => instance.withPlainTime(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.withPlainTime(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-time-separators.js
new file mode 100644
index 0000000000..4698f39f6c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/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.zoneddatetime.prototype.withplaintime
+description: Time separator in string argument can vary
+features: [Temporal]
+---*/
+
+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 timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.withPlainTime(arg);
+
+ assert.sameValue(
+ result.epochNanoseconds,
+ 45_296_987_654_321n,
+ `variant time separators (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..b861c603cd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/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.zoneddatetime.prototype.withplaintime
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+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 timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.withPlainTime(arg);
+
+ assert.sameValue(
+ result.epochNanoseconds,
+ 45_296_987_654_321n,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..7870e582ff
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/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.zoneddatetime.prototype.withplaintime
+description: Various forms of unknown annotation
+features: [Temporal]
+---*/
+
+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 timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.withPlainTime(arg);
+
+ assert.sameValue(
+ result.epochNanoseconds,
+ 45_296_987_654_321n,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-with-time-designator.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-with-time-designator.js
new file mode 100644
index 0000000000..5158ef1810
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-with-time-designator.js
@@ -0,0 +1,31 @@
+// |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.zoneddatetime.prototype.withplaintime
+description: ISO 8601 time designator "T" allowed at the start of PlainTime strings
+features: [Temporal, arrow-function]
+---*/
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+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.withPlainTime(arg);
+ assert.sameValue(result.epochNanoseconds, 999995400_000_000_000n, `T prefix is accepted: ${arg}`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..5421bdb7bd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/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.zoneddatetime.prototype.withplaintime
+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.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.withPlainTime(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/ZonedDateTime/prototype/withPlainTime/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-wrong-type.js
new file mode 100644
index 0000000000..9ee14e55fb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-wrong-type.js
@@ -0,0 +1,42 @@
+// |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.zoneddatetime.prototype.withplaintime
+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.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+
+const primitiveTests = [
+ [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.withPlainTime(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.withPlainTime(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/ZonedDateTime/prototype/withPlainTime/argument-zoneddatetime-balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-zoneddatetime-balance-negative-time-units.js
new file mode 100644
index 0000000000..36c14236b5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-zoneddatetime-balance-negative-time-units.js
@@ -0,0 +1,49 @@
+// |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.zoneddatetime.prototype.withplaintime
+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.zoneddatetime.prototype.withplaintime step 4.a:
+ a. Let _plainTime_ be ? ToTemporalTime(_plainTimeLike_).
+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 otherTimeZone = new Temporal.TimeZone("UTC"); // should not be used to convert datetime -> PlainTime
+const zdt = new Temporal.ZonedDateTime(86400_000_000_000n, otherTimeZone);
+const newzdt = zdt.withPlainTime(datetime);
+
+assert.sameValue(newzdt.microsecond, 0);
+assert.sameValue(newzdt.nanosecond, 999);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-zoneddatetime-negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-zoneddatetime-negative-epochnanoseconds.js
new file mode 100644
index 0000000000..2bdf59aa9c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-zoneddatetime-negative-epochnanoseconds.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.zoneddatetime.prototype.withplaintime
+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]
+---*/
+
+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.ZonedDateTime(0n, "UTC");
+const result = instance.withPlainTime(datetime);
+assert.sameValue(result.epochNanoseconds, 60635_000_000_001n);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..d1064ae7ab
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/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.zoneddatetime.prototype.withplaintime
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+ const other = new Temporal.ZonedDateTime(1_100_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.withPlainTime(other));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..65dc46f85f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/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.zoneddatetime.prototype.withplaintime
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+ const other = new Temporal.ZonedDateTime(1_100_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => datetime.withPlainTime(other),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..3baca11c86
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/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.zoneddatetime.prototype.withplaintime
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+ const other = new Temporal.ZonedDateTime(1_100_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.withPlainTime(other));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..f34255f8cd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/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.zoneddatetime.prototype.withplaintime
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+ const other = new Temporal.ZonedDateTime(1_100_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.withPlainTime(other));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/branding.js
new file mode 100644
index 0000000000..644b98458e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/branding.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.zoneddatetime.prototype.withplaintime
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const withPlainTime = Temporal.ZonedDateTime.prototype.withPlainTime;
+
+assert.sameValue(typeof withPlainTime, "function");
+
+assert.throws(TypeError, () => withPlainTime.call(undefined), "undefined");
+assert.throws(TypeError, () => withPlainTime.call(null), "null");
+assert.throws(TypeError, () => withPlainTime.call(true), "true");
+assert.throws(TypeError, () => withPlainTime.call(""), "empty string");
+assert.throws(TypeError, () => withPlainTime.call(Symbol()), "symbol");
+assert.throws(TypeError, () => withPlainTime.call(1), "1");
+assert.throws(TypeError, () => withPlainTime.call({}), "plain object");
+assert.throws(TypeError, () => withPlainTime.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime");
+assert.throws(TypeError, () => withPlainTime.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/builtin-timezone-no-observable-calls.js
new file mode 100644
index 0000000000..e4282fd17c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/builtin-timezone-no-observable-calls.js
@@ -0,0 +1,37 @@
+// |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.zoneddatetime.prototype.withplaintime
+description: >
+ Calling the method on an instance constructed with a builtin time zone causes
+ no observable lookups or calls to time zone methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+const getPossibleInstantsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getPossibleInstantsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.withPlainTime(new Temporal.PlainTime(12, 34, 56));
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", getPossibleInstantsForOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/builtin.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/builtin.js
new file mode 100644
index 0000000000..c44365ab26
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/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.zoneddatetime.prototype.withplaintime
+description: >
+ Tests that Temporal.ZonedDateTime.prototype.withPlainTime
+ 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.ZonedDateTime.prototype.withPlainTime),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.withPlainTime),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.withPlainTime),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.ZonedDateTime.prototype.withPlainTime.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/getpossibleinstantsfor-called-with-iso8601-calendar.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/getpossibleinstantsfor-called-with-iso8601-calendar.js
new file mode 100644
index 0000000000..890bef4f0c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/getpossibleinstantsfor-called-with-iso8601-calendar.js
@@ -0,0 +1,58 @@
+// |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.zoneddatetime.prototype.withplaintime
+description: >
+ Time zone's getPossibleInstantsFor is called with a PlainDateTime with the
+ built-in ISO 8601 calendar
+features: [Temporal]
+info: |
+ DisambiguatePossibleInstants:
+ 2. Let _n_ be _possibleInstants_'s length.
+ ...
+ 5. Assert: _n_ = 0.
+ ...
+ 19. If _disambiguation_ is *"earlier"*, then
+ ...
+ c. Let _earlierDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*).
+ d. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _earlierDateTime_).
+ ...
+ 20. Assert: _disambiguation_ is *"compatible"* or *"later"*.
+ ...
+ 23. Let _laterDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*).
+ 24. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _laterDateTime_).
+---*/
+
+class SkippedDateTime extends Temporal.TimeZone {
+ constructor() {
+ super("UTC");
+ this.calls = 0;
+ }
+
+ getPossibleInstantsFor(dateTime) {
+ // Calls occur in pairs. For the first one return no possible instants so
+ // that DisambiguatePossibleInstants will call it again
+ if (this.calls++ % 2 == 0) {
+ return [];
+ }
+
+ assert.sameValue(
+ dateTime.getISOFields().calendar,
+ "iso8601",
+ "getPossibleInstantsFor called with dateTime with built-in ISO 8601 calendar"
+ );
+ return super.getPossibleInstantsFor(dateTime);
+ }
+}
+
+const nonBuiltinISOCalendar = new Temporal.Calendar("iso8601");
+const timeZone = new SkippedDateTime();
+
+const instance = new Temporal.ZonedDateTime(0n, timeZone, nonBuiltinISOCalendar);
+instance.withPlainTime();
+
+assert.sameValue(timeZone.calls, 2, "getPossibleInstantsFor should have been called 2 times");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/leap-second.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/leap-second.js
new file mode 100644
index 0000000000..79685eb7ca
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/leap-second.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.zoneddatetime.prototype.withplaintime
+description: Leap second is a valid ISO string for PlainTime
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+
+let arg = "2016-12-31T23:59:60";
+const result1 = instance.withPlainTime(arg);
+assert.sameValue(
+ result1.epochNanoseconds,
+ 1000079999_000_000_000n,
+ "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.withPlainTime(arg);
+assert.sameValue(
+ result2.epochNanoseconds,
+ 1000079999_000_000_000n,
+ "second: 60 is ignored in property bag for PlainTime"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/length.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/length.js
new file mode 100644
index 0000000000..5e419dd76a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/length.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.zoneddatetime.prototype.withplaintime
+description: Temporal.ZonedDateTime.prototype.withPlainTime.length is 0
+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.ZonedDateTime.prototype.withPlainTime, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/name.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/name.js
new file mode 100644
index 0000000000..b9294ef7dd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/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.zoneddatetime.prototype.withplaintime
+description: Temporal.ZonedDateTime.prototype.withPlainTime.name is "withPlainTime".
+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.ZonedDateTime.prototype.withPlainTime, "name", {
+ value: "withPlainTime",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/not-a-constructor.js
new file mode 100644
index 0000000000..9e0d86e8aa
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/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.zoneddatetime.prototype.withplaintime
+description: >
+ Temporal.ZonedDateTime.prototype.withPlainTime 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.ZonedDateTime.prototype.withPlainTime();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.withPlainTime), false,
+ "isConstructor(Temporal.ZonedDateTime.prototype.withPlainTime)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/order-of-operations.js
new file mode 100644
index 0000000000..420678ddfd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/order-of-operations.js
@@ -0,0 +1,88 @@
+// |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-get-temporal.zoneddatetime.prototype.withplaintime
+description: User code calls happen in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const expected = [
+ // ToTemporalTime
+ "get plainTimeLike.hour",
+ "get plainTimeLike.hour.valueOf",
+ "call plainTimeLike.hour.valueOf",
+ "get plainTimeLike.microsecond",
+ "get plainTimeLike.microsecond.valueOf",
+ "call plainTimeLike.microsecond.valueOf",
+ "get plainTimeLike.millisecond",
+ "get plainTimeLike.millisecond.valueOf",
+ "call plainTimeLike.millisecond.valueOf",
+ "get plainTimeLike.minute",
+ "get plainTimeLike.minute.valueOf",
+ "call plainTimeLike.minute.valueOf",
+ "get plainTimeLike.nanosecond",
+ "get plainTimeLike.nanosecond.valueOf",
+ "call plainTimeLike.nanosecond.valueOf",
+ "get plainTimeLike.second",
+ "get plainTimeLike.second.valueOf",
+ "call plainTimeLike.second.valueOf",
+ // lookup
+ "get this.timeZone.getOffsetNanosecondsFor",
+ "get this.timeZone.getPossibleInstantsFor",
+ // GetPlainDateTimeFor
+ "call this.timeZone.getOffsetNanosecondsFor",
+ // GetInstantFor
+ "call this.timeZone.getPossibleInstantsFor",
+];
+
+const calendar = TemporalHelpers.calendarObserver(actual, "this.calendar");
+const dstTimeZone = TemporalHelpers.springForwardFallBackTimeZone();
+const timeZone = TemporalHelpers.timeZoneObserver(actual, "this.timeZone", {
+ getOffsetNanosecondsFor: dstTimeZone.getOffsetNanosecondsFor,
+ getPossibleInstantsFor: dstTimeZone.getPossibleInstantsFor,
+});
+
+const instance = new Temporal.ZonedDateTime(946713600_000_000_000n /* 2000-01-01T00:00-08:00 */, timeZone, calendar);
+const fallBackInstance = new Temporal.ZonedDateTime(972802800_000_000_000n /* 2000-10-29T00:00-07:00 */, timeZone, calendar);
+const springForwardInstance = new Temporal.ZonedDateTime(954662400_000_000_000n /* 2000-04-02T00:00-08:00 */, timeZone, calendar);
+actual.splice(0); // clear calls that happened in constructors
+
+const plainTimeLike = TemporalHelpers.propertyBagObserver(actual, {
+ hour: 2,
+ minute: 30,
+ second: 0,
+ millisecond: 0,
+ microsecond: 0,
+ nanosecond: 0,
+}, "plainTimeLike");
+
+instance.withPlainTime(plainTimeLike);
+assert.compareArray(actual, expected, "order of operations at normal wall-clock time");
+actual.splice(0); // clear
+
+const plainTimeLike130 = TemporalHelpers.propertyBagObserver(actual, {
+ hour: 1,
+ minute: 30,
+ second: 0,
+ millisecond: 0,
+ microsecond: 0,
+ nanosecond: 0,
+}, "plainTimeLike");
+
+fallBackInstance.withPlainTime(plainTimeLike130);
+assert.compareArray(actual, expected, "order of operations at repeated wall-clock time");
+actual.splice(0); // clear
+
+springForwardInstance.withPlainTime(plainTimeLike);
+assert.compareArray(actual, expected.concat([
+ "call this.timeZone.getOffsetNanosecondsFor",
+ "call this.timeZone.getOffsetNanosecondsFor",
+ "call this.timeZone.getPossibleInstantsFor",
+]), "order of operations at skipped wall-clock time");
+actual.splice(0); // clear
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/plaintime-propertybag-no-time-units.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/plaintime-propertybag-no-time-units.js
new file mode 100644
index 0000000000..62a6c07697
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/plaintime-propertybag-no-time-units.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.zoneddatetime.prototype.withplaintime
+description: Missing time units in property bag default to 0
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+
+const props = {};
+assert.throws(TypeError, () => instance.withPlainTime(props), "TypeError if no properties are present");
+
+props.minute = 30;
+const result = instance.withPlainTime(props);
+assert.sameValue(result.epochNanoseconds, 999995400_000_000_000n, "missing time units default to 0");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/prop-desc.js
new file mode 100644
index 0000000000..0df6a75f9f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/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.zoneddatetime.prototype.withplaintime
+description: The "withPlainTime" property of Temporal.ZonedDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.ZonedDateTime.prototype.withPlainTime,
+ "function",
+ "`typeof ZonedDateTime.prototype.withPlainTime` is `function`"
+);
+
+verifyProperty(Temporal.ZonedDateTime.prototype, "withPlainTime", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/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/ZonedDateTime/prototype/withPlainTime/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/subclassing-ignored.js
new file mode 100644
index 0000000000..70ad2f2ea8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/subclassing-ignored.js
@@ -0,0 +1,31 @@
+// |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.zoneddatetime.prototype.withplaintime
+description: Objects of a subclass are never created as return values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.ZonedDateTime,
+ [10n, "UTC"],
+ "withPlainTime",
+ ["05:43:21.123456789"],
+ (result) => {
+ assert.sameValue(result.epochNanoseconds, 20601_123_456_789n, "epochNanoseconds result");
+ assert.sameValue(result.year, 1970, "year result");
+ assert.sameValue(result.month, 1, "month result");
+ assert.sameValue(result.day, 1, "day result");
+ assert.sameValue(result.hour, 5, "hour result");
+ assert.sameValue(result.minute, 43, "minute result");
+ assert.sameValue(result.second, 21, "second result");
+ assert.sameValue(result.millisecond, 123, "millisecond result");
+ assert.sameValue(result.microsecond, 456, "microsecond result");
+ assert.sameValue(result.nanosecond, 789, "nanosecond result");
+ },
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/time-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/time-undefined.js
new file mode 100644
index 0000000000..fb56367443
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/time-undefined.js
@@ -0,0 +1,29 @@
+// |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.zoneddatetime.prototype.withplaintime
+description: The time is assumed to be midnight if not given
+features: [BigInt, Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(957270896_987_654_321n, "UTC");
+
+const explicit = datetime.withPlainTime(undefined);
+assert.sameValue(explicit.hour, 0, "default time is midnight");
+assert.sameValue(explicit.minute, 0, "default time is midnight");
+assert.sameValue(explicit.second, 0, "default time is midnight");
+assert.sameValue(explicit.millisecond, 0, "default time is midnight");
+assert.sameValue(explicit.microsecond, 0, "default time is midnight");
+assert.sameValue(explicit.nanosecond, 0, "default time is midnight");
+
+const implicit = datetime.withPlainTime();
+assert.sameValue(implicit.hour, 0, "default time is midnight");
+assert.sameValue(implicit.minute, 0, "default time is midnight");
+assert.sameValue(implicit.second, 0, "default time is midnight");
+assert.sameValue(implicit.millisecond, 0, "default time is midnight");
+assert.sameValue(implicit.microsecond, 0, "default time is midnight");
+assert.sameValue(implicit.nanosecond, 0, "default time is midnight");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..abc5aab8a1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/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.zoneddatetime.prototype.withplaintime
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+ assert.throws(RangeError, () => datetime.withPlainTime(time));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..a2a089cc9c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/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.zoneddatetime.prototype.withplaintime
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => datetime.withPlainTime(time),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..a08ece63c0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/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.zoneddatetime.prototype.withplaintime
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+ assert.throws(RangeError, () => datetime.withPlainTime(time));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..81a053e2cd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/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.zoneddatetime.prototype.withplaintime
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+ assert.throws(TypeError, () => datetime.withPlainTime(time));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/year-zero.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/year-zero.js
new file mode 100644
index 0000000000..e092ae571b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/year-zero.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.zoneddatetime.prototype.withplaintime
+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 timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.ZonedDateTime(0n, timeZone);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.withPlainTime(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/branding.js
new file mode 100644
index 0000000000..957ee3d3d1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/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.zoneddatetime.prototype.withtimezone
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const withTimeZone = Temporal.ZonedDateTime.prototype.withTimeZone;
+
+assert.sameValue(typeof withTimeZone, "function");
+
+const args = [new Temporal.TimeZone("UTC")];
+
+assert.throws(TypeError, () => withTimeZone.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => withTimeZone.apply(null, args), "null");
+assert.throws(TypeError, () => withTimeZone.apply(true, args), "true");
+assert.throws(TypeError, () => withTimeZone.apply("", args), "empty string");
+assert.throws(TypeError, () => withTimeZone.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => withTimeZone.apply(1, args), "1");
+assert.throws(TypeError, () => withTimeZone.apply({}, args), "plain object");
+assert.throws(TypeError, () => withTimeZone.apply(Temporal.ZonedDateTime, args), "Temporal.ZonedDateTime");
+assert.throws(TypeError, () => withTimeZone.apply(Temporal.ZonedDateTime.prototype, args), "Temporal.ZonedDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/builtin-timezone-no-observable-calls.js
new file mode 100644
index 0000000000..c3fe4d8706
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/builtin-timezone-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.zoneddatetime.prototype.withtimezone
+description: >
+ Calling the method on an instance constructed with a builtin time zone causes
+ no observable lookups or calls to time zone methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const idOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "id");
+Object.defineProperty(Temporal.TimeZone.prototype, "id", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("id should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.withTimeZone("+01:00");
+
+Object.defineProperty(Temporal.TimeZone.prototype, "id", idOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/builtin.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/builtin.js
new file mode 100644
index 0000000000..3f2dd9fb56
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/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.zoneddatetime.prototype.withtimezone
+description: >
+ Tests that Temporal.ZonedDateTime.prototype.withTimeZone
+ 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.ZonedDateTime.prototype.withTimeZone),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.withTimeZone),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.withTimeZone),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.ZonedDateTime.prototype.withTimeZone.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/length.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/length.js
new file mode 100644
index 0000000000..fe8047a98a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/length.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.zoneddatetime.prototype.withtimezone
+description: Temporal.ZonedDateTime.prototype.withTimeZone.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.ZonedDateTime.prototype.withTimeZone, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/name.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/name.js
new file mode 100644
index 0000000000..7b5a2f228c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/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.zoneddatetime.prototype.withtimezone
+description: Temporal.ZonedDateTime.prototype.withTimeZone.name is "withTimeZone".
+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.ZonedDateTime.prototype.withTimeZone, "name", {
+ value: "withTimeZone",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/not-a-constructor.js
new file mode 100644
index 0000000000..6e98f2b5a0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/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.zoneddatetime.prototype.withtimezone
+description: >
+ Temporal.ZonedDateTime.prototype.withTimeZone 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.ZonedDateTime.prototype.withTimeZone();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.withTimeZone), false,
+ "isConstructor(Temporal.ZonedDateTime.prototype.withTimeZone)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/prop-desc.js
new file mode 100644
index 0000000000..74d4dce98a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/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.zoneddatetime.prototype.withtimezone
+description: The "withTimeZone" property of Temporal.ZonedDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.ZonedDateTime.prototype.withTimeZone,
+ "function",
+ "`typeof ZonedDateTime.prototype.withTimeZone` is `function`"
+);
+
+verifyProperty(Temporal.ZonedDateTime.prototype, "withTimeZone", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/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/ZonedDateTime/prototype/withTimeZone/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/subclassing-ignored.js
new file mode 100644
index 0000000000..eb405c94a4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/subclassing-ignored.js
@@ -0,0 +1,31 @@
+// |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.zoneddatetime.prototype.withtimezone
+description: Objects of a subclass are never created as return values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.ZonedDateTime,
+ [10n, "UTC"],
+ "withTimeZone",
+ ["+01:00"],
+ (result) => {
+ assert.sameValue(result.epochNanoseconds, 10n, "epochNanoseconds result");
+ assert.sameValue(result.year, 1970, "year result");
+ assert.sameValue(result.month, 1, "month result");
+ assert.sameValue(result.day, 1, "day result");
+ assert.sameValue(result.hour, 1, "hour result");
+ assert.sameValue(result.minute, 0, "minute result");
+ assert.sameValue(result.second, 0, "second result");
+ assert.sameValue(result.millisecond, 0, "millisecond result");
+ assert.sameValue(result.microsecond, 0, "microsecond result");
+ assert.sameValue(result.nanosecond, 10, "nanosecond result");
+ },
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/timezone-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/timezone-case-insensitive.js
new file mode 100644
index 0000000000..e547deb149
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/timezone-case-insensitive.js
@@ -0,0 +1,17 @@
+// |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.zoneddatetime.prototype.withtimezone
+description: Time zone names are case insensitive
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(0n, "UTC");
+
+const timeZone = 'uTc';
+const result = instance.withTimeZone(timeZone);
+assert.sameValue(result.timeZoneId, 'UTC', `Time zone created from string "${timeZone}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/timezone-string-datetime.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/timezone-string-datetime.js
new file mode 100644
index 0000000000..0aa4bf71f9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/timezone-string-datetime.js
@@ -0,0 +1,65 @@
+// |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.zoneddatetime.prototype.withtimezone
+description: Conversion of ISO date-time strings to Temporal.TimeZone instances
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(0n, "UTC");
+
+let timeZone = "2021-08-19T17:30";
+assert.throws(RangeError, () => instance.withTimeZone(timeZone), "bare date-time string is not a time zone");
+
+[
+ "2021-08-19T17:30-07:00:01",
+ "2021-08-19T17:30-07:00:00",
+ "2021-08-19T17:30-07:00:00.1",
+ "2021-08-19T17:30-07:00:00.0",
+ "2021-08-19T17:30-07:00:00.01",
+ "2021-08-19T17:30-07:00:00.00",
+ "2021-08-19T17:30-07:00:00.001",
+ "2021-08-19T17:30-07:00:00.000",
+ "2021-08-19T17:30-07:00:00.0001",
+ "2021-08-19T17:30-07:00:00.0000",
+ "2021-08-19T17:30-07:00:00.00001",
+ "2021-08-19T17:30-07:00:00.00000",
+ "2021-08-19T17:30-07:00:00.000001",
+ "2021-08-19T17:30-07:00:00.000000",
+ "2021-08-19T17:30-07:00:00.0000001",
+ "2021-08-19T17:30-07:00:00.0000000",
+ "2021-08-19T17:30-07:00:00.00000001",
+ "2021-08-19T17:30-07:00:00.00000000",
+ "2021-08-19T17:30-07:00:00.000000001",
+ "2021-08-19T17:30-07:00:00.000000000",
+].forEach((timeZone) => {
+ assert.throws(
+ RangeError,
+ () => instance.withTimeZone(timeZone),
+ `ISO string ${timeZone} with a sub-minute offset is not a valid time zone`
+ );
+});
+
+timeZone = "2021-08-19T17:30Z";
+const result1 = instance.withTimeZone(timeZone);
+assert.sameValue(result1.timeZoneId, "UTC", "date-time + Z is UTC time zone");
+
+timeZone = "2021-08-19T17:30-07:00";
+const result2 = instance.withTimeZone(timeZone);
+assert.sameValue(result2.timeZoneId, "-07:00", "date-time + offset is the offset time zone");
+
+timeZone = "2021-08-19T17:30[UTC]";
+const result3 = instance.withTimeZone(timeZone);
+assert.sameValue(result3.timeZoneId, "UTC", "date-time + IANA annotation is the IANA time zone");
+
+timeZone = "2021-08-19T17:30Z[UTC]";
+const result4 = instance.withTimeZone(timeZone);
+assert.sameValue(result4.timeZoneId, "UTC", "date-time + Z + IANA annotation is the IANA time zone");
+
+timeZone = "2021-08-19T17:30-07:00[UTC]";
+const result5 = instance.withTimeZone(timeZone);
+assert.sameValue(result5.timeZoneId, "UTC", "date-time + offset + IANA annotation is the IANA time zone");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/timezone-string-leap-second.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/timezone-string-leap-second.js
new file mode 100644
index 0000000000..9967c91a9c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/timezone-string-leap-second.js
@@ -0,0 +1,20 @@
+// |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.zoneddatetime.prototype.withtimezone
+description: Leap second is a valid ISO string for TimeZone
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(0n, "UTC");
+let timeZone = "2016-12-31T23:59:60+00:00[UTC]";
+
+const result = instance.withTimeZone(timeZone);
+assert.sameValue(result.timeZoneId, "UTC", "leap second is a valid ISO string for TimeZone");
+
+timeZone = "2021-08-19T17:30:45.123456789+23:59[+23:59:60]";
+assert.throws(RangeError, () => instance.withTimeZone(timeZone), "leap second in time zone name not valid");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/timezone-string-multiple-offsets.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/timezone-string-multiple-offsets.js
new file mode 100644
index 0000000000..428fd5462d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/timezone-string-multiple-offsets.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.zoneddatetime.prototype.withtimezone
+description: Time zone parsing from ISO strings uses the bracketed offset, not the ISO string offset
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(0n, "UTC");
+const timeZone = "2021-08-19T17:30:45.123456789-12:12[+01:46]";
+
+const result = instance.withTimeZone(timeZone);
+assert.sameValue(result.timeZoneId, "+01:46", "Time zone string determined from bracket name");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/timezone-string-year-zero.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/timezone-string-year-zero.js
new file mode 100644
index 0000000000..e9812515ab
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/timezone-string-year-zero.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.zoneddatetime.prototype.withtimezone
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+const instance = new Temporal.ZonedDateTime(0n, new Temporal.TimeZone("UTC"));
+invalidStrings.forEach((timeZone) => {
+ assert.throws(
+ RangeError,
+ () => instance.withTimeZone(timeZone),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/timezone-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/timezone-string.js
new file mode 100644
index 0000000000..31e77b645d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/timezone-string.js
@@ -0,0 +1,39 @@
+// |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.zoneddatetime.prototype.withtimezone
+description: Time zone IDs are valid input for a time zone
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getPossibleInstantsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getPossibleInstantsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be looked up");
+ },
+});
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(0n, "UTC");
+
+["UTC", "+01:30"].forEach((timeZone) => {
+ const result = instance.withTimeZone(timeZone);
+ assert.sameValue(result.getISOFields().timeZone, timeZone, `time zone slot should store string "${timeZone}"`);
+});
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", getPossibleInstantsForOriginal);
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/timezone-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/timezone-wrong-type.js
new file mode 100644
index 0000000000..3937d18bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/timezone-wrong-type.js
@@ -0,0 +1,42 @@
+// |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.zoneddatetime.prototype.withtimezone
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or object for TimeZone
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(0n, new Temporal.TimeZone("UTC"));
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [19761118, "number that would convert to a valid ISO string in other contexts"],
+ [1n, "bigint"],
+];
+
+for (const [timeZone, description] of primitiveTests) {
+ assert.throws(
+ typeof timeZone === 'string' ? RangeError : TypeError,
+ () => instance.withTimeZone(timeZone),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "object not implementing time zone protocol"],
+ [new Temporal.Calendar("iso8601"), "calendar instance"],
+];
+
+for (const [timeZone, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.withTimeZone(timeZone), `${description} is not a valid object and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/branding.js
new file mode 100644
index 0000000000..a825d9c545
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/branding.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-get-temporal.zoneddatetime.prototype.year
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const year = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "year").get;
+
+assert.sameValue(typeof year, "function");
+
+assert.throws(TypeError, () => year.call(undefined), "undefined");
+assert.throws(TypeError, () => year.call(null), "null");
+assert.throws(TypeError, () => year.call(true), "true");
+assert.throws(TypeError, () => year.call(""), "empty string");
+assert.throws(TypeError, () => year.call(Symbol()), "symbol");
+assert.throws(TypeError, () => year.call(1), "1");
+assert.throws(TypeError, () => year.call({}), "plain object");
+assert.throws(TypeError, () => year.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime");
+assert.throws(TypeError, () => year.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..3e356f42b0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.zoneddatetime.prototype.year
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const yearOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "year");
+Object.defineProperty(Temporal.Calendar.prototype, "year", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("year should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.year;
+
+Object.defineProperty(Temporal.Calendar.prototype, "year", yearOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/builtin-timezone-no-observable-calls.js
new file mode 100644
index 0000000000..ed29c0748f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/builtin-timezone-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.zoneddatetime.prototype.year
+description: >
+ Calling the method on an instance constructed with a builtin time zone causes
+ no observable lookups or calls to time zone methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.year;
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/custom.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/custom.js
new file mode 100644
index 0000000000..127e75615b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/custom.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-get-temporal.zoneddatetime.prototype.year
+description: Custom calendar tests for year().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ year(...args) {
+ ++calls;
+ assert.compareArray(args.map(String), [instance].map((arg) => arg.toPlainDateTime().toString()), "year arguments");
+ return 7;
+ }
+}
+
+const calendar = new CustomCalendar();
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar);
+const result = instance.year;
+assert.sameValue(result, 7, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/prop-desc.js
new file mode 100644
index 0000000000..b71f040c7c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/prop-desc.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-get-temporal.zoneddatetime.prototype.year
+description: The "year" property of Temporal.ZonedDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "year");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..ee90ba37fd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,18 @@
+// |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-get-temporal.zoneddatetime.prototype.year
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.year);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..0140df1601
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/timezone-getoffsetnanosecondsfor-not-callable.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-get-temporal.zoneddatetime.prototype.year
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => datetime.year,
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..afe8f737b6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,18 @@
+// |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-get-temporal.zoneddatetime.prototype.year
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.year);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..22eff03c22
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/timezone-getoffsetnanosecondsfor-wrong-type.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-get-temporal.zoneddatetime.prototype.year
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.year);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/validate-calendar-value.js
new file mode 100644
index 0000000000..10361214ad
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/validate-calendar-value.js
@@ -0,0 +1,54 @@
+// |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-get-temporal.zoneddatetime.prototype.year
+description: Validate result returned from calendar year() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [Infinity, RangeError],
+ [-Infinity, RangeError],
+ [Symbol("foo"), TypeError],
+ [7n, TypeError],
+ [NaN, RangeError],
+ ["string", TypeError],
+ [{}, TypeError],
+ [null, TypeError],
+ [true, TypeError],
+ [false, TypeError],
+ [7.1, RangeError],
+ [-0.1, RangeError],
+ ["7", TypeError],
+ ["7.5", TypeError],
+ [{valueOf() { return 7; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ year() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar);
+ assert.throws(error, () => instance.year, `${typeof result} ${String(result)} not converted to integer`);
+});
+
+const preservedResults = [
+ -7,
+];
+
+preservedResults.forEach(result => {
+ const calendar = new class extends Temporal.Calendar {
+ year() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar);
+ assert.sameValue(instance.year, result, `${typeof result} ${String(result)} preserved`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/branding.js
new file mode 100644
index 0000000000..fdb41c4b84
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/branding.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-get-temporal.zoneddatetime.prototype.yearofweek
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const yearOfWeek = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "yearOfWeek").get;
+
+assert.sameValue(typeof yearOfWeek, "function");
+
+assert.throws(TypeError, () => yearOfWeek.call(undefined), "undefined");
+assert.throws(TypeError, () => yearOfWeek.call(null), "null");
+assert.throws(TypeError, () => yearOfWeek.call(true), "true");
+assert.throws(TypeError, () => yearOfWeek.call(""), "empty string");
+assert.throws(TypeError, () => yearOfWeek.call(Symbol()), "symbol");
+assert.throws(TypeError, () => yearOfWeek.call(1), "1");
+assert.throws(TypeError, () => yearOfWeek.call({}), "plain object");
+assert.throws(TypeError, () => yearOfWeek.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime");
+assert.throws(TypeError, () => yearOfWeek.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..a090ffa2ec
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.zoneddatetime.prototype.yearofweek
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const yearOfWeekOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "yearOfWeek");
+Object.defineProperty(Temporal.Calendar.prototype, "yearOfWeek", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("yearOfWeek should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.yearOfWeek;
+
+Object.defineProperty(Temporal.Calendar.prototype, "yearOfWeek", yearOfWeekOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/builtin-timezone-no-observable-calls.js
new file mode 100644
index 0000000000..ec06a59283
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/builtin-timezone-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |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.zoneddatetime.prototype.yearofweek
+description: >
+ Calling the method on an instance constructed with a builtin time zone causes
+ no observable lookups or calls to time zone methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
+ },
+});
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
+instance.yearOfWeek;
+
+Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/custom.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/custom.js
new file mode 100644
index 0000000000..85418c9f63
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/custom.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-get-temporal.zoneddatetime.prototype.yearofweek
+description: Custom calendar tests for yearOfWeek().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ yearOfWeek(...args) {
+ ++calls;
+ assert.compareArray(args.map(String), [instance].map((arg) => arg.toPlainDateTime().toString()), "yearOfWeek arguments");
+ return 7;
+ }
+}
+
+const calendar = new CustomCalendar();
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar);
+const result = instance.yearOfWeek;
+assert.sameValue(result, 7, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/prop-desc.js
new file mode 100644
index 0000000000..2cd9360938
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/prop-desc.js
@@ -0,0 +1,17 @@
+// |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-get-temporal.zoneddatetime.prototype.yearofweek
+description: The "yearOfWeek" property of Temporal.ZonedDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "yearOfWeek");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..18aa0dea68
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,18 @@
+// |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-get-temporal.zoneddatetime.prototype.yearofweek
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.yearOfWeek);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..7050c29ded
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/timezone-getoffsetnanosecondsfor-not-callable.js
@@ -0,0 +1,22 @@
+// |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-get-temporal.zoneddatetime.prototype.yearofweek
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => datetime.yearOfWeek,
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..0eb761b86c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,18 @@
+// |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-get-temporal.zoneddatetime.prototype.yearofweek
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.yearOfWeek);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..dfc1f0ebec
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/timezone-getoffsetnanosecondsfor-wrong-type.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-get-temporal.zoneddatetime.prototype.yearofweek
+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 datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.yearOfWeek);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/validate-calendar-value.js
new file mode 100644
index 0000000000..9bd447f9a1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/validate-calendar-value.js
@@ -0,0 +1,54 @@
+// |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-get-temporal.zoneddatetime.prototype.yearofweek
+description: Validate result returned from calendar yearOfWeek() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [Infinity, RangeError],
+ [-Infinity, RangeError],
+ [Symbol("foo"), TypeError],
+ [7n, TypeError],
+ [NaN, RangeError],
+ ["string", TypeError],
+ [{}, TypeError],
+ [null, TypeError],
+ [true, TypeError],
+ [false, TypeError],
+ [7.1, RangeError],
+ [-0.1, RangeError],
+ ["7", TypeError],
+ ["7.5", TypeError],
+ [{valueOf() { return 7; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ yearOfWeek() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar);
+ assert.throws(error, () => instance.yearOfWeek, `${typeof result} ${String(result)} not converted to integer`);
+});
+
+const preservedResults = [
+ -7,
+];
+
+preservedResults.forEach(result => {
+ const calendar = new class extends Temporal.Calendar {
+ yearOfWeek() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar);
+ assert.sameValue(instance.yearOfWeek, result, `${typeof result} ${String(result)} preserved`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/subclass.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/subclass.js
new file mode 100644
index 0000000000..54360455db
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/subclass.js
@@ -0,0 +1,20 @@
+// |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.zoneddatetime
+description: Test for Temporal.ZonedDateTime subclassing.
+features: [Temporal]
+---*/
+
+class CustomZonedDateTime extends Temporal.ZonedDateTime {
+}
+
+const instance = new CustomZonedDateTime(0n, "UTC");
+assert.sameValue(instance.epochNanoseconds, 0n);
+assert.sameValue(Object.getPrototypeOf(instance), CustomZonedDateTime.prototype, "Instance of CustomZonedDateTime");
+assert(instance instanceof CustomZonedDateTime, "Instance of CustomZonedDateTime");
+assert(instance instanceof Temporal.ZonedDateTime, "Instance of Temporal.ZonedDateTime");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/timezone-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/timezone-case-insensitive.js
new file mode 100644
index 0000000000..afcda49591
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/timezone-case-insensitive.js
@@ -0,0 +1,15 @@
+// |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.zoneddatetime.prototype.constructor
+description: Time zone names are case insensitive
+features: [Temporal]
+---*/
+
+const timeZone = 'uTc';
+const result = new Temporal.ZonedDateTime(0n, timeZone);
+assert.sameValue(result.timeZoneId, 'UTC', `Time zone created from string "${timeZone}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/timezone-string-datetime.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/timezone-string-datetime.js
new file mode 100644
index 0000000000..16e8513a5c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/timezone-string-datetime.js
@@ -0,0 +1,63 @@
+// |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.zoneddatetime
+description: Conversion of ISO date-time strings to Temporal.TimeZone instances
+features: [Temporal]
+---*/
+
+let timeZone = "2021-08-19T17:30";
+assert.throws(RangeError, () => new Temporal.ZonedDateTime(0n, timeZone), "bare date-time string is not a time zone");
+
+[
+ "2021-08-19T17:30-07:00:01",
+ "2021-08-19T17:30-07:00:00",
+ "2021-08-19T17:30-07:00:00.1",
+ "2021-08-19T17:30-07:00:00.0",
+ "2021-08-19T17:30-07:00:00.01",
+ "2021-08-19T17:30-07:00:00.00",
+ "2021-08-19T17:30-07:00:00.001",
+ "2021-08-19T17:30-07:00:00.000",
+ "2021-08-19T17:30-07:00:00.0001",
+ "2021-08-19T17:30-07:00:00.0000",
+ "2021-08-19T17:30-07:00:00.00001",
+ "2021-08-19T17:30-07:00:00.00000",
+ "2021-08-19T17:30-07:00:00.000001",
+ "2021-08-19T17:30-07:00:00.000000",
+ "2021-08-19T17:30-07:00:00.0000001",
+ "2021-08-19T17:30-07:00:00.0000000",
+ "2021-08-19T17:30-07:00:00.00000001",
+ "2021-08-19T17:30-07:00:00.00000000",
+ "2021-08-19T17:30-07:00:00.000000001",
+ "2021-08-19T17:30-07:00:00.000000000",
+].forEach((timeZone) => {
+ assert.throws(
+ RangeError,
+ () => new Temporal.ZonedDateTime(0n, timeZone),
+ `ISO string ${timeZone} with a sub-minute offset is not a valid time zone`
+ );
+});
+
+timeZone = "2021-08-19T17:30Z";
+const result1 = new Temporal.ZonedDateTime(0n, timeZone);
+assert.sameValue(result1.timeZoneId, "UTC", "date-time + Z is UTC time zone");
+
+timeZone = "2021-08-19T17:30-07:00";
+const result2 = new Temporal.ZonedDateTime(0n, timeZone);
+assert.sameValue(result2.timeZoneId, "-07:00", "date-time + offset is the offset time zone");
+
+timeZone = "2021-08-19T17:30[UTC]";
+const result3 = new Temporal.ZonedDateTime(0n, timeZone);
+assert.sameValue(result3.timeZoneId, "UTC", "date-time + IANA annotation is the IANA time zone");
+
+timeZone = "2021-08-19T17:30Z[UTC]";
+const result4 = new Temporal.ZonedDateTime(0n, timeZone);
+assert.sameValue(result4.timeZoneId, "UTC", "date-time + Z + IANA annotation is the IANA time zone");
+
+timeZone = "2021-08-19T17:30-07:00[UTC]";
+const result5 = new Temporal.ZonedDateTime(0n, timeZone);
+assert.sameValue(result5.timeZoneId, "UTC", "date-time + offset + IANA annotation is the IANA time zone");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/timezone-string-leap-second.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/timezone-string-leap-second.js
new file mode 100644
index 0000000000..09113ed5f2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/timezone-string-leap-second.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.zoneddatetime
+description: Leap second is a valid ISO string for TimeZone
+features: [Temporal]
+---*/
+
+let timeZone = "2016-12-31T23:59:60+00:00[UTC]";
+
+const result = new Temporal.ZonedDateTime(0n, timeZone);
+assert.sameValue(result.timeZoneId, "UTC", "Time zone string determined from bracket name");
+
+timeZone = "2021-08-19T17:30:45.123456789+23:59[+23:59:60]";
+assert.throws(RangeError, () => new Temporal.ZonedDateTime(0n, timeZone), "leap second in time zone name not valid");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/timezone-string-multiple-offsets.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/timezone-string-multiple-offsets.js
new file mode 100644
index 0000000000..74d3887c5b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/timezone-string-multiple-offsets.js
@@ -0,0 +1,16 @@
+// |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.zoneddatetime
+description: Time zone parsing from ISO strings uses the bracketed offset, not the ISO string offset
+features: [Temporal]
+---*/
+
+const timeZone = "2021-08-19T17:30:45.123456789-12:12[+01:46]";
+
+const result = new Temporal.ZonedDateTime(0n, timeZone);
+assert.sameValue(result.timeZoneId, "+01:46", "Time zone string determined from bracket name");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/timezone-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/timezone-string.js
new file mode 100644
index 0000000000..6f86ee18fe
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/timezone-string.js
@@ -0,0 +1,16 @@
+// |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.zoneddatetime
+description: Time zone IDs are valid input for a time zone
+features: [Temporal]
+---*/
+
+["UTC", "+01:30"].forEach((timeZone) => {
+ const result = new Temporal.ZonedDateTime(0n, timeZone);
+ assert.sameValue(result.getISOFields().timeZone, timeZone, `time zone slot should store string "${timeZone}"`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/timezone-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/timezone-wrong-type.js
new file mode 100644
index 0000000000..4c159defbc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/timezone-wrong-type.js
@@ -0,0 +1,38 @@
+// |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.zoneddatetime
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or object for TimeZone
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [19761118, "number that would convert to a valid ISO string in other contexts"],
+ [1n, "bigint"],
+];
+
+for (const [timeZone, description] of primitiveTests) {
+ assert.throws(
+ typeof timeZone === 'string' ? RangeError : TypeError,
+ () => new Temporal.ZonedDateTime(0n, timeZone),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+];
+
+for (const [timeZone, description] of typeErrorTests) {
+ assert.throws(TypeError, () => new Temporal.ZonedDateTime(0n, timeZone), `${description} is not a valid object and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/browser.js b/js/src/tests/test262/built-ins/Temporal/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/getOwnPropertyNames.js b/js/src/tests/test262/built-ins/Temporal/getOwnPropertyNames.js
new file mode 100644
index 0000000000..6f02865526
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/getOwnPropertyNames.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-objects
+description: Temporal has own property names
+features: [Temporal]
+---*/
+
+const keys = Object.getOwnPropertyNames(Temporal);
+
+assert(keys.indexOf("Instant") > -1, "Instant");
+assert(keys.indexOf("TimeZone") > -1, "TimeZone");
+assert(keys.indexOf("PlainDate") > -1, "PlainDate");
+assert(keys.indexOf("PlainTime") > -1, "PlainTime");
+assert(keys.indexOf("PlainDateTime") > -1, "PlainDateTime");
+assert(keys.indexOf("ZonedDateTime") > -1, "ZonedDateTime");
+assert(keys.indexOf("PlainYearMonth") > -1, "PlainYearMonth");
+assert(keys.indexOf("PlainMonthDay") > -1, "PlainMonthDay");
+assert(keys.indexOf("Duration") > -1, "Duration");
+assert(keys.indexOf("Calendar") > -1, "Calendar");
+assert(keys.indexOf("Now") > -1, "Now");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/keys.js b/js/src/tests/test262/built-ins/Temporal/keys.js
new file mode 100644
index 0000000000..4318acd6f1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/keys.js
@@ -0,0 +1,15 @@
+// |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-objects
+description: Temporal has no enumerable properties
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const keys = Object.keys(Temporal);
+assert.compareArray(keys, []);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/prop-desc.js
new file mode 100644
index 0000000000..03027d3dbb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/prop-desc.js
@@ -0,0 +1,19 @@
+// |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-objects
+includes: [propertyHelper.js]
+description: The "Temporal" property of the global object
+features: [Temporal]
+---*/
+
+assert.sameValue(typeof Temporal, "object");
+verifyProperty(this, "Temporal", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/shell.js b/js/src/tests/test262/built-ins/Temporal/shell.js
new file mode 100644
index 0000000000..60f74c2518
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/shell.js
@@ -0,0 +1,2158 @@
+// GENERATED, DO NOT EDIT
+// file: temporalHelpers.js
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+description: |
+ This defines helper objects and functions for testing Temporal.
+defines: [TemporalHelpers]
+features: [Symbol.species, Symbol.iterator, Temporal]
+---*/
+
+const ASCII_IDENTIFIER = /^[$_a-zA-Z][$_a-zA-Z0-9]*$/u;
+
+function formatPropertyName(propertyKey, objectName = "") {
+ switch (typeof propertyKey) {
+ case "symbol":
+ if (Symbol.keyFor(propertyKey) !== undefined) {
+ return `${objectName}[Symbol.for('${Symbol.keyFor(propertyKey)}')]`;
+ } else if (propertyKey.description.startsWith('Symbol.')) {
+ return `${objectName}[${propertyKey.description}]`;
+ } else {
+ return `${objectName}[Symbol('${propertyKey.description}')]`
+ }
+ case "string":
+ if (propertyKey !== String(Number(propertyKey))) {
+ if (ASCII_IDENTIFIER.test(propertyKey)) {
+ return objectName ? `${objectName}.${propertyKey}` : propertyKey;
+ }
+ return `${objectName}['${propertyKey.replace(/'/g, "\\'")}']`
+ }
+ // fall through
+ default:
+ // integer or string integer-index
+ return `${objectName}[${propertyKey}]`;
+ }
+}
+
+const SKIP_SYMBOL = Symbol("Skip");
+
+var TemporalHelpers = {
+ /*
+ * Codes and maximum lengths of months in the ISO 8601 calendar.
+ */
+ ISOMonths: [
+ { month: 1, monthCode: "M01", daysInMonth: 31 },
+ { month: 2, monthCode: "M02", daysInMonth: 29 },
+ { month: 3, monthCode: "M03", daysInMonth: 31 },
+ { month: 4, monthCode: "M04", daysInMonth: 30 },
+ { month: 5, monthCode: "M05", daysInMonth: 31 },
+ { month: 6, monthCode: "M06", daysInMonth: 30 },
+ { month: 7, monthCode: "M07", daysInMonth: 31 },
+ { month: 8, monthCode: "M08", daysInMonth: 31 },
+ { month: 9, monthCode: "M09", daysInMonth: 30 },
+ { month: 10, monthCode: "M10", daysInMonth: 31 },
+ { month: 11, monthCode: "M11", daysInMonth: 30 },
+ { month: 12, monthCode: "M12", daysInMonth: 31 }
+ ],
+
+ /*
+ * assertDuration(duration, years, ..., nanoseconds[, description]):
+ *
+ * Shorthand for asserting that each field of a Temporal.Duration is equal to
+ * an expected value.
+ */
+ assertDuration(duration, years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, description = "") {
+ const prefix = description ? `${description}: ` : "";
+ assert(duration instanceof Temporal.Duration, `${prefix}instanceof`);
+ assert.sameValue(duration.years, years, `${prefix}years result:`);
+ assert.sameValue(duration.months, months, `${prefix}months result:`);
+ assert.sameValue(duration.weeks, weeks, `${prefix}weeks result:`);
+ assert.sameValue(duration.days, days, `${prefix}days result:`);
+ assert.sameValue(duration.hours, hours, `${prefix}hours result:`);
+ assert.sameValue(duration.minutes, minutes, `${prefix}minutes result:`);
+ assert.sameValue(duration.seconds, seconds, `${prefix}seconds result:`);
+ assert.sameValue(duration.milliseconds, milliseconds, `${prefix}milliseconds result:`);
+ assert.sameValue(duration.microseconds, microseconds, `${prefix}microseconds result:`);
+ assert.sameValue(duration.nanoseconds, nanoseconds, `${prefix}nanoseconds result`);
+ },
+
+ /*
+ * assertDateDuration(duration, years, months, weeks, days, [, description]):
+ *
+ * Shorthand for asserting that each date field of a Temporal.Duration is
+ * equal to an expected value.
+ */
+ assertDateDuration(duration, years, months, weeks, days, description = "") {
+ const prefix = description ? `${description}: ` : "";
+ assert(duration instanceof Temporal.Duration, `${prefix}instanceof`);
+ assert.sameValue(duration.years, years, `${prefix}years result:`);
+ assert.sameValue(duration.months, months, `${prefix}months result:`);
+ assert.sameValue(duration.weeks, weeks, `${prefix}weeks result:`);
+ assert.sameValue(duration.days, days, `${prefix}days result:`);
+ assert.sameValue(duration.hours, 0, `${prefix}hours result should be zero:`);
+ assert.sameValue(duration.minutes, 0, `${prefix}minutes result should be zero:`);
+ assert.sameValue(duration.seconds, 0, `${prefix}seconds result should be zero:`);
+ assert.sameValue(duration.milliseconds, 0, `${prefix}milliseconds result should be zero:`);
+ assert.sameValue(duration.microseconds, 0, `${prefix}microseconds result should be zero:`);
+ assert.sameValue(duration.nanoseconds, 0, `${prefix}nanoseconds result should be zero:`);
+ },
+
+ /*
+ * assertDurationsEqual(actual, expected[, description]):
+ *
+ * Shorthand for asserting that each field of a Temporal.Duration is equal to
+ * the corresponding field in another Temporal.Duration.
+ */
+ assertDurationsEqual(actual, expected, description = "") {
+ const prefix = description ? `${description}: ` : "";
+ assert(expected instanceof Temporal.Duration, `${prefix}expected value should be a Temporal.Duration`);
+ TemporalHelpers.assertDuration(actual, expected.years, expected.months, expected.weeks, expected.days, expected.hours, expected.minutes, expected.seconds, expected.milliseconds, expected.microseconds, expected.nanoseconds, description);
+ },
+
+ /*
+ * assertInstantsEqual(actual, expected[, description]):
+ *
+ * Shorthand for asserting that two Temporal.Instants are of the correct type
+ * and equal according to their equals() methods.
+ */
+ assertInstantsEqual(actual, expected, description = "") {
+ const prefix = description ? `${description}: ` : "";
+ assert(expected instanceof Temporal.Instant, `${prefix}expected value should be a Temporal.Instant`);
+ assert(actual instanceof Temporal.Instant, `${prefix}instanceof`);
+ assert(actual.equals(expected), `${prefix}equals method`);
+ },
+
+ /*
+ * assertPlainDate(date, year, ..., nanosecond[, description[, era, eraYear]]):
+ *
+ * Shorthand for asserting that each field of a Temporal.PlainDate is equal to
+ * an expected value. (Except the `calendar` property, since callers may want
+ * to assert either object equality with an object they put in there, or the
+ * value of date.calendarId.)
+ */
+ assertPlainDate(date, year, month, monthCode, day, description = "", era = undefined, eraYear = undefined) {
+ const prefix = description ? `${description}: ` : "";
+ assert(date instanceof Temporal.PlainDate, `${prefix}instanceof`);
+ assert.sameValue(date.era, era, `${prefix}era result:`);
+ assert.sameValue(date.eraYear, eraYear, `${prefix}eraYear result:`);
+ assert.sameValue(date.year, year, `${prefix}year result:`);
+ assert.sameValue(date.month, month, `${prefix}month result:`);
+ assert.sameValue(date.monthCode, monthCode, `${prefix}monthCode result:`);
+ assert.sameValue(date.day, day, `${prefix}day result:`);
+ },
+
+ /*
+ * assertPlainDateTime(datetime, year, ..., nanosecond[, description[, era, eraYear]]):
+ *
+ * Shorthand for asserting that each field of a Temporal.PlainDateTime is
+ * equal to an expected value. (Except the `calendar` property, since callers
+ * may want to assert either object equality with an object they put in there,
+ * or the value of datetime.calendarId.)
+ */
+ assertPlainDateTime(datetime, year, month, monthCode, day, hour, minute, second, millisecond, microsecond, nanosecond, description = "", era = undefined, eraYear = undefined) {
+ const prefix = description ? `${description}: ` : "";
+ assert(datetime instanceof Temporal.PlainDateTime, `${prefix}instanceof`);
+ assert.sameValue(datetime.era, era, `${prefix}era result:`);
+ assert.sameValue(datetime.eraYear, eraYear, `${prefix}eraYear result:`);
+ assert.sameValue(datetime.year, year, `${prefix}year result:`);
+ assert.sameValue(datetime.month, month, `${prefix}month result:`);
+ assert.sameValue(datetime.monthCode, monthCode, `${prefix}monthCode result:`);
+ assert.sameValue(datetime.day, day, `${prefix}day result:`);
+ assert.sameValue(datetime.hour, hour, `${prefix}hour result:`);
+ assert.sameValue(datetime.minute, minute, `${prefix}minute result:`);
+ assert.sameValue(datetime.second, second, `${prefix}second result:`);
+ assert.sameValue(datetime.millisecond, millisecond, `${prefix}millisecond result:`);
+ assert.sameValue(datetime.microsecond, microsecond, `${prefix}microsecond result:`);
+ assert.sameValue(datetime.nanosecond, nanosecond, `${prefix}nanosecond result:`);
+ },
+
+ /*
+ * assertPlainDateTimesEqual(actual, expected[, description]):
+ *
+ * Shorthand for asserting that two Temporal.PlainDateTimes are of the correct
+ * type, equal according to their equals() methods, and additionally that
+ * their calendar internal slots are the same value.
+ */
+ assertPlainDateTimesEqual(actual, expected, description = "") {
+ const prefix = description ? `${description}: ` : "";
+ assert(expected instanceof Temporal.PlainDateTime, `${prefix}expected value should be a Temporal.PlainDateTime`);
+ assert(actual instanceof Temporal.PlainDateTime, `${prefix}instanceof`);
+ assert(actual.equals(expected), `${prefix}equals method`);
+ assert.sameValue(
+ actual.getISOFields().calendar,
+ expected.getISOFields().calendar,
+ `${prefix}calendar same value:`
+ );
+ },
+
+ /*
+ * assertPlainMonthDay(monthDay, monthCode, day[, description [, referenceISOYear]]):
+ *
+ * Shorthand for asserting that each field of a Temporal.PlainMonthDay is
+ * equal to an expected value. (Except the `calendar` property, since callers
+ * may want to assert either object equality with an object they put in there,
+ * or the value of monthDay.calendarId().)
+ */
+ assertPlainMonthDay(monthDay, monthCode, day, description = "", referenceISOYear = 1972) {
+ const prefix = description ? `${description}: ` : "";
+ assert(monthDay instanceof Temporal.PlainMonthDay, `${prefix}instanceof`);
+ assert.sameValue(monthDay.monthCode, monthCode, `${prefix}monthCode result:`);
+ assert.sameValue(monthDay.day, day, `${prefix}day result:`);
+ assert.sameValue(monthDay.getISOFields().isoYear, referenceISOYear, `${prefix}referenceISOYear result:`);
+ },
+
+ /*
+ * assertPlainTime(time, hour, ..., nanosecond[, description]):
+ *
+ * Shorthand for asserting that each field of a Temporal.PlainTime is equal to
+ * an expected value.
+ */
+ assertPlainTime(time, hour, minute, second, millisecond, microsecond, nanosecond, description = "") {
+ const prefix = description ? `${description}: ` : "";
+ assert(time instanceof Temporal.PlainTime, `${prefix}instanceof`);
+ assert.sameValue(time.hour, hour, `${prefix}hour result:`);
+ assert.sameValue(time.minute, minute, `${prefix}minute result:`);
+ assert.sameValue(time.second, second, `${prefix}second result:`);
+ assert.sameValue(time.millisecond, millisecond, `${prefix}millisecond result:`);
+ assert.sameValue(time.microsecond, microsecond, `${prefix}microsecond result:`);
+ assert.sameValue(time.nanosecond, nanosecond, `${prefix}nanosecond result:`);
+ },
+
+ /*
+ * assertPlainTimesEqual(actual, expected[, description]):
+ *
+ * Shorthand for asserting that two Temporal.PlainTimes are of the correct
+ * type and equal according to their equals() methods.
+ */
+ assertPlainTimesEqual(actual, expected, description = "") {
+ const prefix = description ? `${description}: ` : "";
+ assert(expected instanceof Temporal.PlainTime, `${prefix}expected value should be a Temporal.PlainTime`);
+ assert(actual instanceof Temporal.PlainTime, `${prefix}instanceof`);
+ assert(actual.equals(expected), `${prefix}equals method`);
+ },
+
+ /*
+ * assertPlainYearMonth(yearMonth, year, month, monthCode[, description[, era, eraYear, referenceISODay]]):
+ *
+ * Shorthand for asserting that each field of a Temporal.PlainYearMonth is
+ * equal to an expected value. (Except the `calendar` property, since callers
+ * may want to assert either object equality with an object they put in there,
+ * or the value of yearMonth.calendarId.)
+ */
+ assertPlainYearMonth(yearMonth, year, month, monthCode, description = "", era = undefined, eraYear = undefined, referenceISODay = 1) {
+ const prefix = description ? `${description}: ` : "";
+ assert(yearMonth instanceof Temporal.PlainYearMonth, `${prefix}instanceof`);
+ assert.sameValue(yearMonth.era, era, `${prefix}era result:`);
+ assert.sameValue(yearMonth.eraYear, eraYear, `${prefix}eraYear result:`);
+ assert.sameValue(yearMonth.year, year, `${prefix}year result:`);
+ assert.sameValue(yearMonth.month, month, `${prefix}month result:`);
+ assert.sameValue(yearMonth.monthCode, monthCode, `${prefix}monthCode result:`);
+ assert.sameValue(yearMonth.getISOFields().isoDay, referenceISODay, `${prefix}referenceISODay result:`);
+ },
+
+ /*
+ * assertZonedDateTimesEqual(actual, expected[, description]):
+ *
+ * Shorthand for asserting that two Temporal.ZonedDateTimes are of the correct
+ * type, equal according to their equals() methods, and additionally that
+ * their time zones and calendar internal slots are the same value.
+ */
+ assertZonedDateTimesEqual(actual, expected, description = "") {
+ const prefix = description ? `${description}: ` : "";
+ assert(expected instanceof Temporal.ZonedDateTime, `${prefix}expected value should be a Temporal.ZonedDateTime`);
+ assert(actual instanceof Temporal.ZonedDateTime, `${prefix}instanceof`);
+ assert(actual.equals(expected), `${prefix}equals method`);
+ assert.sameValue(actual.timeZone, expected.timeZone, `${prefix}time zone same value:`);
+ assert.sameValue(
+ actual.getISOFields().calendar,
+ expected.getISOFields().calendar,
+ `${prefix}calendar same value:`
+ );
+ },
+
+ /*
+ * assertUnreachable(description):
+ *
+ * Helper for asserting that code is not executed. This is useful for
+ * assertions that methods of user calendars and time zones are not called.
+ */
+ assertUnreachable(description) {
+ let message = "This code should not be executed";
+ if (description) {
+ message = `${message}: ${description}`;
+ }
+ throw new Test262Error(message);
+ },
+
+ /*
+ * checkCalendarDateUntilLargestUnitSingular(func, expectedLargestUnitCalls):
+ *
+ * When an options object with a largestUnit property is synthesized inside
+ * Temporal and passed to user code such as calendar.dateUntil(), the value of
+ * the largestUnit property should be in the singular form, even if the input
+ * was given in the plural form.
+ * (This doesn't apply when the options object is passed through verbatim.)
+ *
+ * func(calendar, largestUnit, index) is the operation under test. It's called
+ * with an instance of a calendar that keeps track of which largestUnit is
+ * passed to dateUntil(), each key of expectedLargestUnitCalls in turn, and
+ * the key's numerical index in case the function needs to generate test data
+ * based on the index. At the end, the actual values passed to dateUntil() are
+ * compared with the array values of expectedLargestUnitCalls.
+ */
+ checkCalendarDateUntilLargestUnitSingular(func, expectedLargestUnitCalls) {
+ const actual = [];
+
+ class DateUntilOptionsCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+
+ dateUntil(earlier, later, options) {
+ actual.push(options.largestUnit);
+ return super.dateUntil(earlier, later, options);
+ }
+
+ toString() {
+ return "date-until-options";
+ }
+ }
+
+ const calendar = new DateUntilOptionsCalendar();
+ Object.entries(expectedLargestUnitCalls).forEach(([largestUnit, expected], index) => {
+ func(calendar, largestUnit, index);
+ assert.compareArray(actual, expected, `largestUnit passed to calendar.dateUntil() for largestUnit ${largestUnit}`);
+ actual.splice(0); // empty it for the next check
+ });
+ },
+
+ /*
+ * checkPlainDateTimeConversionFastPath(func):
+ *
+ * ToTemporalDate and ToTemporalTime should both, if given a
+ * Temporal.PlainDateTime instance, convert to the desired type by reading the
+ * PlainDateTime's internal slots, rather than calling any getters.
+ *
+ * func(datetime, calendar) is the actual operation to test, that must
+ * internally call the abstract operation ToTemporalDate or ToTemporalTime.
+ * It is passed a Temporal.PlainDateTime instance, as well as the instance's
+ * calendar object (so that it doesn't have to call the calendar getter itself
+ * if it wants to make any assertions about the calendar.)
+ */
+ checkPlainDateTimeConversionFastPath(func, message = "checkPlainDateTimeConversionFastPath") {
+ const actual = [];
+ const expected = [];
+
+ const calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+ const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.PlainDateTime.prototype);
+ ["year", "month", "monthCode", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((property) => {
+ Object.defineProperty(datetime, property, {
+ get() {
+ actual.push(`get ${formatPropertyName(property)}`);
+ const value = prototypeDescrs[property].get.call(this);
+ return {
+ toString() {
+ actual.push(`toString ${formatPropertyName(property)}`);
+ return value.toString();
+ },
+ valueOf() {
+ actual.push(`valueOf ${formatPropertyName(property)}`);
+ return value;
+ },
+ };
+ },
+ });
+ });
+ Object.defineProperty(datetime, "calendar", {
+ get() {
+ actual.push("get calendar");
+ return calendar;
+ },
+ });
+
+ func(datetime, calendar);
+ assert.compareArray(actual, expected, `${message}: property getters not called`);
+ },
+
+ /*
+ * Check that an options bag that accepts units written in the singular form,
+ * also accepts the same units written in the plural form.
+ * func(unit) should call the method with the appropriate options bag
+ * containing unit as a value. This will be called twice for each element of
+ * validSingularUnits, once with singular and once with plural, and the
+ * results of each pair should be the same (whether a Temporal object or a
+ * primitive value.)
+ */
+ checkPluralUnitsAccepted(func, validSingularUnits) {
+ const plurals = {
+ year: 'years',
+ month: 'months',
+ week: 'weeks',
+ day: 'days',
+ hour: 'hours',
+ minute: 'minutes',
+ second: 'seconds',
+ millisecond: 'milliseconds',
+ microsecond: 'microseconds',
+ nanosecond: 'nanoseconds',
+ };
+
+ validSingularUnits.forEach((unit) => {
+ const singularValue = func(unit);
+ const pluralValue = func(plurals[unit]);
+ const desc = `Plural ${plurals[unit]} produces the same result as singular ${unit}`;
+ if (singularValue instanceof Temporal.Duration) {
+ TemporalHelpers.assertDurationsEqual(pluralValue, singularValue, desc);
+ } else if (singularValue instanceof Temporal.Instant) {
+ TemporalHelpers.assertInstantsEqual(pluralValue, singularValue, desc);
+ } else if (singularValue instanceof Temporal.PlainDateTime) {
+ TemporalHelpers.assertPlainDateTimesEqual(pluralValue, singularValue, desc);
+ } else if (singularValue instanceof Temporal.PlainTime) {
+ TemporalHelpers.assertPlainTimesEqual(pluralValue, singularValue, desc);
+ } else if (singularValue instanceof Temporal.ZonedDateTime) {
+ TemporalHelpers.assertZonedDateTimesEqual(pluralValue, singularValue, desc);
+ } else {
+ assert.sameValue(pluralValue, singularValue);
+ }
+ });
+ },
+
+ /*
+ * checkRoundingIncrementOptionWrongType(checkFunc, assertTrueResultFunc, assertObjectResultFunc):
+ *
+ * Checks the type handling of the roundingIncrement option.
+ * checkFunc(roundingIncrement) is a function which takes the value of
+ * roundingIncrement to test, and calls the method under test with it,
+ * returning the result. assertTrueResultFunc(result, description) should
+ * assert that result is the expected result with roundingIncrement: true, and
+ * assertObjectResultFunc(result, description) should assert that result is
+ * the expected result with roundingIncrement being an object with a valueOf()
+ * method.
+ */
+ checkRoundingIncrementOptionWrongType(checkFunc, assertTrueResultFunc, assertObjectResultFunc) {
+ // null converts to 0, which is out of range
+ assert.throws(RangeError, () => checkFunc(null), "null");
+ // Booleans convert to either 0 or 1, and 1 is allowed
+ const trueResult = checkFunc(true);
+ assertTrueResultFunc(trueResult, "true");
+ assert.throws(RangeError, () => checkFunc(false), "false");
+ // Symbols and BigInts cannot convert to numbers
+ assert.throws(TypeError, () => checkFunc(Symbol()), "symbol");
+ assert.throws(TypeError, () => checkFunc(2n), "bigint");
+
+ // Objects prefer their valueOf() methods when converting to a number
+ assert.throws(RangeError, () => checkFunc({}), "plain object");
+
+ const expected = [
+ "get roundingIncrement.valueOf",
+ "call roundingIncrement.valueOf",
+ ];
+ const actual = [];
+ const observer = TemporalHelpers.toPrimitiveObserver(actual, 2, "roundingIncrement");
+ const objectResult = checkFunc(observer);
+ assertObjectResultFunc(objectResult, "object with valueOf");
+ assert.compareArray(actual, expected, "order of operations");
+ },
+
+ /*
+ * checkStringOptionWrongType(propertyName, value, checkFunc, assertFunc):
+ *
+ * Checks the type handling of a string option, of which there are several in
+ * Temporal.
+ * propertyName is the name of the option, and value is the value that
+ * assertFunc should expect it to have.
+ * checkFunc(value) is a function which takes the value of the option to test,
+ * and calls the method under test with it, returning the result.
+ * assertFunc(result, description) should assert that result is the expected
+ * result with the option value being an object with a toString() method
+ * which returns the given value.
+ */
+ checkStringOptionWrongType(propertyName, value, checkFunc, assertFunc) {
+ // null converts to the string "null", which is an invalid string value
+ assert.throws(RangeError, () => checkFunc(null), "null");
+ // Booleans convert to the strings "true" or "false", which are invalid
+ assert.throws(RangeError, () => checkFunc(true), "true");
+ assert.throws(RangeError, () => checkFunc(false), "false");
+ // Symbols cannot convert to strings
+ assert.throws(TypeError, () => checkFunc(Symbol()), "symbol");
+ // Numbers convert to strings which are invalid
+ assert.throws(RangeError, () => checkFunc(2), "number");
+ // BigInts convert to strings which are invalid
+ assert.throws(RangeError, () => checkFunc(2n), "bigint");
+
+ // Objects prefer their toString() methods when converting to a string
+ assert.throws(RangeError, () => checkFunc({}), "plain object");
+
+ const expected = [
+ `get ${propertyName}.toString`,
+ `call ${propertyName}.toString`,
+ ];
+ const actual = [];
+ const observer = TemporalHelpers.toPrimitiveObserver(actual, value, propertyName);
+ const result = checkFunc(observer);
+ assertFunc(result, "object with toString");
+ assert.compareArray(actual, expected, "order of operations");
+ },
+
+ /*
+ * checkSubclassingIgnored(construct, constructArgs, method, methodArgs,
+ * resultAssertions):
+ *
+ * Methods of Temporal classes that return a new instance of the same class,
+ * must not take the constructor of a subclass into account, nor the @@species
+ * property. This helper runs tests to ensure this.
+ *
+ * construct(...constructArgs) must yield a valid instance of the Temporal
+ * class. instance[method](...methodArgs) is the method call under test, which
+ * must also yield a valid instance of the same Temporal class, not a
+ * subclass. See below for the individual tests that this runs.
+ * resultAssertions() is a function that performs additional assertions on the
+ * instance returned by the method under test.
+ */
+ checkSubclassingIgnored(...args) {
+ this.checkSubclassConstructorNotObject(...args);
+ this.checkSubclassConstructorUndefined(...args);
+ this.checkSubclassConstructorThrows(...args);
+ this.checkSubclassConstructorNotCalled(...args);
+ this.checkSubclassSpeciesInvalidResult(...args);
+ this.checkSubclassSpeciesNotAConstructor(...args);
+ this.checkSubclassSpeciesNull(...args);
+ this.checkSubclassSpeciesUndefined(...args);
+ this.checkSubclassSpeciesThrows(...args);
+ },
+
+ /*
+ * Checks that replacing the 'constructor' property of the instance with
+ * various primitive values does not affect the returned new instance.
+ */
+ checkSubclassConstructorNotObject(construct, constructArgs, method, methodArgs, resultAssertions) {
+ function check(value, description) {
+ const instance = new construct(...constructArgs);
+ instance.constructor = value;
+ const result = instance[method](...methodArgs);
+ assert.sameValue(Object.getPrototypeOf(result), construct.prototype, description);
+ resultAssertions(result);
+ }
+
+ check(null, "null");
+ check(true, "true");
+ check("test", "string");
+ check(Symbol(), "Symbol");
+ check(7, "number");
+ check(7n, "bigint");
+ },
+
+ /*
+ * Checks that replacing the 'constructor' property of the subclass with
+ * undefined does not affect the returned new instance.
+ */
+ checkSubclassConstructorUndefined(construct, constructArgs, method, methodArgs, resultAssertions) {
+ let called = 0;
+
+ class MySubclass extends construct {
+ constructor() {
+ ++called;
+ super(...constructArgs);
+ }
+ }
+
+ const instance = new MySubclass();
+ assert.sameValue(called, 1);
+
+ MySubclass.prototype.constructor = undefined;
+
+ const result = instance[method](...methodArgs);
+ assert.sameValue(called, 1);
+ assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
+ resultAssertions(result);
+ },
+
+ /*
+ * Checks that making the 'constructor' property of the instance throw when
+ * called does not affect the returned new instance.
+ */
+ checkSubclassConstructorThrows(construct, constructArgs, method, methodArgs, resultAssertions) {
+ function CustomError() {}
+ const instance = new construct(...constructArgs);
+ Object.defineProperty(instance, "constructor", {
+ get() {
+ throw new CustomError();
+ }
+ });
+ const result = instance[method](...methodArgs);
+ assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
+ resultAssertions(result);
+ },
+
+ /*
+ * Checks that when subclassing, the subclass constructor is not called by
+ * the method under test.
+ */
+ checkSubclassConstructorNotCalled(construct, constructArgs, method, methodArgs, resultAssertions) {
+ let called = 0;
+
+ class MySubclass extends construct {
+ constructor() {
+ ++called;
+ super(...constructArgs);
+ }
+ }
+
+ const instance = new MySubclass();
+ assert.sameValue(called, 1);
+
+ const result = instance[method](...methodArgs);
+ assert.sameValue(called, 1);
+ assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
+ resultAssertions(result);
+ },
+
+ /*
+ * Check that the constructor's @@species property is ignored when it's a
+ * constructor that returns a non-object value.
+ */
+ checkSubclassSpeciesInvalidResult(construct, constructArgs, method, methodArgs, resultAssertions) {
+ function check(value, description) {
+ const instance = new construct(...constructArgs);
+ instance.constructor = {
+ [Symbol.species]: function() {
+ return value;
+ },
+ };
+ const result = instance[method](...methodArgs);
+ assert.sameValue(Object.getPrototypeOf(result), construct.prototype, description);
+ resultAssertions(result);
+ }
+
+ check(undefined, "undefined");
+ check(null, "null");
+ check(true, "true");
+ check("test", "string");
+ check(Symbol(), "Symbol");
+ check(7, "number");
+ check(7n, "bigint");
+ check({}, "plain object");
+ },
+
+ /*
+ * Check that the constructor's @@species property is ignored when it's not a
+ * constructor.
+ */
+ checkSubclassSpeciesNotAConstructor(construct, constructArgs, method, methodArgs, resultAssertions) {
+ function check(value, description) {
+ const instance = new construct(...constructArgs);
+ instance.constructor = {
+ [Symbol.species]: value,
+ };
+ const result = instance[method](...methodArgs);
+ assert.sameValue(Object.getPrototypeOf(result), construct.prototype, description);
+ resultAssertions(result);
+ }
+
+ check(true, "true");
+ check("test", "string");
+ check(Symbol(), "Symbol");
+ check(7, "number");
+ check(7n, "bigint");
+ check({}, "plain object");
+ },
+
+ /*
+ * Check that the constructor's @@species property is ignored when it's null.
+ */
+ checkSubclassSpeciesNull(construct, constructArgs, method, methodArgs, resultAssertions) {
+ let called = 0;
+
+ class MySubclass extends construct {
+ constructor() {
+ ++called;
+ super(...constructArgs);
+ }
+ }
+
+ const instance = new MySubclass();
+ assert.sameValue(called, 1);
+
+ MySubclass.prototype.constructor = {
+ [Symbol.species]: null,
+ };
+
+ const result = instance[method](...methodArgs);
+ assert.sameValue(called, 1);
+ assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
+ resultAssertions(result);
+ },
+
+ /*
+ * Check that the constructor's @@species property is ignored when it's
+ * undefined.
+ */
+ checkSubclassSpeciesUndefined(construct, constructArgs, method, methodArgs, resultAssertions) {
+ let called = 0;
+
+ class MySubclass extends construct {
+ constructor() {
+ ++called;
+ super(...constructArgs);
+ }
+ }
+
+ const instance = new MySubclass();
+ assert.sameValue(called, 1);
+
+ MySubclass.prototype.constructor = {
+ [Symbol.species]: undefined,
+ };
+
+ const result = instance[method](...methodArgs);
+ assert.sameValue(called, 1);
+ assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
+ resultAssertions(result);
+ },
+
+ /*
+ * Check that the constructor's @@species property is ignored when it throws,
+ * i.e. it is not called at all.
+ */
+ checkSubclassSpeciesThrows(construct, constructArgs, method, methodArgs, resultAssertions) {
+ function CustomError() {}
+
+ const instance = new construct(...constructArgs);
+ instance.constructor = {
+ get [Symbol.species]() {
+ throw new CustomError();
+ },
+ };
+
+ const result = instance[method](...methodArgs);
+ assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
+ },
+
+ /*
+ * checkSubclassingIgnoredStatic(construct, method, methodArgs, resultAssertions):
+ *
+ * Static methods of Temporal classes that return a new instance of the class,
+ * must not use the this-value as a constructor. This helper runs tests to
+ * ensure this.
+ *
+ * construct[method](...methodArgs) is the static method call under test, and
+ * must yield a valid instance of the Temporal class, not a subclass. See
+ * below for the individual tests that this runs.
+ * resultAssertions() is a function that performs additional assertions on the
+ * instance returned by the method under test.
+ */
+ checkSubclassingIgnoredStatic(...args) {
+ this.checkStaticInvalidReceiver(...args);
+ this.checkStaticReceiverNotCalled(...args);
+ this.checkThisValueNotCalled(...args);
+ },
+
+ /*
+ * Check that calling the static method with a receiver that's not callable,
+ * still calls the intrinsic constructor.
+ */
+ checkStaticInvalidReceiver(construct, method, methodArgs, resultAssertions) {
+ function check(value, description) {
+ const result = construct[method].apply(value, methodArgs);
+ assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
+ resultAssertions(result);
+ }
+
+ check(undefined, "undefined");
+ check(null, "null");
+ check(true, "true");
+ check("test", "string");
+ check(Symbol(), "symbol");
+ check(7, "number");
+ check(7n, "bigint");
+ check({}, "Non-callable object");
+ },
+
+ /*
+ * Check that calling the static method with a receiver that returns a value
+ * that's not callable, still calls the intrinsic constructor.
+ */
+ checkStaticReceiverNotCalled(construct, method, methodArgs, resultAssertions) {
+ function check(value, description) {
+ const receiver = function () {
+ return value;
+ };
+ const result = construct[method].apply(receiver, methodArgs);
+ assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
+ resultAssertions(result);
+ }
+
+ check(undefined, "undefined");
+ check(null, "null");
+ check(true, "true");
+ check("test", "string");
+ check(Symbol(), "symbol");
+ check(7, "number");
+ check(7n, "bigint");
+ check({}, "Non-callable object");
+ },
+
+ /*
+ * Check that the receiver isn't called.
+ */
+ checkThisValueNotCalled(construct, method, methodArgs, resultAssertions) {
+ let called = false;
+
+ class MySubclass extends construct {
+ constructor(...args) {
+ called = true;
+ super(...args);
+ }
+ }
+
+ const result = MySubclass[method](...methodArgs);
+ assert.sameValue(called, false);
+ assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
+ resultAssertions(result);
+ },
+
+ /*
+ * Check that any iterable returned from a custom time zone's
+ * getPossibleInstantsFor() method is exhausted.
+ * The custom time zone object is passed in to func().
+ * expected is an array of strings representing the expected calls to the
+ * getPossibleInstantsFor() method. The PlainDateTimes that it is called with,
+ * are compared (using their toString() results) with the array.
+ */
+ checkTimeZonePossibleInstantsIterable(func, expected) {
+ // A custom time zone that returns an iterable instead of an array from its
+ // getPossibleInstantsFor() method, and for testing purposes skips
+ // 00:00-01:00 UTC on January 1, 2030, and repeats 00:00-01:00 UTC+1 on
+ // January 3, 2030. Otherwise identical to the UTC time zone.
+ class TimeZonePossibleInstantsIterable extends Temporal.TimeZone {
+ constructor() {
+ super("UTC");
+ this.getPossibleInstantsForCallCount = 0;
+ this.getPossibleInstantsForCalledWith = [];
+ this.getPossibleInstantsForReturns = [];
+ this.iteratorExhausted = [];
+ }
+
+ toString() {
+ return "Custom/Iterable";
+ }
+
+ getOffsetNanosecondsFor(instant) {
+ if (Temporal.Instant.compare(instant, "2030-01-01T00:00Z") >= 0 &&
+ Temporal.Instant.compare(instant, "2030-01-03T01:00Z") < 0) {
+ return 3600_000_000_000;
+ } else {
+ return 0;
+ }
+ }
+
+ getPossibleInstantsFor(dateTime) {
+ this.getPossibleInstantsForCallCount++;
+ this.getPossibleInstantsForCalledWith.push(dateTime);
+
+ // Fake DST transition
+ let retval = super.getPossibleInstantsFor(dateTime);
+ if (dateTime.toPlainDate().equals("2030-01-01") && dateTime.hour === 0) {
+ retval = [];
+ } else if (dateTime.toPlainDate().equals("2030-01-03") && dateTime.hour === 0) {
+ retval.push(retval[0].subtract({ hours: 1 }));
+ } else if (dateTime.year === 2030 && dateTime.month === 1 && dateTime.day >= 1 && dateTime.day <= 2) {
+ retval[0] = retval[0].subtract({ hours: 1 });
+ }
+
+ this.getPossibleInstantsForReturns.push(retval);
+ this.iteratorExhausted.push(false);
+ return {
+ callIndex: this.getPossibleInstantsForCallCount - 1,
+ timeZone: this,
+ *[Symbol.iterator]() {
+ yield* this.timeZone.getPossibleInstantsForReturns[this.callIndex];
+ this.timeZone.iteratorExhausted[this.callIndex] = true;
+ },
+ };
+ }
+ }
+
+ const timeZone = new TimeZonePossibleInstantsIterable();
+ func(timeZone);
+
+ assert.sameValue(timeZone.getPossibleInstantsForCallCount, expected.length, "getPossibleInstantsFor() method called correct number of times");
+
+ for (let index = 0; index < expected.length; index++) {
+ assert.sameValue(timeZone.getPossibleInstantsForCalledWith[index].toString(), expected[index], "getPossibleInstantsFor() called with expected PlainDateTime");
+ assert(timeZone.iteratorExhausted[index], "iterated through the whole iterable");
+ }
+ },
+
+ /*
+ * Check that any calendar-carrying Temporal object has its [[Calendar]]
+ * internal slot read by ToTemporalCalendar, and does not fetch the calendar
+ * by calling getters.
+ * The custom calendar object is passed in to func() so that it can do its
+ * own additional assertions involving the calendar if necessary. (Sometimes
+ * there is nothing to assert as the calendar isn't stored anywhere that can
+ * be asserted about.)
+ */
+ checkToTemporalCalendarFastPath(func) {
+ class CalendarFastPathCheck extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+
+ dateFromFields(...args) {
+ return super.dateFromFields(...args).withCalendar(this);
+ }
+
+ monthDayFromFields(...args) {
+ const { isoYear, isoMonth, isoDay } = super.monthDayFromFields(...args).getISOFields();
+ return new Temporal.PlainMonthDay(isoMonth, isoDay, this, isoYear);
+ }
+
+ yearMonthFromFields(...args) {
+ const { isoYear, isoMonth, isoDay } = super.yearMonthFromFields(...args).getISOFields();
+ return new Temporal.PlainYearMonth(isoYear, isoMonth, this, isoDay);
+ }
+
+ toString() {
+ return "fast-path-check";
+ }
+ }
+ const calendar = new CalendarFastPathCheck();
+
+ const plainDate = new Temporal.PlainDate(2000, 5, 2, calendar);
+ const plainDateTime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+ const plainMonthDay = new Temporal.PlainMonthDay(5, 2, calendar);
+ const plainYearMonth = new Temporal.PlainYearMonth(2000, 5, calendar);
+ const zonedDateTime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar);
+
+ [plainDate, plainDateTime, plainMonthDay, plainYearMonth, zonedDateTime].forEach((temporalObject) => {
+ const actual = [];
+ const expected = [];
+
+ Object.defineProperty(temporalObject, "calendar", {
+ get() {
+ actual.push("get calendar");
+ return calendar;
+ },
+ });
+
+ func(temporalObject, calendar);
+ assert.compareArray(actual, expected, "calendar getter not called");
+ });
+ },
+
+ checkToTemporalInstantFastPath(func) {
+ const actual = [];
+ const expected = [];
+
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+ Object.defineProperty(datetime, 'toString', {
+ get() {
+ actual.push("get toString");
+ return function (options) {
+ actual.push("call toString");
+ return Temporal.ZonedDateTime.prototype.toString.call(this, options);
+ };
+ },
+ });
+
+ func(datetime);
+ assert.compareArray(actual, expected, "toString not called");
+ },
+
+ checkToTemporalPlainDateTimeFastPath(func) {
+ const actual = [];
+ const expected = [];
+
+ const calendar = new Temporal.Calendar("iso8601");
+ const date = new Temporal.PlainDate(2000, 5, 2, calendar);
+ const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.PlainDate.prototype);
+ ["year", "month", "monthCode", "day"].forEach((property) => {
+ Object.defineProperty(date, property, {
+ get() {
+ actual.push(`get ${formatPropertyName(property)}`);
+ const value = prototypeDescrs[property].get.call(this);
+ return TemporalHelpers.toPrimitiveObserver(actual, value, property);
+ },
+ });
+ });
+ ["hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((property) => {
+ Object.defineProperty(date, property, {
+ get() {
+ actual.push(`get ${formatPropertyName(property)}`);
+ return undefined;
+ },
+ });
+ });
+ Object.defineProperty(date, "calendar", {
+ get() {
+ actual.push("get calendar");
+ return calendar;
+ },
+ });
+
+ func(date, calendar);
+ assert.compareArray(actual, expected, "property getters not called");
+ },
+
+ /*
+ * A custom calendar used in prototype pollution checks. Verifies that the
+ * fromFields methods are always called with a null-prototype fields object.
+ */
+ calendarCheckFieldsPrototypePollution() {
+ class CalendarCheckFieldsPrototypePollution extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ this.dateFromFieldsCallCount = 0;
+ this.yearMonthFromFieldsCallCount = 0;
+ this.monthDayFromFieldsCallCount = 0;
+ }
+
+ // toString must remain "iso8601", so that some methods don't throw due to
+ // incompatible calendars
+
+ dateFromFields(fields, options = {}) {
+ this.dateFromFieldsCallCount++;
+ assert.sameValue(Object.getPrototypeOf(fields), null, "dateFromFields should be called with null-prototype fields object");
+ return super.dateFromFields(fields, options);
+ }
+
+ yearMonthFromFields(fields, options = {}) {
+ this.yearMonthFromFieldsCallCount++;
+ assert.sameValue(Object.getPrototypeOf(fields), null, "yearMonthFromFields should be called with null-prototype fields object");
+ return super.yearMonthFromFields(fields, options);
+ }
+
+ monthDayFromFields(fields, options = {}) {
+ this.monthDayFromFieldsCallCount++;
+ assert.sameValue(Object.getPrototypeOf(fields), null, "monthDayFromFields should be called with null-prototype fields object");
+ return super.monthDayFromFields(fields, options);
+ }
+ }
+
+ return new CalendarCheckFieldsPrototypePollution();
+ },
+
+ /*
+ * A custom calendar used in prototype pollution checks. Verifies that the
+ * mergeFields() method is always called with null-prototype fields objects.
+ */
+ calendarCheckMergeFieldsPrototypePollution() {
+ class CalendarCheckMergeFieldsPrototypePollution extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ this.mergeFieldsCallCount = 0;
+ }
+
+ toString() {
+ return "merge-fields-null-proto";
+ }
+
+ mergeFields(fields, additionalFields) {
+ this.mergeFieldsCallCount++;
+ assert.sameValue(Object.getPrototypeOf(fields), null, "mergeFields should be called with null-prototype fields object (first argument)");
+ assert.sameValue(Object.getPrototypeOf(additionalFields), null, "mergeFields should be called with null-prototype fields object (second argument)");
+ return super.mergeFields(fields, additionalFields);
+ }
+ }
+
+ return new CalendarCheckMergeFieldsPrototypePollution();
+ },
+
+ /*
+ * A custom calendar used in prototype pollution checks. Verifies that methods
+ * are always called with a null-prototype options object.
+ */
+ calendarCheckOptionsPrototypePollution() {
+ class CalendarCheckOptionsPrototypePollution extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ this.yearMonthFromFieldsCallCount = 0;
+ this.dateUntilCallCount = 0;
+ }
+
+ toString() {
+ return "options-null-proto";
+ }
+
+ yearMonthFromFields(fields, options) {
+ this.yearMonthFromFieldsCallCount++;
+ assert.sameValue(Object.getPrototypeOf(options), null, "yearMonthFromFields should be called with null-prototype options");
+ return super.yearMonthFromFields(fields, options);
+ }
+
+ dateUntil(one, two, options) {
+ this.dateUntilCallCount++;
+ assert.sameValue(Object.getPrototypeOf(options), null, "dateUntil should be called with null-prototype options");
+ return super.dateUntil(one, two, options);
+ }
+ }
+
+ return new CalendarCheckOptionsPrototypePollution();
+ },
+
+ /*
+ * A custom calendar that asserts its dateAdd() method is called with the
+ * options parameter having the value undefined.
+ */
+ calendarDateAddUndefinedOptions() {
+ class CalendarDateAddUndefinedOptions extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ this.dateAddCallCount = 0;
+ }
+
+ toString() {
+ return "dateadd-undef-options";
+ }
+
+ dateAdd(date, duration, options) {
+ this.dateAddCallCount++;
+ assert.sameValue(options, undefined, "dateAdd shouldn't be called with options");
+ return super.dateAdd(date, duration, options);
+ }
+ }
+ return new CalendarDateAddUndefinedOptions();
+ },
+
+ /*
+ * A custom calendar that asserts its dateAdd() method is called with a
+ * PlainDate instance. Optionally, it also asserts that the PlainDate instance
+ * is the specific object `this.specificPlainDate`, if it is set by the
+ * calling code.
+ */
+ calendarDateAddPlainDateInstance() {
+ class CalendarDateAddPlainDateInstance extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ this.dateAddCallCount = 0;
+ this.specificPlainDate = undefined;
+ }
+
+ toString() {
+ return "dateadd-plain-date-instance";
+ }
+
+ dateFromFields(...args) {
+ return super.dateFromFields(...args).withCalendar(this);
+ }
+
+ dateAdd(date, duration, options) {
+ this.dateAddCallCount++;
+ assert(date instanceof Temporal.PlainDate, "dateAdd() should be called with a PlainDate instance");
+ if (this.dateAddCallCount === 1 && this.specificPlainDate) {
+ assert.sameValue(date, this.specificPlainDate, `dateAdd() should be called first with the specific PlainDate instance ${this.specificPlainDate}`);
+ }
+ return super.dateAdd(date, duration, options).withCalendar(this);
+ }
+ }
+ return new CalendarDateAddPlainDateInstance();
+ },
+
+ /*
+ * A custom calendar that returns an iterable instead of an array from its
+ * fields() method, otherwise identical to the ISO calendar.
+ */
+ calendarFieldsIterable() {
+ class CalendarFieldsIterable extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ this.fieldsCallCount = 0;
+ this.fieldsCalledWith = [];
+ this.iteratorExhausted = [];
+ }
+
+ toString() {
+ return "fields-iterable";
+ }
+
+ fields(fieldNames) {
+ this.fieldsCallCount++;
+ this.fieldsCalledWith.push(fieldNames.slice());
+ this.iteratorExhausted.push(false);
+ return {
+ callIndex: this.fieldsCallCount - 1,
+ calendar: this,
+ *[Symbol.iterator]() {
+ yield* this.calendar.fieldsCalledWith[this.callIndex];
+ this.calendar.iteratorExhausted[this.callIndex] = true;
+ },
+ };
+ }
+ }
+ return new CalendarFieldsIterable();
+ },
+
+ /*
+ * A custom calendar that asserts its ...FromFields() methods are called with
+ * the options parameter having the value undefined.
+ */
+ calendarFromFieldsUndefinedOptions() {
+ class CalendarFromFieldsUndefinedOptions extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ this.dateFromFieldsCallCount = 0;
+ this.monthDayFromFieldsCallCount = 0;
+ this.yearMonthFromFieldsCallCount = 0;
+ }
+
+ toString() {
+ return "from-fields-undef-options";
+ }
+
+ dateFromFields(fields, options) {
+ this.dateFromFieldsCallCount++;
+ assert.sameValue(options, undefined, "dateFromFields shouldn't be called with options");
+ return super.dateFromFields(fields, options);
+ }
+
+ yearMonthFromFields(fields, options) {
+ this.yearMonthFromFieldsCallCount++;
+ assert.sameValue(options, undefined, "yearMonthFromFields shouldn't be called with options");
+ return super.yearMonthFromFields(fields, options);
+ }
+
+ monthDayFromFields(fields, options) {
+ this.monthDayFromFieldsCallCount++;
+ assert.sameValue(options, undefined, "monthDayFromFields shouldn't be called with options");
+ return super.monthDayFromFields(fields, options);
+ }
+ }
+ return new CalendarFromFieldsUndefinedOptions();
+ },
+
+ /*
+ * A custom calendar that modifies the fields object passed in to
+ * dateFromFields, sabotaging its time properties.
+ */
+ calendarMakeInfinityTime() {
+ class CalendarMakeInfinityTime extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+
+ dateFromFields(fields, options) {
+ const retval = super.dateFromFields(fields, options);
+ fields.hour = Infinity;
+ fields.minute = Infinity;
+ fields.second = Infinity;
+ fields.millisecond = Infinity;
+ fields.microsecond = Infinity;
+ fields.nanosecond = Infinity;
+ return retval;
+ }
+ }
+ return new CalendarMakeInfinityTime();
+ },
+
+ /*
+ * A custom calendar that defines getters on the fields object passed into
+ * dateFromFields that throw, sabotaging its time properties.
+ */
+ calendarMakeInvalidGettersTime() {
+ class CalendarMakeInvalidGettersTime extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+
+ dateFromFields(fields, options) {
+ const retval = super.dateFromFields(fields, options);
+ const throwingDescriptor = {
+ get() {
+ throw new Test262Error("reading a sabotaged time field");
+ },
+ };
+ Object.defineProperties(fields, {
+ hour: throwingDescriptor,
+ minute: throwingDescriptor,
+ second: throwingDescriptor,
+ millisecond: throwingDescriptor,
+ microsecond: throwingDescriptor,
+ nanosecond: throwingDescriptor,
+ });
+ return retval;
+ }
+ }
+ return new CalendarMakeInvalidGettersTime();
+ },
+
+ /*
+ * A custom calendar whose mergeFields() method returns a proxy object with
+ * all of its Get and HasProperty operations observable, as well as adding a
+ * "shouldNotBeCopied": true property.
+ */
+ calendarMergeFieldsGetters() {
+ class CalendarMergeFieldsGetters extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ this.mergeFieldsReturnOperations = [];
+ }
+
+ toString() {
+ return "merge-fields-getters";
+ }
+
+ dateFromFields(fields, options) {
+ assert.sameValue(fields.shouldNotBeCopied, undefined, "extra fields should not be copied");
+ return super.dateFromFields(fields, options);
+ }
+
+ yearMonthFromFields(fields, options) {
+ assert.sameValue(fields.shouldNotBeCopied, undefined, "extra fields should not be copied");
+ return super.yearMonthFromFields(fields, options);
+ }
+
+ monthDayFromFields(fields, options) {
+ assert.sameValue(fields.shouldNotBeCopied, undefined, "extra fields should not be copied");
+ return super.monthDayFromFields(fields, options);
+ }
+
+ mergeFields(fields, additionalFields) {
+ const retval = super.mergeFields(fields, additionalFields);
+ retval._calendar = this;
+ retval.shouldNotBeCopied = true;
+ return new Proxy(retval, {
+ get(target, key) {
+ target._calendar.mergeFieldsReturnOperations.push(`get ${key}`);
+ const result = target[key];
+ if (result === undefined) {
+ return undefined;
+ }
+ return TemporalHelpers.toPrimitiveObserver(target._calendar.mergeFieldsReturnOperations, result, key);
+ },
+ has(target, key) {
+ target._calendar.mergeFieldsReturnOperations.push(`has ${key}`);
+ return key in target;
+ },
+ });
+ }
+ }
+ return new CalendarMergeFieldsGetters();
+ },
+
+ /*
+ * A custom calendar whose mergeFields() method returns a primitive value,
+ * given by @primitive, and which records the number of calls made to its
+ * dateFromFields(), yearMonthFromFields(), and monthDayFromFields() methods.
+ */
+ calendarMergeFieldsReturnsPrimitive(primitive) {
+ class CalendarMergeFieldsPrimitive extends Temporal.Calendar {
+ constructor(mergeFieldsReturnValue) {
+ super("iso8601");
+ this._mergeFieldsReturnValue = mergeFieldsReturnValue;
+ this.dateFromFieldsCallCount = 0;
+ this.monthDayFromFieldsCallCount = 0;
+ this.yearMonthFromFieldsCallCount = 0;
+ }
+
+ toString() {
+ return "merge-fields-primitive";
+ }
+
+ dateFromFields(fields, options) {
+ this.dateFromFieldsCallCount++;
+ return super.dateFromFields(fields, options);
+ }
+
+ yearMonthFromFields(fields, options) {
+ this.yearMonthFromFieldsCallCount++;
+ return super.yearMonthFromFields(fields, options);
+ }
+
+ monthDayFromFields(fields, options) {
+ this.monthDayFromFieldsCallCount++;
+ return super.monthDayFromFields(fields, options);
+ }
+
+ mergeFields() {
+ return this._mergeFieldsReturnValue;
+ }
+ }
+ return new CalendarMergeFieldsPrimitive(primitive);
+ },
+
+ /*
+ * A custom calendar whose fields() method returns the same value as the
+ * iso8601 calendar, with the addition of extraFields provided as parameter.
+ */
+ calendarWithExtraFields(fields) {
+ class CalendarWithExtraFields extends Temporal.Calendar {
+ constructor(extraFields) {
+ super("iso8601");
+ this._extraFields = extraFields;
+ }
+
+ fields(fieldNames) {
+ return super.fields(fieldNames).concat(this._extraFields);
+ }
+ }
+
+ return new CalendarWithExtraFields(fields);
+ },
+
+ /*
+ * crossDateLineTimeZone():
+ *
+ * This returns an instance of a custom time zone class that implements one
+ * single transition where the time zone moves from one side of the
+ * International Date Line to the other, for the purpose of testing time zone
+ * calculations without depending on system time zone data.
+ *
+ * The transition occurs at epoch second 1325239200 and goes from offset
+ * -10:00 to +14:00. In other words, the time zone skips the whole calendar
+ * day of 2011-12-30. This is the same as the real-life transition in the
+ * Pacific/Apia time zone.
+ */
+ crossDateLineTimeZone() {
+ const { compare } = Temporal.PlainDate;
+ const skippedDay = new Temporal.PlainDate(2011, 12, 30);
+ const transitionEpoch = 1325239200_000_000_000n;
+ const beforeOffset = new Temporal.TimeZone("-10:00");
+ const afterOffset = new Temporal.TimeZone("+14:00");
+
+ class CrossDateLineTimeZone extends Temporal.TimeZone {
+ constructor() {
+ super("+14:00");
+ }
+
+ getOffsetNanosecondsFor(instant) {
+ if (instant.epochNanoseconds < transitionEpoch) {
+ return beforeOffset.getOffsetNanosecondsFor(instant);
+ }
+ return afterOffset.getOffsetNanosecondsFor(instant);
+ }
+
+ getPossibleInstantsFor(datetime) {
+ const comparison = compare(datetime.toPlainDate(), skippedDay);
+ if (comparison === 0) {
+ return [];
+ }
+ if (comparison < 0) {
+ return [beforeOffset.getInstantFor(datetime)];
+ }
+ return [afterOffset.getInstantFor(datetime)];
+ }
+
+ getPreviousTransition(instant) {
+ if (instant.epochNanoseconds > transitionEpoch) return new Temporal.Instant(transitionEpoch);
+ return null;
+ }
+
+ getNextTransition(instant) {
+ if (instant.epochNanoseconds < transitionEpoch) return new Temporal.Instant(transitionEpoch);
+ return null;
+ }
+
+ toString() {
+ return "Custom/Date_Line";
+ }
+ }
+ return new CrossDateLineTimeZone();
+ },
+
+ /*
+ * observeProperty(calls, object, propertyName, value):
+ *
+ * Defines an own property @object.@propertyName with value @value, that
+ * will log any calls to its accessors to the array @calls.
+ */
+ observeProperty(calls, object, propertyName, value, objectName = "") {
+ Object.defineProperty(object, propertyName, {
+ get() {
+ calls.push(`get ${formatPropertyName(propertyName, objectName)}`);
+ return value;
+ },
+ set(v) {
+ calls.push(`set ${formatPropertyName(propertyName, objectName)}`);
+ }
+ });
+ },
+
+ /*
+ * observeMethod(calls, object, propertyName, value):
+ *
+ * Defines an own property @object.@propertyName with value @value, that
+ * will log any calls of @value to the array @calls.
+ */
+ observeMethod(calls, object, propertyName, objectName = "") {
+ const method = object[propertyName];
+ object[propertyName] = function () {
+ calls.push(`call ${formatPropertyName(propertyName, objectName)}`);
+ return method.apply(object, arguments);
+ };
+ },
+
+ /*
+ * Used for substituteMethod to indicate default behavior instead of a
+ * substituted value
+ */
+ SUBSTITUTE_SKIP: SKIP_SYMBOL,
+
+ /*
+ * substituteMethod(object, propertyName, values):
+ *
+ * Defines an own property @object.@propertyName that will, for each
+ * subsequent call to the method previously defined as
+ * @object.@propertyName:
+ * - Call the method, if no more values remain
+ * - Call the method, if the value in @values for the corresponding call
+ * is SUBSTITUTE_SKIP
+ * - Otherwise, return the corresponding value in @value
+ */
+ substituteMethod(object, propertyName, values) {
+ let calls = 0;
+ const method = object[propertyName];
+ object[propertyName] = function () {
+ if (calls >= values.length) {
+ return method.apply(object, arguments);
+ } else if (values[calls] === SKIP_SYMBOL) {
+ calls++;
+ return method.apply(object, arguments);
+ } else {
+ return values[calls++];
+ }
+ };
+ },
+
+ /*
+ * calendarObserver:
+ * A custom calendar that behaves exactly like the ISO 8601 calendar but
+ * tracks calls to any of its methods, and Get/Has operations on its
+ * properties, by appending messages to an array. This is for the purpose of
+ * testing order of operations that are observable from user code.
+ * objectName is used in the log.
+ */
+ calendarObserver(calls, objectName, methodOverrides = {}) {
+ function removeExtraHasPropertyChecks(objectName, calls) {
+ // Inserting the tracking calendar into the return values of methods
+ // that we chain up into the ISO calendar for, causes extra HasProperty
+ // checks, which we observe. This removes them so that we don't leak
+ // implementation details of the helper into the test code.
+ assert.sameValue(calls.pop(), `has ${objectName}.yearOfWeek`);
+ assert.sameValue(calls.pop(), `has ${objectName}.yearMonthFromFields`);
+ assert.sameValue(calls.pop(), `has ${objectName}.year`);
+ assert.sameValue(calls.pop(), `has ${objectName}.weekOfYear`);
+ assert.sameValue(calls.pop(), `has ${objectName}.monthsInYear`);
+ assert.sameValue(calls.pop(), `has ${objectName}.monthDayFromFields`);
+ assert.sameValue(calls.pop(), `has ${objectName}.monthCode`);
+ assert.sameValue(calls.pop(), `has ${objectName}.month`);
+ assert.sameValue(calls.pop(), `has ${objectName}.mergeFields`);
+ assert.sameValue(calls.pop(), `has ${objectName}.inLeapYear`);
+ assert.sameValue(calls.pop(), `has ${objectName}.id`);
+ assert.sameValue(calls.pop(), `has ${objectName}.fields`);
+ assert.sameValue(calls.pop(), `has ${objectName}.daysInYear`);
+ assert.sameValue(calls.pop(), `has ${objectName}.daysInWeek`);
+ assert.sameValue(calls.pop(), `has ${objectName}.daysInMonth`);
+ assert.sameValue(calls.pop(), `has ${objectName}.dayOfYear`);
+ assert.sameValue(calls.pop(), `has ${objectName}.dayOfWeek`);
+ assert.sameValue(calls.pop(), `has ${objectName}.day`);
+ assert.sameValue(calls.pop(), `has ${objectName}.dateUntil`);
+ assert.sameValue(calls.pop(), `has ${objectName}.dateFromFields`);
+ assert.sameValue(calls.pop(), `has ${objectName}.dateAdd`);
+ }
+
+ const iso8601 = new Temporal.Calendar("iso8601");
+ const trackingMethods = {
+ dateFromFields(...args) {
+ calls.push(`call ${objectName}.dateFromFields`);
+ if ('dateFromFields' in methodOverrides) {
+ const value = methodOverrides.dateFromFields;
+ return typeof value === "function" ? value(...args) : value;
+ }
+ const originalResult = iso8601.dateFromFields(...args);
+ // Replace the calendar in the result with the call-tracking calendar
+ const {isoYear, isoMonth, isoDay} = originalResult.getISOFields();
+ const result = new Temporal.PlainDate(isoYear, isoMonth, isoDay, this);
+ removeExtraHasPropertyChecks(objectName, calls);
+ return result;
+ },
+ yearMonthFromFields(...args) {
+ calls.push(`call ${objectName}.yearMonthFromFields`);
+ if ('yearMonthFromFields' in methodOverrides) {
+ const value = methodOverrides.yearMonthFromFields;
+ return typeof value === "function" ? value(...args) : value;
+ }
+ const originalResult = iso8601.yearMonthFromFields(...args);
+ // Replace the calendar in the result with the call-tracking calendar
+ const {isoYear, isoMonth, isoDay} = originalResult.getISOFields();
+ const result = new Temporal.PlainYearMonth(isoYear, isoMonth, this, isoDay);
+ removeExtraHasPropertyChecks(objectName, calls);
+ return result;
+ },
+ monthDayFromFields(...args) {
+ calls.push(`call ${objectName}.monthDayFromFields`);
+ if ('monthDayFromFields' in methodOverrides) {
+ const value = methodOverrides.monthDayFromFields;
+ return typeof value === "function" ? value(...args) : value;
+ }
+ const originalResult = iso8601.monthDayFromFields(...args);
+ // Replace the calendar in the result with the call-tracking calendar
+ const {isoYear, isoMonth, isoDay} = originalResult.getISOFields();
+ const result = new Temporal.PlainMonthDay(isoMonth, isoDay, this, isoYear);
+ removeExtraHasPropertyChecks(objectName, calls);
+ return result;
+ },
+ dateAdd(...args) {
+ calls.push(`call ${objectName}.dateAdd`);
+ if ('dateAdd' in methodOverrides) {
+ const value = methodOverrides.dateAdd;
+ return typeof value === "function" ? value(...args) : value;
+ }
+ const originalResult = iso8601.dateAdd(...args);
+ const {isoYear, isoMonth, isoDay} = originalResult.getISOFields();
+ const result = new Temporal.PlainDate(isoYear, isoMonth, isoDay, this);
+ removeExtraHasPropertyChecks(objectName, calls);
+ return result;
+ },
+ id: "iso8601",
+ };
+ // Automatically generate the other methods that don't need any custom code
+ [
+ "dateUntil",
+ "day",
+ "dayOfWeek",
+ "dayOfYear",
+ "daysInMonth",
+ "daysInWeek",
+ "daysInYear",
+ "era",
+ "eraYear",
+ "fields",
+ "inLeapYear",
+ "mergeFields",
+ "month",
+ "monthCode",
+ "monthsInYear",
+ "toString",
+ "weekOfYear",
+ "year",
+ "yearOfWeek",
+ ].forEach((methodName) => {
+ trackingMethods[methodName] = function (...args) {
+ calls.push(`call ${formatPropertyName(methodName, objectName)}`);
+ if (methodName in methodOverrides) {
+ const value = methodOverrides[methodName];
+ return typeof value === "function" ? value(...args) : value;
+ }
+ return iso8601[methodName](...args);
+ };
+ });
+ return new Proxy(trackingMethods, {
+ get(target, key, receiver) {
+ const result = Reflect.get(target, key, receiver);
+ calls.push(`get ${formatPropertyName(key, objectName)}`);
+ return result;
+ },
+ has(target, key) {
+ calls.push(`has ${formatPropertyName(key, objectName)}`);
+ return Reflect.has(target, key);
+ },
+ });
+ },
+
+ /*
+ * A custom calendar that does not allow any of its methods to be called, for
+ * the purpose of asserting that a particular operation does not call into
+ * user code.
+ */
+ calendarThrowEverything() {
+ class CalendarThrowEverything extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ toString() {
+ TemporalHelpers.assertUnreachable("toString should not be called");
+ }
+ dateFromFields() {
+ TemporalHelpers.assertUnreachable("dateFromFields should not be called");
+ }
+ yearMonthFromFields() {
+ TemporalHelpers.assertUnreachable("yearMonthFromFields should not be called");
+ }
+ monthDayFromFields() {
+ TemporalHelpers.assertUnreachable("monthDayFromFields should not be called");
+ }
+ dateAdd() {
+ TemporalHelpers.assertUnreachable("dateAdd should not be called");
+ }
+ dateUntil() {
+ TemporalHelpers.assertUnreachable("dateUntil should not be called");
+ }
+ era() {
+ TemporalHelpers.assertUnreachable("era should not be called");
+ }
+ eraYear() {
+ TemporalHelpers.assertUnreachable("eraYear should not be called");
+ }
+ year() {
+ TemporalHelpers.assertUnreachable("year should not be called");
+ }
+ month() {
+ TemporalHelpers.assertUnreachable("month should not be called");
+ }
+ monthCode() {
+ TemporalHelpers.assertUnreachable("monthCode should not be called");
+ }
+ day() {
+ TemporalHelpers.assertUnreachable("day should not be called");
+ }
+ fields() {
+ TemporalHelpers.assertUnreachable("fields should not be called");
+ }
+ mergeFields() {
+ TemporalHelpers.assertUnreachable("mergeFields should not be called");
+ }
+ }
+
+ return new CalendarThrowEverything();
+ },
+
+ /*
+ * oneShiftTimeZone(shiftInstant, shiftNanoseconds):
+ *
+ * In the case of a spring-forward time zone offset transition (skipped time),
+ * and disambiguation === 'earlier', BuiltinTimeZoneGetInstantFor subtracts a
+ * negative number of nanoseconds from a PlainDateTime, which should balance
+ * with the microseconds field.
+ *
+ * This returns an instance of a custom time zone class which skips a length
+ * of time equal to shiftNanoseconds (a number), at the Temporal.Instant
+ * shiftInstant. Before shiftInstant, it's identical to UTC, and after
+ * shiftInstant it's a constant-offset time zone.
+ *
+ * It provides a getPossibleInstantsForCalledWith member which is an array
+ * with the result of calling toString() on any PlainDateTimes passed to
+ * getPossibleInstantsFor().
+ */
+ oneShiftTimeZone(shiftInstant, shiftNanoseconds) {
+ class OneShiftTimeZone extends Temporal.TimeZone {
+ constructor(shiftInstant, shiftNanoseconds) {
+ super("+00:00");
+ this._shiftInstant = shiftInstant;
+ this._epoch1 = shiftInstant.epochNanoseconds;
+ this._epoch2 = this._epoch1 + BigInt(shiftNanoseconds);
+ this._shiftNanoseconds = shiftNanoseconds;
+ this._shift = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, this._shiftNanoseconds);
+ this.getPossibleInstantsForCalledWith = [];
+ }
+
+ _isBeforeShift(instant) {
+ return instant.epochNanoseconds < this._epoch1;
+ }
+
+ getOffsetNanosecondsFor(instant) {
+ return this._isBeforeShift(instant) ? 0 : this._shiftNanoseconds;
+ }
+
+ getPossibleInstantsFor(plainDateTime) {
+ this.getPossibleInstantsForCalledWith.push(plainDateTime.toString({ calendarName: "never" }));
+ const [instant] = super.getPossibleInstantsFor(plainDateTime);
+ if (this._shiftNanoseconds > 0) {
+ if (this._isBeforeShift(instant)) return [instant];
+ if (instant.epochNanoseconds < this._epoch2) return [];
+ return [instant.subtract(this._shift)];
+ }
+ if (instant.epochNanoseconds < this._epoch2) return [instant];
+ const shifted = instant.subtract(this._shift);
+ if (this._isBeforeShift(instant)) return [instant, shifted];
+ return [shifted];
+ }
+
+ getNextTransition(instant) {
+ return this._isBeforeShift(instant) ? this._shiftInstant : null;
+ }
+
+ getPreviousTransition(instant) {
+ return this._isBeforeShift(instant) ? null : this._shiftInstant;
+ }
+
+ toString() {
+ return "Custom/One_Shift";
+ }
+ }
+ return new OneShiftTimeZone(shiftInstant, shiftNanoseconds);
+ },
+
+ /*
+ * propertyBagObserver():
+ * Returns an object that behaves like the given propertyBag but tracks Get
+ * and Has operations on any of its properties, by appending messages to an
+ * array. If the value of a property in propertyBag is a primitive, the value
+ * of the returned object's property will additionally be a
+ * TemporalHelpers.toPrimitiveObserver that will track calls to its toString
+ * and valueOf methods in the same array. This is for the purpose of testing
+ * order of operations that are observable from user code. objectName is used
+ * in the log.
+ */
+ propertyBagObserver(calls, propertyBag, objectName) {
+ return new Proxy(propertyBag, {
+ ownKeys(target) {
+ calls.push(`ownKeys ${objectName}`);
+ return Reflect.ownKeys(target);
+ },
+ getOwnPropertyDescriptor(target, key) {
+ calls.push(`getOwnPropertyDescriptor ${formatPropertyName(key, objectName)}`);
+ return Reflect.getOwnPropertyDescriptor(target, key);
+ },
+ get(target, key, receiver) {
+ calls.push(`get ${formatPropertyName(key, objectName)}`);
+ const result = Reflect.get(target, key, receiver);
+ if (result === undefined) {
+ return undefined;
+ }
+ if ((result !== null && typeof result === "object") || typeof result === "function") {
+ return result;
+ }
+ return TemporalHelpers.toPrimitiveObserver(calls, result, `${formatPropertyName(key, objectName)}`);
+ },
+ has(target, key) {
+ calls.push(`has ${formatPropertyName(key, objectName)}`);
+ return Reflect.has(target, key);
+ },
+ });
+ },
+
+ /*
+ * specificOffsetTimeZone():
+ *
+ * This returns an instance of a custom time zone class, which returns a
+ * specific custom value from its getOffsetNanosecondsFrom() method. This is
+ * for the purpose of testing the validation of what this method returns.
+ *
+ * It also returns an empty array from getPossibleInstantsFor(), so as to
+ * trigger calls to getOffsetNanosecondsFor() when used from the
+ * BuiltinTimeZoneGetInstantFor operation.
+ */
+ specificOffsetTimeZone(offsetValue) {
+ class SpecificOffsetTimeZone extends Temporal.TimeZone {
+ constructor(offsetValue) {
+ super("UTC");
+ this._offsetValue = offsetValue;
+ }
+
+ getOffsetNanosecondsFor() {
+ return this._offsetValue;
+ }
+
+ getPossibleInstantsFor(dt) {
+ if (typeof this._offsetValue !== 'number' || Math.abs(this._offsetValue) >= 86400e9 || isNaN(this._offsetValue)) return [];
+ const zdt = dt.toZonedDateTime("UTC").add({ nanoseconds: -this._offsetValue });
+ return [zdt.toInstant()];
+ }
+
+ get id() {
+ return this.getOffsetStringFor(new Temporal.Instant(0n));
+ }
+ }
+ return new SpecificOffsetTimeZone(offsetValue);
+ },
+
+ /*
+ * springForwardFallBackTimeZone():
+ *
+ * This returns an instance of a custom time zone class that implements one
+ * single spring-forward/fall-back transition, for the purpose of testing the
+ * disambiguation option, without depending on system time zone data.
+ *
+ * The spring-forward occurs at epoch second 954669600 (2000-04-02T02:00
+ * local) and goes from offset -08:00 to -07:00.
+ *
+ * The fall-back occurs at epoch second 972810000 (2000-10-29T02:00 local) and
+ * goes from offset -07:00 to -08:00.
+ */
+ springForwardFallBackTimeZone() {
+ const { compare } = Temporal.PlainDateTime;
+ const springForwardLocal = new Temporal.PlainDateTime(2000, 4, 2, 2);
+ const springForwardEpoch = 954669600_000_000_000n;
+ const fallBackLocal = new Temporal.PlainDateTime(2000, 10, 29, 1);
+ const fallBackEpoch = 972810000_000_000_000n;
+ const winterOffset = new Temporal.TimeZone('-08:00');
+ const summerOffset = new Temporal.TimeZone('-07:00');
+
+ class SpringForwardFallBackTimeZone extends Temporal.TimeZone {
+ constructor() {
+ super("-08:00");
+ }
+
+ getOffsetNanosecondsFor(instant) {
+ if (instant.epochNanoseconds < springForwardEpoch ||
+ instant.epochNanoseconds >= fallBackEpoch) {
+ return winterOffset.getOffsetNanosecondsFor(instant);
+ }
+ return summerOffset.getOffsetNanosecondsFor(instant);
+ }
+
+ getPossibleInstantsFor(datetime) {
+ if (compare(datetime, springForwardLocal) >= 0 && compare(datetime, springForwardLocal.add({ hours: 1 })) < 0) {
+ return [];
+ }
+ if (compare(datetime, fallBackLocal) >= 0 && compare(datetime, fallBackLocal.add({ hours: 1 })) < 0) {
+ return [summerOffset.getInstantFor(datetime), winterOffset.getInstantFor(datetime)];
+ }
+ if (compare(datetime, springForwardLocal) < 0 || compare(datetime, fallBackLocal) >= 0) {
+ return [winterOffset.getInstantFor(datetime)];
+ }
+ return [summerOffset.getInstantFor(datetime)];
+ }
+
+ getPreviousTransition(instant) {
+ if (instant.epochNanoseconds > fallBackEpoch) return new Temporal.Instant(fallBackEpoch);
+ if (instant.epochNanoseconds > springForwardEpoch) return new Temporal.Instant(springForwardEpoch);
+ return null;
+ }
+
+ getNextTransition(instant) {
+ if (instant.epochNanoseconds < springForwardEpoch) return new Temporal.Instant(springForwardEpoch);
+ if (instant.epochNanoseconds < fallBackEpoch) return new Temporal.Instant(fallBackEpoch);
+ return null;
+ }
+
+ get id() {
+ return "Custom/Spring_Fall";
+ }
+
+ toString() {
+ return "Custom/Spring_Fall";
+ }
+ }
+ return new SpringForwardFallBackTimeZone();
+ },
+
+ /*
+ * timeZoneObserver:
+ * A custom calendar that behaves exactly like the UTC time zone but tracks
+ * calls to any of its methods, and Get/Has operations on its properties, by
+ * appending messages to an array. This is for the purpose of testing order of
+ * operations that are observable from user code. objectName is used in the
+ * log. methodOverrides is an optional object containing properties with the
+ * same name as Temporal.TimeZone methods. If the property value is a function
+ * it will be called with the proper arguments instead of the UTC method.
+ * Otherwise, the property value will be returned directly.
+ */
+ timeZoneObserver(calls, objectName, methodOverrides = {}) {
+ const utc = new Temporal.TimeZone("UTC");
+ const trackingMethods = {
+ id: "UTC",
+ };
+ // Automatically generate the methods
+ ["getOffsetNanosecondsFor", "getPossibleInstantsFor", "toString"].forEach((methodName) => {
+ trackingMethods[methodName] = function (...args) {
+ calls.push(`call ${formatPropertyName(methodName, objectName)}`);
+ if (methodName in methodOverrides) {
+ const value = methodOverrides[methodName];
+ return typeof value === "function" ? value(...args) : value;
+ }
+ return utc[methodName](...args);
+ };
+ });
+ return new Proxy(trackingMethods, {
+ get(target, key, receiver) {
+ const result = Reflect.get(target, key, receiver);
+ calls.push(`get ${formatPropertyName(key, objectName)}`);
+ return result;
+ },
+ has(target, key) {
+ calls.push(`has ${formatPropertyName(key, objectName)}`);
+ return Reflect.has(target, key);
+ },
+ });
+ },
+
+ /*
+ * A custom time zone that does not allow any of its methods to be called, for
+ * the purpose of asserting that a particular operation does not call into
+ * user code.
+ */
+ timeZoneThrowEverything() {
+ class TimeZoneThrowEverything extends Temporal.TimeZone {
+ constructor() {
+ super("UTC");
+ }
+ getOffsetNanosecondsFor() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be called");
+ }
+ getPossibleInstantsFor() {
+ TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be called");
+ }
+ toString() {
+ TemporalHelpers.assertUnreachable("toString should not be called");
+ }
+ }
+
+ return new TimeZoneThrowEverything();
+ },
+
+ /*
+ * Returns an object that will append logs of any Gets or Calls of its valueOf
+ * or toString properties to the array calls. Both valueOf and toString will
+ * return the actual primitiveValue. propertyName is used in the log.
+ */
+ toPrimitiveObserver(calls, primitiveValue, propertyName) {
+ return {
+ get valueOf() {
+ calls.push(`get ${propertyName}.valueOf`);
+ return function () {
+ calls.push(`call ${propertyName}.valueOf`);
+ return primitiveValue;
+ };
+ },
+ get toString() {
+ calls.push(`get ${propertyName}.toString`);
+ return function () {
+ calls.push(`call ${propertyName}.toString`);
+ if (primitiveValue === undefined) return undefined;
+ return primitiveValue.toString();
+ };
+ },
+ };
+ },
+
+ /*
+ * An object containing further methods that return arrays of ISO strings, for
+ * testing parsers.
+ */
+ ISO: {
+ /*
+ * PlainMonthDay strings that are not valid.
+ */
+ plainMonthDayStringsInvalid() {
+ return [
+ "11-18junk",
+ "11-18[u-ca=gregory]",
+ "11-18[u-ca=hebrew]",
+ ];
+ },
+
+ /*
+ * PlainMonthDay strings that are valid and that should produce October 1st.
+ */
+ plainMonthDayStringsValid() {
+ return [
+ "10-01",
+ "1001",
+ "1965-10-01",
+ "1976-10-01T152330.1+00:00",
+ "19761001T15:23:30.1+00:00",
+ "1976-10-01T15:23:30.1+0000",
+ "1976-10-01T152330.1+0000",
+ "19761001T15:23:30.1+0000",
+ "19761001T152330.1+00:00",
+ "19761001T152330.1+0000",
+ "+001976-10-01T152330.1+00:00",
+ "+0019761001T15:23:30.1+00:00",
+ "+001976-10-01T15:23:30.1+0000",
+ "+001976-10-01T152330.1+0000",
+ "+0019761001T15:23:30.1+0000",
+ "+0019761001T152330.1+00:00",
+ "+0019761001T152330.1+0000",
+ "1976-10-01T15:23:00",
+ "1976-10-01T15:23",
+ "1976-10-01T15",
+ "1976-10-01",
+ "--10-01",
+ "--1001",
+ ];
+ },
+
+ /*
+ * PlainTime strings that may be mistaken for PlainMonthDay or
+ * PlainYearMonth strings, and so require a time designator.
+ */
+ plainTimeStringsAmbiguous() {
+ const ambiguousStrings = [
+ "2021-12", // ambiguity between YYYY-MM and HHMM-UU
+ "2021-12[-12:00]", // ditto, TZ does not disambiguate
+ "1214", // ambiguity between MMDD and HHMM
+ "0229", // ditto, including MMDD that doesn't occur every year
+ "1130", // ditto, including DD that doesn't occur in every month
+ "12-14", // ambiguity between MM-DD and HH-UU
+ "12-14[-14:00]", // ditto, TZ does not disambiguate
+ "202112", // ambiguity between YYYYMM and HHMMSS
+ "202112[UTC]", // ditto, TZ does not disambiguate
+ ];
+ // Adding a calendar annotation to one of these strings must not cause
+ // disambiguation in favour of time.
+ const stringsWithCalendar = ambiguousStrings.map((s) => s + '[u-ca=iso8601]');
+ return ambiguousStrings.concat(stringsWithCalendar);
+ },
+
+ /*
+ * PlainTime strings that are of similar form to PlainMonthDay and
+ * PlainYearMonth strings, but are not ambiguous due to components that
+ * aren't valid as months or days.
+ */
+ plainTimeStringsUnambiguous() {
+ return [
+ "2021-13", // 13 is not a month
+ "202113", // ditto
+ "2021-13[-13:00]", // ditto
+ "202113[-13:00]", // ditto
+ "0000-00", // 0 is not a month
+ "000000", // ditto
+ "0000-00[UTC]", // ditto
+ "000000[UTC]", // ditto
+ "1314", // 13 is not a month
+ "13-14", // ditto
+ "1232", // 32 is not a day
+ "0230", // 30 is not a day in February
+ "0631", // 31 is not a day in June
+ "0000", // 0 is neither a month nor a day
+ "00-00", // ditto
+ ];
+ },
+
+ /*
+ * PlainYearMonth-like strings that are not valid.
+ */
+ plainYearMonthStringsInvalid() {
+ return [
+ "2020-13",
+ ];
+ },
+
+ /*
+ * PlainYearMonth-like strings that are valid and should produce November
+ * 1976 in the ISO 8601 calendar.
+ */
+ plainYearMonthStringsValid() {
+ return [
+ "1976-11",
+ "1976-11-10",
+ "1976-11-01T09:00:00+00:00",
+ "1976-11-01T00:00:00+05:00",
+ "197611",
+ "+00197611",
+ "1976-11-18T15:23:30.1\u221202:00",
+ "1976-11-18T152330.1+00:00",
+ "19761118T15:23:30.1+00:00",
+ "1976-11-18T15:23:30.1+0000",
+ "1976-11-18T152330.1+0000",
+ "19761118T15:23:30.1+0000",
+ "19761118T152330.1+00:00",
+ "19761118T152330.1+0000",
+ "+001976-11-18T152330.1+00:00",
+ "+0019761118T15:23:30.1+00:00",
+ "+001976-11-18T15:23:30.1+0000",
+ "+001976-11-18T152330.1+0000",
+ "+0019761118T15:23:30.1+0000",
+ "+0019761118T152330.1+00:00",
+ "+0019761118T152330.1+0000",
+ "1976-11-18T15:23",
+ "1976-11-18T15",
+ "1976-11-18",
+ ];
+ },
+
+ /*
+ * PlainYearMonth-like strings that are valid and should produce November of
+ * the ISO year -9999.
+ */
+ plainYearMonthStringsValidNegativeYear() {
+ return [
+ "\u2212009999-11",
+ ];
+ },
+ }
+};
diff --git a/js/src/tests/test262/built-ins/Temporal/toStringTag/browser.js b/js/src/tests/test262/built-ins/Temporal/toStringTag/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/toStringTag/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/toStringTag/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/toStringTag/prop-desc.js
new file mode 100644
index 0000000000..b4db86321d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/toStringTag/prop-desc.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-@@tostringtag
+description: The @@toStringTag property of Temporal
+includes: [propertyHelper.js]
+features: [Symbol.toStringTag, Temporal]
+---*/
+
+verifyProperty(Temporal, Symbol.toStringTag, {
+ value: "Temporal",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/toStringTag/shell.js b/js/src/tests/test262/built-ins/Temporal/toStringTag/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/toStringTag/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/toStringTag/string.js b/js/src/tests/test262/built-ins/Temporal/toStringTag/string.js
new file mode 100644
index 0000000000..81a8039fcf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/toStringTag/string.js
@@ -0,0 +1,13 @@
+// |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-@@tostringtag
+description: The @@toStringTag property of Temporal produces the correct value in toString
+features: [Symbol.toStringTag, Temporal]
+---*/
+
+assert.sameValue(String(Temporal), "[object Temporal]");
+
+reportCompare(0, 0);